Skip to content

Commit

Permalink
md: fix handling of array level takeover that re-arranges devices.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
NeilBrown committed Jun 24, 2010
1 parent 0544a21 commit e93f68a
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 35 deletions.
35 changes: 30 additions & 5 deletions drivers/md/md.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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));
Expand Down
3 changes: 3 additions & 0 deletions drivers/md/md.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 3 additions & 8 deletions drivers/md/raid0.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 - "
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 0 additions & 3 deletions drivers/md/raid0.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
19 changes: 5 additions & 14 deletions drivers/md/raid10.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand All @@ -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;
}

Expand Down
5 changes: 0 additions & 5 deletions drivers/md/raid10.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit e93f68a

Please sign in to comment.