Grow: allow for different sized devices when updating data_offset.

It is possible that the devices in an array have different sizes, and
different data_offsets.  So the 'before_space' and 'after_space' may
be different from drive to drive.
Any decisions about how much to change the data_offset must work on
all devices, so must be based on the minimum available space on
any devices.

So find this minimum first, then do the calculation.

Signed-off-by: NeilBrown <neilb@suse.de>
This commit is contained in:
NeilBrown 2013-05-27 15:09:38 +10:00
parent 199f1a1fad
commit f9b08fecd8
1 changed files with 49 additions and 16 deletions

65
Grow.c
View File

@ -24,6 +24,7 @@
#include "mdadm.h" #include "mdadm.h"
#include "dlink.h" #include "dlink.h"
#include <sys/mman.h> #include <sys/mman.h>
#include <stdint.h>
#if ! defined(__BIG_ENDIAN) && ! defined(__LITTLE_ENDIAN) #if ! defined(__BIG_ENDIAN) && ! defined(__LITTLE_ENDIAN)
#error no endian defined #error no endian defined
@ -2152,7 +2153,13 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
struct mdinfo *sd; struct mdinfo *sd;
int dir = 0; int dir = 0;
int err = 0; int err = 0;
unsigned long long before, after;
/* Need to find min space before and after so same is used
* on all devices
*/
before = UINT64_MAX;
after = UINT64_MAX;
for (sd = sra->devs; sd; sd = sd->next) { for (sd = sra->devs; sd; sd = sd->next) {
char *dn; char *dn;
int dfd; int dfd;
@ -2186,6 +2193,38 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
/* Metadata doesn't support data_offset changes */ /* Metadata doesn't support data_offset changes */
return 1; return 1;
} }
if (before > info2.space_before)
before = info2.space_before;
if (after > info2.space_after)
after = info2.space_after;
if (data_offset != INVALID_SECTORS) {
if (dir == 0) {
if (info2.data_offset == data_offset) {
pr_err("%s: already has that data_offset\n",
dn);
goto release;
}
if (data_offset < info2.data_offset)
dir = -1;
else
dir = 1;
} else if ((data_offset <= info2.data_offset && dir == 1) ||
(data_offset >= info2.data_offset && dir == -1)) {
pr_err("%s: differing data offsets on devices make this --data-offset setting impossible\n",
dn);
goto release;
}
}
}
if (before == UINT64_MAX)
/* impossible really, there must be no devices */
return 1;
for (sd = sra->devs; sd; sd = sd->next) {
char *dn = map_dev(sd->disk.major, sd->disk.minor, 0);
struct mdinfo info2;
if (delta_disks < 0) { if (delta_disks < 0) {
/* Don't need any space as array is shrinking /* Don't need any space as array is shrinking
* just move data_offset up by min * just move data_offset up by min
@ -2202,7 +2241,7 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
} }
} else if (delta_disks > 0) { } else if (delta_disks > 0) {
/* need space before */ /* need space before */
if (info2.space_before < min) { if (before < min) {
pr_err("Insufficient head-space for reshape on %s\n", pr_err("Insufficient head-space for reshape on %s\n",
dn); dn);
goto release; goto release;
@ -2219,25 +2258,21 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
} }
} else { } else {
if (dir == 0) { if (dir == 0) {
/* can move up or down. 'data_offset' /* can move up or down. If 'data_offset'
* might guide us, otherwise choose * was set we would have already decided,
* direction with most space * so just choose direction with most space.
*/ */
if (data_offset == INVALID_SECTORS) { if (info2.space_before > info2.space_after)
if (info2.space_before > info2.space_after)
dir = -1;
else
dir = 1;
} else if (data_offset < info2.data_offset)
dir = -1; dir = -1;
else else
dir = 1; dir = 1;
sysfs_set_str(sra, NULL, "reshape_direction", sysfs_set_str(sra, NULL, "reshape_direction",
dir == 1 ? "backwards" : "forwards"); dir == 1 ? "backwards" : "forwards");
} }
switch (dir) { switch (dir) {
case 1: /* Increase data offset */ case 1: /* Increase data offset */
if (info2.space_after < min) { if (after < min) {
pr_err("Insufficient tail-space for reshape on %s\n", pr_err("Insufficient tail-space for reshape on %s\n",
dn); dn);
goto release; goto release;
@ -2251,8 +2286,7 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
if (data_offset != INVALID_SECTORS) if (data_offset != INVALID_SECTORS)
info2.new_data_offset = data_offset; info2.new_data_offset = data_offset;
else { else {
unsigned long long off = unsigned long long off = after / 2;
info2.space_after / 2;
off &= ~7ULL; off &= ~7ULL;
if (off < min) if (off < min)
off = min; off = min;
@ -2261,7 +2295,7 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
} }
break; break;
case -1: /* Decrease data offset */ case -1: /* Decrease data offset */
if (info2.space_before < min) { if (before < min) {
pr_err("insufficient head-room on %s\n", pr_err("insufficient head-room on %s\n",
dn); dn);
goto release; goto release;
@ -2275,8 +2309,7 @@ static int set_new_data_offset(struct mdinfo *sra, struct supertype *st,
if (data_offset != INVALID_SECTORS) if (data_offset != INVALID_SECTORS)
info2.new_data_offset = data_offset; info2.new_data_offset = data_offset;
else { else {
unsigned long long off = unsigned long long off = before / 2;
info2.space_before / 2;
off &= ~7ULL; off &= ~7ULL;
if (off < min) if (off < min)
off = min; off = min;