From ee2429e0bc33d52bc1e4831e91446e08e48dc85c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 4 Oct 2012 16:34:21 +1000 Subject: [PATCH] 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 --- Grow.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/Grow.c b/Grow.c index e8418d77..29a1c864 100644 --- a/Grow.c +++ b/Grow.c @@ -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<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; -- 2.39.2