summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2006-06-26 00:27:41 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 09:58:37 -0700
commit7c7546ccf6463edbeee8d9aac6de7be1cd80d08a (patch)
tree56c1f734db9f8a66a7c8c5ef11fb35e07dbb2f91
parent5fd6c1dce06ec24ef3de20fe0c7ecf2ba9fe5ef9 (diff)
[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>
-rw-r--r--drivers/md/linear.c74
-rw-r--r--drivers/md/md.c15
-rw-r--r--include/linux/raid/linear.h2
3 files changed, 76 insertions, 15 deletions
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 777585458c8..ff83c9b5979 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk,
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;
dev_info_t **table;
@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev)
sector_t curr_offset;
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);
if (!conf)
- goto out;
+ return NULL;
+
mddev->private = conf;
cnt = 0;
- mddev->array_size = 0;
+ conf->array_size = 0;
ITERATE_RDEV(mddev,rdev,tmp) {
int j = rdev->raid_disk;
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");
goto out;
}
@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev)
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->size = rdev->size;
- mddev->array_size += rdev->size;
+ conf->array_size += rdev->size;
cnt++;
}
- if (cnt != mddev->raid_disks) {
+ if (cnt != raid_disks) {
printk("linear: not enough drives present. Aborting!\n");
goto out;
}
@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev)
unsigned round;
unsigned long base;
- sz = mddev->array_size >> conf->preshift;
+ sz = conf->array_size >> conf->preshift;
sz += 1; /* force round-up */
base = conf->hash_spacing >> conf->preshift;
round = sector_div(sz, base);
@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev)
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);
mddev->queue->unplug_fn = linear_unplug;
mddev->queue->issue_flush_fn = linear_issue_flush;
return 0;
+}
-out:
- kfree(conf);
- return 1;
+static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+ /* 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)
@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev)
linear_conf_t *conf = mddev_to_conf(mddev);
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
- kfree(conf->hash_table);
- kfree(conf);
+ do {
+ linear_conf_t *t = conf->prev;
+ kfree(conf->hash_table);
+ kfree(conf);
+ conf = t;
+ } while (conf);
return 0;
}
@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality =
.run = linear_run,
.stop = linear_stop,
.status = linear_status,
+ .hot_add_disk = linear_add,
};
static int __init linear_init (void)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index a296edd7e1c..2ce750d4be0 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -817,8 +817,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
if (desc->state & (1<<MD_DISK_FAULTY))
set_bit(Faulty, &rdev->flags);
- else if (desc->state & (1<<MD_DISK_SYNC) &&
- desc->raid_disk < mddev->raid_disks) {
+ else if (desc->state & (1<<MD_DISK_SYNC) /* &&
+ desc->raid_disk < mddev->raid_disks */) {
set_bit(In_sync, &rdev->flags);
rdev->raid_disk = desc->raid_disk;
}
@@ -3359,6 +3359,17 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
rdev->raid_disk = -1;
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)
export_rdev(rdev);
diff --git a/include/linux/raid/linear.h b/include/linux/raid/linear.h
index 7eaf290e10e..ba15469daf1 100644
--- a/include/linux/raid/linear.h
+++ b/include/linux/raid/linear.h
@@ -13,8 +13,10 @@ typedef struct dev_info dev_info_t;
struct linear_private_data
{
+ struct linear_private_data *prev; /* earlier version */
dev_info_t **hash_table;
sector_t hash_spacing;
+ sector_t array_size;
int preshift; /* shift before dividing by hash_spacing */
dev_info_t disks[0];
};