[PATCH] md: allow a linear array to have drives added while active
authorNeilBrown <neilb@suse.de>
Mon, 26 Jun 2006 07:27:41 +0000 (00:27 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 26 Jun 2006 16:58:37 +0000 (09:58 -0700)
Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
drivers/md/linear.c
drivers/md/md.c
include/linux/raid/linear.h

index 777585458c8526a68ebaee3d40cb32974c07149a..ff83c9b5979e5feb0c433b4e3a118344e479a5cc 100644 (file)
@@ -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)
index a296edd7e1c34059d734752613fa05ba7aa6780f..2ce750d4be023922bcba40753c4897a64a736c90 100644 (file)
@@ -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);
 
index 7eaf290e10e75d344a78174e3602b38e3179c400..ba15469daf114e3eb3d75a3ba2cde1659fdfb02e 100644 (file)
@@ -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];
 };