Commit 7c7546cc authored by NeilBrown's avatar NeilBrown Committed by Linus Torvalds

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

Signed-off-by: default avatarNeil Brown <neilb@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5fd6c1dc
...@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk, ...@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret; return ret;
} }
static int linear_run (mddev_t *mddev) static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
{ {
linear_conf_t *conf; linear_conf_t *conf;
dev_info_t **table; dev_info_t **table;
...@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev) ...@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev)
sector_t curr_offset; sector_t curr_offset;
struct list_head *tmp; struct list_head *tmp;
conf = kzalloc (sizeof (*conf) + mddev->raid_disks*sizeof(dev_info_t), conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t),
GFP_KERNEL); GFP_KERNEL);
if (!conf) if (!conf)
goto out; return NULL;
mddev->private = conf; mddev->private = conf;
cnt = 0; cnt = 0;
mddev->array_size = 0; conf->array_size = 0;
ITERATE_RDEV(mddev,rdev,tmp) { ITERATE_RDEV(mddev,rdev,tmp) {
int j = rdev->raid_disk; int j = rdev->raid_disk;
dev_info_t *disk = conf->disks + j; dev_info_t *disk = conf->disks + j;
if (j < 0 || j > mddev->raid_disks || disk->rdev) { if (j < 0 || j > raid_disks || disk->rdev) {
printk("linear: disk numbering problem. Aborting!\n"); printk("linear: disk numbering problem. Aborting!\n");
goto out; goto out;
} }
...@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev) ...@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev)
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->size = rdev->size; disk->size = rdev->size;
mddev->array_size += rdev->size; conf->array_size += rdev->size;
cnt++; cnt++;
} }
if (cnt != mddev->raid_disks) { if (cnt != raid_disks) {
printk("linear: not enough drives present. Aborting!\n"); printk("linear: not enough drives present. Aborting!\n");
goto out; goto out;
} }
...@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev) ...@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev)
unsigned round; unsigned round;
unsigned long base; unsigned long base;
sz = mddev->array_size >> conf->preshift; sz = conf->array_size >> conf->preshift;
sz += 1; /* force round-up */ sz += 1; /* force round-up */
base = conf->hash_spacing >> conf->preshift; base = conf->hash_spacing >> conf->preshift;
round = sector_div(sz, base); round = sector_div(sz, base);
...@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev) ...@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev)
BUG_ON(table - conf->hash_table > nb_zone); BUG_ON(table - conf->hash_table > nb_zone);
return conf;
out:
kfree(conf);
return NULL;
}
static int linear_run (mddev_t *mddev)
{
linear_conf_t *conf;
conf = linear_conf(mddev, mddev->raid_disks);
if (!conf)
return 1;
mddev->private = conf;
mddev->array_size = conf->array_size;
blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
mddev->queue->unplug_fn = linear_unplug; mddev->queue->unplug_fn = linear_unplug;
mddev->queue->issue_flush_fn = linear_issue_flush; mddev->queue->issue_flush_fn = linear_issue_flush;
return 0; return 0;
}
out: static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
kfree(conf); {
return 1; /* Adding a drive to a linear array allows the array to grow.
* It is permitted if the new drive has a matching superblock
* already on it, with raid_disk equal to raid_disks.
* It is achieved by creating a new linear_private_data structure
* and swapping it in in-place of the current one.
* The current one is never freed until the array is stopped.
* This avoids races.
*/
linear_conf_t *newconf;
if (rdev->raid_disk != mddev->raid_disks)
return -EINVAL;
newconf = linear_conf(mddev,mddev->raid_disks+1);
if (!newconf)
return -ENOMEM;
newconf->prev = mddev_to_conf(mddev);
mddev->private = newconf;
mddev->raid_disks++;
mddev->array_size = newconf->array_size;
set_capacity(mddev->gendisk, mddev->array_size << 1);
return 0;
} }
static int linear_stop (mddev_t *mddev) static int linear_stop (mddev_t *mddev)
...@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev) ...@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev)
linear_conf_t *conf = mddev_to_conf(mddev); linear_conf_t *conf = mddev_to_conf(mddev);
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
kfree(conf->hash_table); do {
kfree(conf); linear_conf_t *t = conf->prev;
kfree(conf->hash_table);
kfree(conf);
conf = t;
} while (conf);
return 0; return 0;
} }
...@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality = ...@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality =
.run = linear_run, .run = linear_run,
.stop = linear_stop, .stop = linear_stop,
.status = linear_status, .status = linear_status,
.hot_add_disk = linear_add,
}; };
static int __init linear_init (void) static int __init linear_init (void)
......
...@@ -817,8 +817,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) ...@@ -817,8 +817,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
if (desc->state & (1<<MD_DISK_FAULTY)) if (desc->state & (1<<MD_DISK_FAULTY))
set_bit(Faulty, &rdev->flags); set_bit(Faulty, &rdev->flags);
else if (desc->state & (1<<MD_DISK_SYNC) && else if (desc->state & (1<<MD_DISK_SYNC) /* &&
desc->raid_disk < mddev->raid_disks) { desc->raid_disk < mddev->raid_disks */) {
set_bit(In_sync, &rdev->flags); set_bit(In_sync, &rdev->flags);
rdev->raid_disk = desc->raid_disk; rdev->raid_disk = desc->raid_disk;
} }
...@@ -3359,6 +3359,17 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) ...@@ -3359,6 +3359,17 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
rdev->raid_disk = -1; rdev->raid_disk = -1;
err = bind_rdev_to_array(rdev, mddev); err = bind_rdev_to_array(rdev, mddev);
if (!err && !mddev->pers->hot_remove_disk) {
/* If there is hot_add_disk but no hot_remove_disk
* then added disks for geometry changes,
* and should be added immediately.
*/
super_types[mddev->major_version].
validate_super(mddev, rdev);
err = mddev->pers->hot_add_disk(mddev, rdev);
if (err)
unbind_rdev_from_array(rdev);
}
if (err) if (err)
export_rdev(rdev); export_rdev(rdev);
......
...@@ -13,8 +13,10 @@ typedef struct dev_info dev_info_t; ...@@ -13,8 +13,10 @@ typedef struct dev_info dev_info_t;
struct linear_private_data struct linear_private_data
{ {
struct linear_private_data *prev; /* earlier version */
dev_info_t **hash_table; dev_info_t **hash_table;
sector_t hash_spacing; sector_t hash_spacing;
sector_t array_size;
int preshift; /* shift before dividing by hash_spacing */ int preshift; /* shift before dividing by hash_spacing */
dev_info_t disks[0]; dev_info_t disks[0];
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment