summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2012-05-21 09:42:27 +1000
committerNeilBrown <neilb@suse.de>2012-05-21 09:42:27 +1000
commit872ddb0608700a338c25a84a0e23c744714c8f0d (patch)
tree0565765c9c72c8703796bc5a31ab16c0c6550c10
parent5ef6ef98e9c4f0b6227a55ea3c909c7c61244b0a (diff)
downloadmdadm-872ddb0608700a338c25a84a0e23c744714c8f0d.tar.gz
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, is 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>
-rw-r--r--Grow.c89
1 files changed, 78 insertions, 11 deletions
diff --git a/Grow.c b/Grow.c
index b8d2444..16d592e 100644
--- a/Grow.c
+++ b/Grow.c
@@ -1155,8 +1155,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:
@@ -2154,20 +2160,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'
*/
@@ -2189,6 +2196,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;
@@ -2332,7 +2349,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;
@@ -2358,6 +2375,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,
@@ -2400,6 +2463,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;