Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] md: allow a linear array to have drives added while active

Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

NeilBrown and committed by
Linus Torvalds
7c7546cc 5fd6c1dc

+76 -15
+61 -13
drivers/md/linear.c
··· 111 111 return ret; 112 112 } 113 113 114 - static int linear_run (mddev_t *mddev) 114 + static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) 115 115 { 116 116 linear_conf_t *conf; 117 117 dev_info_t **table; ··· 121 121 sector_t curr_offset; 122 122 struct list_head *tmp; 123 123 124 - conf = kzalloc (sizeof (*conf) + mddev->raid_disks*sizeof(dev_info_t), 124 + conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t), 125 125 GFP_KERNEL); 126 126 if (!conf) 127 - goto out; 127 + return NULL; 128 + 128 129 mddev->private = conf; 129 130 130 131 cnt = 0; 131 - mddev->array_size = 0; 132 + conf->array_size = 0; 132 133 133 134 ITERATE_RDEV(mddev,rdev,tmp) { 134 135 int j = rdev->raid_disk; 135 136 dev_info_t *disk = conf->disks + j; 136 137 137 - if (j < 0 || j > mddev->raid_disks || disk->rdev) { 138 + if (j < 0 || j > raid_disks || disk->rdev) { 138 139 printk("linear: disk numbering problem. Aborting!\n"); 139 140 goto out; 140 141 } ··· 153 152 blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); 154 153 155 154 disk->size = rdev->size; 156 - mddev->array_size += rdev->size; 155 + conf->array_size += rdev->size; 157 156 158 157 cnt++; 159 158 } 160 - if (cnt != mddev->raid_disks) { 159 + if (cnt != raid_disks) { 161 160 printk("linear: not enough drives present. Aborting!\n"); 162 161 goto out; 163 162 } ··· 201 200 unsigned round; 202 201 unsigned long base; 203 202 204 - sz = mddev->array_size >> conf->preshift; 203 + sz = conf->array_size >> conf->preshift; 205 204 sz += 1; /* force round-up */ 206 205 base = conf->hash_spacing >> conf->preshift; 207 206 round = sector_div(sz, base); ··· 248 247 249 248 BUG_ON(table - conf->hash_table > nb_zone); 250 249 250 + return conf; 251 + 252 + out: 253 + kfree(conf); 254 + return NULL; 255 + } 256 + 257 + static int linear_run (mddev_t *mddev) 258 + { 259 + linear_conf_t *conf; 260 + 261 + conf = linear_conf(mddev, mddev->raid_disks); 262 + 263 + if (!conf) 264 + return 1; 265 + mddev->private = conf; 266 + mddev->array_size = conf->array_size; 267 + 251 268 blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); 252 269 mddev->queue->unplug_fn = linear_unplug; 253 270 mddev->queue->issue_flush_fn = linear_issue_flush; 254 271 return 0; 272 + } 255 273 256 - out: 257 - kfree(conf); 258 - return 1; 274 + static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) 275 + { 276 + /* Adding a drive to a linear array allows the array to grow. 277 + * It is permitted if the new drive has a matching superblock 278 + * already on it, with raid_disk equal to raid_disks. 279 + * It is achieved by creating a new linear_private_data structure 280 + * and swapping it in in-place of the current one. 281 + * The current one is never freed until the array is stopped. 282 + * This avoids races. 283 + */ 284 + linear_conf_t *newconf; 285 + 286 + if (rdev->raid_disk != mddev->raid_disks) 287 + return -EINVAL; 288 + 289 + newconf = linear_conf(mddev,mddev->raid_disks+1); 290 + 291 + if (!newconf) 292 + return -ENOMEM; 293 + 294 + newconf->prev = mddev_to_conf(mddev); 295 + mddev->private = newconf; 296 + mddev->raid_disks++; 297 + mddev->array_size = newconf->array_size; 298 + set_capacity(mddev->gendisk, mddev->array_size << 1); 299 + return 0; 259 300 } 260 301 261 302 static int linear_stop (mddev_t *mddev) ··· 305 262 linear_conf_t *conf = mddev_to_conf(mddev); 306 263 307 264 blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ 308 - kfree(conf->hash_table); 309 - kfree(conf); 265 + do { 266 + linear_conf_t *t = conf->prev; 267 + kfree(conf->hash_table); 268 + kfree(conf); 269 + conf = t; 270 + } while (conf); 310 271 311 272 return 0; 312 273 } ··· 407 360 .run = linear_run, 408 361 .stop = linear_stop, 409 362 .status = linear_status, 363 + .hot_add_disk = linear_add, 410 364 }; 411 365 412 366 static int __init linear_init (void)
+13 -2
drivers/md/md.c
··· 817 817 818 818 if (desc->state & (1<<MD_DISK_FAULTY)) 819 819 set_bit(Faulty, &rdev->flags); 820 - else if (desc->state & (1<<MD_DISK_SYNC) && 821 - desc->raid_disk < mddev->raid_disks) { 820 + else if (desc->state & (1<<MD_DISK_SYNC) /* && 821 + desc->raid_disk < mddev->raid_disks */) { 822 822 set_bit(In_sync, &rdev->flags); 823 823 rdev->raid_disk = desc->raid_disk; 824 824 } ··· 3359 3359 3360 3360 rdev->raid_disk = -1; 3361 3361 err = bind_rdev_to_array(rdev, mddev); 3362 + if (!err && !mddev->pers->hot_remove_disk) { 3363 + /* If there is hot_add_disk but no hot_remove_disk 3364 + * then added disks for geometry changes, 3365 + * and should be added immediately. 3366 + */ 3367 + super_types[mddev->major_version]. 3368 + validate_super(mddev, rdev); 3369 + err = mddev->pers->hot_add_disk(mddev, rdev); 3370 + if (err) 3371 + unbind_rdev_from_array(rdev); 3372 + } 3362 3373 if (err) 3363 3374 export_rdev(rdev); 3364 3375
+2
include/linux/raid/linear.h
··· 13 13 14 14 struct linear_private_data 15 15 { 16 + struct linear_private_data *prev; /* earlier version */ 16 17 dev_info_t **hash_table; 17 18 sector_t hash_spacing; 19 + sector_t array_size; 18 20 int preshift; /* shift before dividing by hash_spacing */ 19 21 dev_info_t disks[0]; 20 22 };