md: fix handling of array level takeover that re-arranges devices.
authorNeilBrown <neilb@suse.de>
Tue, 15 Jun 2010 08:36:03 +0000 (09:36 +0100)
committerNeilBrown <neilb@suse.de>
Thu, 24 Jun 2010 03:33:24 +0000 (13:33 +1000)
Most array level changes leave the list of devices largely unchanged,
possibly causing one at the end to become redundant.
However conversions between RAID0 and RAID10 need to renumber
all devices (except 0).

This renumbering is currently being done in the ->run method when the
new personality takes over.  However this is too late as the common
code in md.c might already have invalidated some of the devices if
they had a ->raid_disk number that appeared to high.

Moving it into the ->takeover method is too early as the array is
still active at that time and wrong ->raid_disk numbers could cause
confusion.

So add a ->new_raid_disk field to mdk_rdev_s and use it to communicate
the new raid_disk number.
Now the common code knows exactly which devices need to be renumbered,
and which can be invalidated, and can do it all at a convenient time
when the array is suspend.
It can also update some symlinks in sysfs which previously were not be
updated correctly.

Reported-by: Maciej Trela <maciej.trela@intel.com>
Signed-off-by: NeilBrown <neilb@suse.de>
drivers/md/md.c
drivers/md/md.h
drivers/md/raid0.c
drivers/md/raid0.h
drivers/md/raid10.c
drivers/md/raid10.h

index 4edcda8f4869811060ec0839cb3c840d51fcab58..4869128bf742b15dfe3601e6b8851eb79168c0f2 100644 (file)
@@ -3001,6 +3001,9 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
                return -EINVAL;
        }
 
+       list_for_each_entry(rdev, &mddev->disks, same_set)
+               rdev->new_raid_disk = rdev->raid_disk;
+
        /* ->takeover must set new_* and/or delta_disks
         * if it succeeds, and may set them when it fails.
         */
@@ -3051,13 +3054,35 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
                mddev->safemode = 0;
        }
 
-       module_put(mddev->pers->owner);
-       /* Invalidate devices that are now superfluous */
-       list_for_each_entry(rdev, &mddev->disks, same_set)
-               if (rdev->raid_disk >= mddev->raid_disks) {
-                       rdev->raid_disk = -1;
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
+               char nm[20];
+               if (rdev->raid_disk < 0)
+                       continue;
+               if (rdev->new_raid_disk > mddev->raid_disks)
+                       rdev->new_raid_disk = -1;
+               if (rdev->new_raid_disk == rdev->raid_disk)
+                       continue;
+               sprintf(nm, "rd%d", rdev->raid_disk);
+               sysfs_remove_link(&mddev->kobj, nm);
+       }
+       list_for_each_entry(rdev, &mddev->disks, same_set) {
+               if (rdev->raid_disk < 0)
+                       continue;
+               if (rdev->new_raid_disk == rdev->raid_disk)
+                       continue;
+               rdev->raid_disk = rdev->new_raid_disk;
+               if (rdev->raid_disk < 0)
                        clear_bit(In_sync, &rdev->flags);
+               else {
+                       char nm[20];
+                       sprintf(nm, "rd%d", rdev->raid_disk);
+                       if(sysfs_create_link(&mddev->kobj, &rdev->kobj, nm))
+                               printk("md: cannot register %s for %s after level change\n",
+                                      nm, mdname(mddev));
                }
+       }
+
+       module_put(mddev->pers->owner);
        mddev->pers = pers;
        mddev->private = priv;
        strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
index 7ab5ea155452c5b745041e05c155df0ded2dd2c4..10597bfec0001c6ec3d732cab348ba459ee89a1a 100644 (file)
@@ -78,6 +78,9 @@ struct mdk_rdev_s
 
        int desc_nr;                    /* descriptor index in the superblock */
        int raid_disk;                  /* role of device in array */
+       int new_raid_disk;              /* role that the device will have in
+                                        * the array after a level-change completes.
+                                        */
        int saved_raid_disk;            /* role that device used to have in the
                                         * array and could again if we did a partial
                                         * resync from the bitmap
index e70f004c99e8b2bf146de202079f763022a048c2..7c7c38058bc25c013d28ff0d1910e3a2c71b0218 100644 (file)
@@ -173,9 +173,11 @@ static int create_strip_zones(mddev_t *mddev, raid0_conf_t **private_conf)
        list_for_each_entry(rdev1, &mddev->disks, same_set) {
                int j = rdev1->raid_disk;
 
-               if (mddev->level == 10)
+               if (mddev->level == 10) {
                        /* taking over a raid10-n2 array */
                        j /= 2;
+                       rdev1->new_raid_disk = j;
+               }
 
                if (j < 0 || j >= mddev->raid_disks) {
                        printk(KERN_ERR "md/raid0:%s: bad disk number %d - "
@@ -361,12 +363,6 @@ static int raid0_run(mddev_t *mddev)
                mddev->private = conf;
        }
        conf = mddev->private;
-       if (conf->scale_raid_disks) {
-               int i;
-               for (i=0; i < conf->strip_zone[0].nb_dev; i++)
-                       conf->devlist[i]->raid_disk /= conf->scale_raid_disks;
-               /* FIXME update sysfs rd links */
-       }
 
        /* calculate array device size */
        md_set_array_sectors(mddev, raid0_size(mddev, 0, 0));
@@ -643,7 +639,6 @@ static void *raid0_takeover_raid10(mddev_t *mddev)
        mddev->recovery_cp = MaxSector;
 
        create_strip_zones(mddev, &priv_conf);
-       priv_conf->scale_raid_disks = 2;
        return priv_conf;
 }
 
index d724e664ca4dace9fa27fee815d84d6b7e40d946..91f8e876ee64516949ddad76b374bc657fd1879c 100644 (file)
@@ -13,9 +13,6 @@ struct raid0_private_data
        struct strip_zone *strip_zone;
        mdk_rdev_t **devlist; /* lists of rdevs, pointed to by strip_zone->dev */
        int nr_strip_zones;
-       int scale_raid_disks; /* divide rdev->raid_disks by this in run()
-                              * to handle conversion from raid10
-                              */
 };
 
 typedef struct raid0_private_data raid0_conf_t;
index 6d420cb487b56963a372ae04ae7156fc3c42bd16..1bab3559f3e2ff9b3b4ef315db74b5dacd0ccf98 100644 (file)
@@ -2241,7 +2241,6 @@ static conf_t *setup_conf(mddev_t *mddev)
        if (!conf->thread)
                goto out;
 
-       conf->scale_disks = 0;
        conf->mddev = mddev;
        return conf;
 
@@ -2300,11 +2299,6 @@ static int run(mddev_t *mddev)
                if (disk_idx >= conf->raid_disks
                    || disk_idx < 0)
                        continue;
-               if (conf->scale_disks) {
-                       disk_idx *= conf->scale_disks;
-                       rdev->raid_disk = disk_idx;
-                       /* MOVE 'rd%d' link !! */
-               }
                disk = conf->mirrors + disk_idx;
 
                disk->rdev = rdev;
@@ -2435,13 +2429,6 @@ static void *raid10_takeover_raid0(mddev_t *mddev)
                return ERR_PTR(-EINVAL);
        }
 
-       /* Update slot numbers to obtain
-        * degraded raid10 with missing mirrors
-        */
-       list_for_each_entry(rdev, &mddev->disks, same_set) {
-               rdev->raid_disk *= 2;
-       }
-
        /* Set new parameters */
        mddev->new_level = 10;
        /* new layout: far_copies = 1, near_copies = 2 */
@@ -2454,7 +2441,11 @@ static void *raid10_takeover_raid0(mddev_t *mddev)
        mddev->recovery_cp = MaxSector;
 
        conf = setup_conf(mddev);
-       conf->scale_disks = 2;
+       if (!IS_ERR(conf))
+               list_for_each_entry(rdev, &mddev->disks, same_set)
+                       if (rdev->raid_disk >= 0)
+                               rdev->new_raid_disk = rdev->raid_disk * 2;
+               
        return conf;
 }
 
index 3824a087e17c65efe0ee327772f7fa699fb41f22..2316ac2e8e218c6a97c96ebd35d99172e0bcb4fc 100644 (file)
@@ -38,11 +38,6 @@ struct r10_private_data_s {
        int chunk_shift; /* shift from chunks to sectors */
        sector_t chunk_mask;
 
-       int                     scale_disks;  /* When starting array, multiply
-                                              * each ->raid_disk by this.
-                                              * Need for raid0->raid10 migration
-                                              */
-
        struct list_head        retry_list;
        /* queue pending writes and submit them on unplug */
        struct bio_list         pending_bio_list;