1 From b522adcde9c4d3fb7b579cfa9160d8bde7744be8 Mon Sep 17 00:00:00 2001
2 From: Dan Williams <dan.j.williams@intel.com>
3 Date: Tue, 31 Mar 2009 15:00:31 +1100
4 Subject: [PATCH] md: 'array_size' sysfs attribute
6 Allow userspace to set the size of the array according to the following
9 1/ size must be <= to the size returned by mddev->pers->size(mddev, 0, 0)
10 a) If size is set before the array is running, do_md_run will fail
11 if size is greater than the default size
12 b) A reshape attempt that reduces the default size to less than the set
13 array size should be blocked
14 2/ once userspace sets the size the kernel will not change it
15 3/ writing 'default' to this attribute returns control of the size to the
16 kernel and reverts to the size reported by the personality
18 Also, convert locations that need to know the default size from directly
19 reading ->array_sectors to <pers>_size. Resync/reshape operations
20 always follow the default size.
22 Finally, fixup other locations that read a number of 1k-blocks from
23 userspace to use strict_blocks_to_sectors() which checks for unsigned
24 long long to sector_t overflow and blocks to sectors overflow.
26 Reviewed-by: Andre Noll <maan@systemlinux.org>
27 Signed-off-by: Dan Williams <dan.j.williams@intel.com>
28 Acked-by: NeilBrown <neilb@suse.de>
30 drivers/md/md.c | 101 +++++++++++++++++++++++++++++++++++++++++++++-
31 drivers/md/raid0.c | 2
32 drivers/md/raid1.c | 6 +-
33 drivers/md/raid10.c | 2
34 drivers/md/raid5.c | 9 +++-
35 include/linux/raid/md_k.h | 3 +
36 6 files changed, 116 insertions(+), 7 deletions(-)
38 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/md/md.c
39 +++ linux-2.6.27-SLE11_BRANCH/drivers/md/md.c
40 @@ -300,6 +300,11 @@ static inline int mddev_lock(mddev_t * m
41 return mutex_lock_interruptible(&mddev->reconfig_mutex);
44 +static inline int mddev_is_locked(mddev_t *mddev)
46 + return mutex_is_locked(&mddev->reconfig_mutex);
49 static inline int mddev_trylock(mddev_t * mddev)
51 return mutex_trylock(&mddev->reconfig_mutex);
52 @@ -2146,6 +2151,25 @@ static int overlaps(sector_t s1, sector_
56 +static int strict_blocks_to_sectors(const char *buf, sector_t *sectors)
58 + unsigned long long blocks;
61 + if (strict_strtoull(buf, 10, &blocks) < 0)
64 + if (blocks & 1ULL << (8 * sizeof(blocks) - 1))
65 + return -EINVAL; /* sector conversion overflow */
68 + if (new != blocks * 2)
69 + return -EINVAL; /* unsigned long long to sector_t overflow */
76 rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
78 @@ -3398,6 +3422,57 @@ static struct md_sysfs_entry md_reshape_
79 __ATTR(reshape_position, S_IRUGO|S_IWUSR, reshape_position_show,
80 reshape_position_store);
83 +array_size_show(mddev_t *mddev, char *page)
85 + if (mddev->external_size)
86 + return sprintf(page, "%llu\n",
87 + (unsigned long long)mddev->array_sectors/2);
89 + return sprintf(page, "default\n");
93 +array_size_store(mddev_t *mddev, const char *buf, size_t len)
97 + if (strncmp(buf, "default", 7) == 0) {
99 + sectors = mddev->pers->size(mddev, 0, 0);
101 + sectors = mddev->array_sectors;
103 + mddev->external_size = 0;
105 + if (strict_blocks_to_sectors(buf, §ors) < 0)
107 + if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors)
110 + mddev->external_size = 1;
113 + mddev->array_sectors = sectors;
114 + set_capacity(mddev->gendisk, mddev->array_sectors);
116 + struct block_device *bdev = bdget_disk(mddev->gendisk, 0);
119 + mutex_lock(&bdev->bd_inode->i_mutex);
120 + i_size_write(bdev->bd_inode,
121 + (loff_t)mddev->array_sectors << 9);
122 + mutex_unlock(&bdev->bd_inode->i_mutex);
130 +static struct md_sysfs_entry md_array_size =
131 +__ATTR(array_size, S_IRUGO|S_IWUSR, array_size_show,
134 static struct attribute *md_default_attrs[] = {
136 @@ -3411,6 +3486,7 @@ static struct attribute *md_default_attr
138 &md_array_state.attr,
139 &md_reshape_position.attr,
140 + &md_array_size.attr,
144 @@ -3722,7 +3798,17 @@ static int do_md_run(mddev_t * mddev)
145 err = mddev->pers->run(mddev);
147 printk(KERN_ERR "md: pers->run() failed ...\n");
148 - else if (mddev->pers->sync_request) {
149 + else if (mddev->pers->size(mddev, 0, 0) < mddev->array_sectors) {
150 + WARN_ONCE(!mddev->external_size, "%s: default size too small,"
151 + " but 'external_size' not in effect?\n", __func__);
153 + "md: invalid array_size %llu > default size %llu\n",
154 + (unsigned long long)mddev->array_sectors / 2,
155 + (unsigned long long)mddev->pers->size(mddev, 0, 0) / 2);
157 + mddev->pers->stop(mddev);
159 + if (err == 0 && mddev->pers->sync_request) {
160 err = bitmap_create(mddev);
162 printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
163 @@ -4664,10 +4750,23 @@ static int set_array_info(mddev_t * mdde
165 void md_set_array_sectors(mddev_t *mddev, sector_t array_sectors)
167 + WARN(!mddev_is_locked(mddev), "%s: unlocked mddev!\n", __func__);
169 + if (mddev->external_size)
172 mddev->array_sectors = array_sectors;
174 EXPORT_SYMBOL(md_set_array_sectors);
176 +void md_set_array_sectors_lock(mddev_t *mddev, sector_t array_sectors)
179 + md_set_array_sectors(mddev, array_sectors);
180 + mddev_unlock(mddev);
182 +EXPORT_SYMBOL(md_set_array_sectors_lock);
184 static int update_size(mddev_t *mddev, sector_t num_sectors)
187 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/md/raid0.c
188 +++ linux-2.6.27-SLE11_BRANCH/drivers/md/raid0.c
189 @@ -314,7 +314,7 @@ static int raid0_run (mddev_t *mddev)
190 printk("raid0 : conf->hash_spacing is %llu blocks.\n",
191 (unsigned long long)conf->hash_spacing);
193 - sector_t s = mddev->array_sectors / 2;
194 + sector_t s = raid0_size(mddev, 0, 0) / 2;
195 sector_t space = conf->hash_spacing;
198 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/md/raid1.c
199 +++ linux-2.6.27-SLE11_BRANCH/drivers/md/raid1.c
200 @@ -2116,14 +2116,16 @@ static int raid1_resize(mddev_t *mddev,
203 md_set_array_sectors(mddev, raid1_size(mddev, sectors, 0));
204 + if (mddev->array_sectors > raid1_size(mddev, sectors, 0))
206 set_capacity(mddev->gendisk, mddev->array_sectors);
208 - if (mddev->array_sectors / 2 > mddev->size &&
209 + if (sectors / 2 > mddev->size &&
210 mddev->recovery_cp == MaxSector) {
211 mddev->recovery_cp = mddev->size << 1;
212 set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
214 - mddev->size = mddev->array_sectors / 2;
215 + mddev->size = sectors / 2;
216 mddev->resync_max_sectors = sectors;
219 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/md/raid10.c
220 +++ linux-2.6.27-SLE11_BRANCH/drivers/md/raid10.c
221 @@ -2187,7 +2187,7 @@ static int run(mddev_t *mddev)
222 * Ok, everything is just fine now
224 md_set_array_sectors(mddev, raid10_size(mddev, 0, 0));
225 - mddev->resync_max_sectors = mddev->array_sectors;
226 + mddev->resync_max_sectors = raid10_size(mddev, 0, 0);
228 mddev->queue->unplug_fn = raid10_unplug;
229 mddev->queue->backing_dev_info.congested_fn = raid10_congested;
230 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/md/raid5.c
231 +++ linux-2.6.27-SLE11_BRANCH/drivers/md/raid5.c
232 @@ -3700,6 +3700,8 @@ static int make_request(struct request_q
236 +static sector_t raid5_size(mddev_t *mddev, sector_t sectors, int raid_disks);
238 static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped)
240 /* reshaping is quite different to recovery/resync so it is
241 @@ -3778,7 +3780,7 @@ static sector_t reshape_request(mddev_t
244 s = compute_blocknr(sh, j);
245 - if (s < mddev->array_sectors) {
246 + if (s < raid5_size(mddev, 0, 0)) {
250 @@ -4660,6 +4662,9 @@ static int raid5_resize(mddev_t *mddev,
251 sectors &= ~((sector_t)mddev->chunk_size/512 - 1);
252 md_set_array_sectors(mddev, raid5_size(mddev, sectors,
254 + if (mddev->array_sectors >
255 + raid5_size(mddev, sectors, mddev->raid_disks))
257 set_capacity(mddev->gendisk, mddev->array_sectors);
259 if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) {
260 @@ -4798,7 +4803,7 @@ static void end_reshape(raid5_conf_t *co
261 if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) {
262 mddev_t *mddev = conf->mddev;
264 - md_set_array_sectors(mddev, raid5_size(mddev, 0,
265 + md_set_array_sectors_lock(mddev, raid5_size(mddev, 0,
267 set_capacity(mddev->gendisk, mddev->array_sectors);
269 --- linux-2.6.27-SLE11_BRANCH.orig/include/linux/raid/md_k.h
270 +++ linux-2.6.27-SLE11_BRANCH/include/linux/raid/md_k.h
271 @@ -152,6 +152,8 @@ struct mddev_s
273 sector_t size; /* used size of component devices */
274 sector_t array_sectors; /* exported array size */
275 + int external_size; /* size managed
280 @@ -394,3 +396,4 @@ static inline void safe_put_page(struct
281 #endif /* CONFIG_BLOCK */
284 +extern void md_set_array_sectors_lock(mddev_t *mddev, sector_t array_sectors);