diff --git a/Create.c b/Create.c index 91c641f..d6dcdbf 100644 --- a/Create.c +++ b/Create.c @@ -289,6 +289,7 @@ int Create(struct supertype *st, char *mddev, char *dname = dv->devname; unsigned long long freesize; int dfd; + char *doff; if (strcasecmp(dname, "missing")==0) { if (first_missing > dnum) @@ -298,6 +299,16 @@ int Create(struct supertype *st, char *mddev, missing_disks ++; continue; } + if (data_offset != VARIABLE_OFFSET) { + doff = strchr(dname, ':'); + if (doff) { + *doff++ = 0; + dv->data_offset = parse_size(doff); + } else + dv->data_offset = INVALID_SECTORS; + } else + dv->data_offset = data_offset; + dfd = open(dname, O_RDONLY); if (dfd < 0) { pr_err("cannot open %s: %s\n", @@ -335,7 +346,7 @@ int Create(struct supertype *st, char *mddev, switch (st->ss->validate_geometry( st, s->level, s->layout, s->raiddisks, &s->chunk, s->size*2, - data_offset, dname, + dv->data_offset, dname, &freesize, c->verbose > 0)) { case -1: /* Not valid, message printed, and not * worth checking any further */ @@ -372,7 +383,7 @@ int Create(struct supertype *st, char *mddev, if (!st->ss->validate_geometry(st, s->level, s->layout, s->raiddisks, &s->chunk, s->size*2, - data_offset, + dv->data_offset, dname, &freesize, c->verbose >= 0)) { @@ -866,7 +877,8 @@ int Create(struct supertype *st, char *mddev, if (fd >= 0) remove_partitions(fd); if (st->ss->add_to_super(st, &inf->disk, - fd, dv->devname)) { + fd, dv->devname, + dv->data_offset)) { ioctl(mdfd, STOP_ARRAY, NULL); goto abort; } diff --git a/Manage.c b/Manage.c index d2b92a2..296feeb 100644 --- a/Manage.c +++ b/Manage.c @@ -733,7 +733,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, disc.state |= 1 << MD_DISK_WRITEMOSTLY; dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (tst->ss->add_to_super(tst, &disc, dfd, - dv->devname)) + dv->devname, INVALID_SECTORS)) return -1; if (tst->ss->write_init_super(tst)) return -1; @@ -791,7 +791,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, if (mdmon_running(tst->container_dev)) tst->update_tail = &tst->updates; if (tst->ss->add_to_super(tst, &disc, dfd, - dv->devname)) { + dv->devname, INVALID_SECTORS)) { close(dfd); close(container_fd); return -1; diff --git a/managemon.c b/managemon.c index ef351b3..f0399b8 100644 --- a/managemon.c +++ b/managemon.c @@ -304,7 +304,7 @@ static void add_disk_to_container(struct supertype *st, struct mdinfo *sd) st2->ss->free_super(st2); st->update_tail = &update; - st->ss->add_to_super(st, &dk, dfd, NULL); + st->ss->add_to_super(st, &dk, dfd, NULL, INVALID_SECTORS); st->ss->write_init_super(st); queue_metadata_update(update); st->update_tail = NULL; diff --git a/mdadm.8.in b/mdadm.8.in index 255b650..a2d3d00 100644 --- a/mdadm.8.in +++ b/mdadm.8.in @@ -787,7 +787,7 @@ Since Linux 3.4, can also be used with .B --grow for some RAID levels (initially on RAID10). This allows the -data-offset to be changed as part of the reshape process. When the +data\-offset to be changed as part of the reshape process. When the data offset is changed, no backup file is required as the difference in offsets is used to provide the same functionality. @@ -795,6 +795,17 @@ When the new offset is earlier than the old offset, the number of devices in the array cannot shrink. When it is after the old offset, the number of devices in the array cannot increase. +When creating an array, +.B \-\-data\-offset +can be specified as +.BR variable . +In the case each member device is expected to have a offset appended +to the name, separated by a colon. This makes it possible to recreate +exactly an array which has varying data offsets (as can happen when +different versions of +.I mdadm +are used to add different devices). + .TP .BR \-\-continue This option is complementary to the diff --git a/mdadm.c b/mdadm.c index 5f39571..42544f1 100644 --- a/mdadm.c +++ b/mdadm.c @@ -465,7 +465,11 @@ int main(int argc, char *argv[]) "Second value is %s.\n", optarg); exit(2); } - data_offset = parse_size(optarg); + if (mode == CREATE && + strcmp(optarg, "variable") == 0) + data_offset = VARIABLE_OFFSET; + else + data_offset = parse_size(optarg); if (data_offset == INVALID_SECTORS) { fprintf(stderr, Name ": invalid data-offset: %s\n", optarg); diff --git a/mdadm.h b/mdadm.h index 457cc58..e142c50 100644 --- a/mdadm.h +++ b/mdadm.h @@ -433,6 +433,7 @@ struct mddev_dev { */ char writemostly; /* 1 for 'set writemostly', 2 for 'clear writemostly' */ char used; /* set when used */ + long long data_offset; struct mddev_dev *next; }; @@ -742,7 +743,8 @@ extern struct superswitch { * when hot-adding a spare. */ int (*add_to_super)(struct supertype *st, mdu_disk_info_t *dinfo, - int fd, char *devname); + int fd, char *devname, + unsigned long long data_offset); /* update the metadata to delete a device, * when hot-removing. */ @@ -1479,5 +1481,7 @@ char *xstrdup(const char *str); * a value for 'invalid'. Use '1'. */ #define INVALID_SECTORS 1 +/* And another special number needed for --data_offset=variable */ +#define VARIABLE_OFFSET 3 extern int __offroot; diff --git a/super-ddf.c b/super-ddf.c index baadc79..4a6f623 100644 --- a/super-ddf.c +++ b/super-ddf.c @@ -2174,7 +2174,8 @@ static void add_to_super_ddf_bvd(struct supertype *st, * expanding a pre-existing container */ static int add_to_super_ddf(struct supertype *st, - mdu_disk_info_t *dk, int fd, char *devname) + mdu_disk_info_t *dk, int fd, char *devname, + unsigned long long data_offset) { struct ddf_super *ddf = st->sb; struct dl *dd; diff --git a/super-intel.c b/super-intel.c index fc1d6ab..e311d0c 100644 --- a/super-intel.c +++ b/super-intel.c @@ -4937,7 +4937,8 @@ int mark_spare(struct dl *disk) } static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk, - int fd, char *devname) + int fd, char *devname, + unsigned long long data_offset) { struct intel_super *super = st->sb; struct dl *dd; diff --git a/super0.c b/super0.c index 4e6375f..ac56d05 100644 --- a/super0.c +++ b/super0.c @@ -695,7 +695,7 @@ struct devinfo { #ifndef MDASSEMBLE /* Add a device to the superblock being created */ static int add_to_super0(struct supertype *st, mdu_disk_info_t *dinfo, - int fd, char *devname) + int fd, char *devname, unsigned long long data_offset) { mdp_super_t *sb = st->sb; mdp_disk_t *dk = &sb->disks[dinfo->number]; diff --git a/super1.c b/super1.c index 8f5f307..d6b69fb 100644 --- a/super1.c +++ b/super1.c @@ -1106,13 +1106,14 @@ static int init_super1(struct supertype *st, mdu_array_info_t *info, struct devinfo { int fd; char *devname; + long long data_offset; mdu_disk_info_t disk; struct devinfo *next; }; #ifndef MDASSEMBLE /* Add a device to the superblock being created */ static int add_to_super1(struct supertype *st, mdu_disk_info_t *dk, - int fd, char *devname) + int fd, char *devname, unsigned long long data_offset) { struct mdp_superblock_1 *sb = st->sb; __u16 *rp = sb->dev_roles + dk->number; @@ -1140,6 +1141,7 @@ static int add_to_super1(struct supertype *st, mdu_disk_info_t *dk, di->fd = fd; di->devname = devname; di->disk = *dk; + di->data_offset = data_offset; di->next = NULL; *dip = di; @@ -1338,14 +1340,13 @@ static int write_init_super1(struct supertype *st) headroom/2 >= __le32_to_cpu(sb->chunksize) * 2) headroom >>= 1; - + data_offset = di->data_offset; switch(st->minor_version) { case 0: sb_offset = dsize; sb_offset -= 8*2; sb_offset &= ~(4*2-1); sb->super_offset = __cpu_to_le64(sb_offset); - data_offset = __le64_to_cpu(sb->data_offset); if (data_offset == INVALID_SECTORS) sb->data_offset = 0; if (sb_offset < array_size + bm_space) @@ -1358,7 +1359,6 @@ static int write_init_super1(struct supertype *st) break; case 1: sb->super_offset = __cpu_to_le64(0); - data_offset = __le64_to_cpu(sb->data_offset); if (data_offset == INVALID_SECTORS) { reserved = bm_space + 4*2; if (reserved < headroom) @@ -1386,7 +1386,6 @@ static int write_init_super1(struct supertype *st) case 2: sb_offset = 4*2; sb->super_offset = __cpu_to_le64(4*2); - data_offset = __le64_to_cpu(sb->data_offset); if (data_offset == INVALID_SECTORS) { if (4*2 + 4*2 + bm_space + array_size > dsize) diff --git a/util.c b/util.c index 4bd07e1..5e653f4 100644 --- a/util.c +++ b/util.c @@ -213,6 +213,9 @@ unsigned long long parse_size(char *size) c++; s *= 1024 * 1024 * 2; break; + case 's': /* sectors */ + c++; + break; } } else s = INVALID_SECTORS;