Grow/raid10: support reducing the devices in a RAID10.

When reducing the number of devices in a RAID10, we increase the
data offset to avoid the need for backup area.

If there is no room at the end of the device to allow this, we need
to first reduce the component size of each device.  However if there
is room, we don't want to insist on that, otherwise growing then
shrinking the array would not be idempotent.

So find the min before/after space before analysing a RAID10 for
reshape, and if the after space is insufficient, reduce the total size
of the array and the component size accordingly.

Signed-off-by: NeilBrown <neilb@suse.de>
This commit is contained in:
NeilBrown 2012-10-04 16:34:21 +10:00
parent cb19a251a5
commit ee2429e0bc
1 changed files with 78 additions and 11 deletions

89
Grow.c
View File

@ -1151,8 +1151,14 @@ char *analyse_change(struct mdinfo *info, struct reshape *re)
*/
re->backup_blocks = max(old_chunk, new_chunk) / 512;
re->new_size = (info->component_size * new_disks
/ copies);
if (new_disks < re->before.data_disks &&
info->space_after < re->backup_blocks)
/* Reduce component size by one chunk */
re->new_size = (info->component_size -
re->backup_blocks);
else
re->new_size = info->component_size;
re->new_size = re->new_size * new_disks / copies;
return NULL;
default:
@ -2152,20 +2158,21 @@ static int raid10_reshape(char *container, int fd, char *devname,
{
/* Changing raid_disks, layout, chunksize or possibly
* just data_offset for a RAID10.
* We must always change data_offset.
* The amount is change it relates to the minimum copy size.
* This is reshape->backup_blocks * copies / raid_disks
* where 'raid_disks' is the smaller of 'new' and 'old'.
* We must always change data_offset. We change by at least
* ->backup_blocks which is the largest of the old and new
* chunk sizes.
* If raid_disks is increasing, then data_offset must decrease
* by at least this copy size.
* If raid_disks is unchanged, data_offset must increase or
* decrease by at least min-copy-size but preferably by much more.
* decrease by at least backup_blocks but preferably by much more.
* We choose half of the available space.
* If raid_disks is decreasing, data_offset must increase by
* at least min-copy-size.
* at least backup_blocks. To allow of this, component_size
* must be decreased by the same amount.
*
* So we calculate the required minimum and direction, then iterate
* through the devices and set the new_data_offset.
* So we calculate the required minimum and direction, possibly
* reduce the component_size, then iterate through the devices
* and set the new_data_offset.
* If that all works, we set chunk_size, layout, raid_disks, and start
* 'reshape'
*/
@ -2187,6 +2194,16 @@ static int raid10_reshape(char *container, int fd, char *devname,
if (info->delta_disks)
sysfs_set_str(sra, NULL, "reshape_direction",
info->delta_disks < 0 ? "backwards" : "forwards");
if (info->delta_disks < 0 &&
info->space_after < reshape->backup_blocks) {
int rv = sysfs_set_num(sra, NULL, "component_size",
(sra->component_size -
reshape->backup_blocks)/2);
if (rv) {
fprintf(stderr, Name ": cannot reduce component size\n");
goto release;
}
}
for (sd = sra->devs; sd; sd = sd->next) {
char *dn;
int dfd;
@ -2327,7 +2344,7 @@ static int raid10_reshape(char *container, int fd, char *devname,
break;
}
}
if (sysfs_set_num(sra, NULL, "chunk_size", info->new_chunk) < 0)
if (!err && sysfs_set_num(sra, NULL, "chunk_size", info->new_chunk) < 0)
err = errno;
if (!err && sysfs_set_num(sra, NULL, "layout", reshape->after.layout) < 0)
err = errno;
@ -2353,6 +2370,52 @@ release:
return 1;
}
static void get_space_after(int fd, struct supertype *st, struct mdinfo *info)
{
struct mdinfo *sra, *sd;
unsigned long long min_space_before, min_space_after;
int first = 1;
sra = sysfs_read(fd, 0, GET_DEVS);
if (!sra)
return;
for (sd = sra->devs; sd; sd = sd->next) {
char *dn;
int dfd;
struct supertype *st2;
struct mdinfo info2;
if (sd->disk.state & (1<<MD_DISK_FAULTY))
continue;
dn = map_dev(sd->disk.major, sd->disk.minor, 0);
dfd = dev_open(dn, O_RDONLY);
if (dfd < 0)
break;
st2 = dup_super(st);
if (st2->ss->load_super(st2,dfd, NULL)) {
close(dfd);
free(st2);
break;
}
close(dfd);
st2->ss->getinfo_super(st2, &info2, NULL);
st2->ss->free_super(st2);
free(st2);
if (first ||
min_space_before > info2.space_before)
min_space_before = info2.space_before;
if (first ||
min_space_after > info2.space_after)
min_space_after = info2.space_after;
first = 0;
}
if (sd == NULL && !first) {
info->space_after = min_space_after;
info->space_before = min_space_before;
}
sysfs_free(sra);
}
static int reshape_array(char *container, int fd, char *devname,
struct supertype *st, struct mdinfo *info,
int force, struct mddev_dev *devlist,
@ -2396,6 +2459,10 @@ static int reshape_array(char *container, int fd, char *devname,
info->component_size = array_size / array.raid_disks;
}
if (array.level == 10)
/* Need space_after info */
get_space_after(fd, st, info);
if (info->reshape_active) {
int new_level = info->new_level;
info->new_level = UnSet;