X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=Assemble.c;h=080993dfc47d7010ca480fb278ad8cb4b1dd9b9f;hb=ae2416e7b6937b9414321c5239e2ad415f7c1988;hp=f66a21b96526d5b646cc3e58dc55fc12b53fea6b;hpb=02e7c5b75cd4ad5176441add156389c71dab6e3a;p=thirdparty%2Fmdadm.git diff --git a/Assemble.c b/Assemble.c index f66a21b9..080993df 100644 --- a/Assemble.c +++ b/Assemble.c @@ -70,12 +70,75 @@ static int is_member_busy(char *metadata_version) return busy; } +static int ident_matches(struct mddev_ident *ident, + struct mdinfo *content, + struct supertype *tst, + char *homehost, + char *update, char *devname) +{ + + if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) && + same_uuid(content->uuid, ident->uuid, tst->ss->swapuuid)==0 && + memcmp(content->uuid, uuid_zero, sizeof(int[4])) != 0) { + if (devname) + fprintf(stderr, Name ": %s has wrong uuid.\n", + devname); + return 0; + } + if (ident->name[0] && (!update || strcmp(update, "name")!= 0) && + name_matches(content->name, ident->name, homehost)==0) { + if (devname) + fprintf(stderr, Name ": %s has wrong name.\n", + devname); + return 0; + } + if (ident->super_minor != UnSet && + ident->super_minor != content->array.md_minor) { + if (devname) + fprintf(stderr, Name ": %s has wrong super-minor.\n", + devname); + return 0; + } + if (ident->level != UnSet && + ident->level != content->array.level) { + if (devname) + fprintf(stderr, Name ": %s has wrong raid level.\n", + devname); + return 0; + } + if (ident->raid_disks != UnSet && + ident->raid_disks!= content->array.raid_disks) { + if (devname) + fprintf(stderr, Name ": %s requires wrong number of drives.\n", + devname); + return 0; + } + if (ident->member && ident->member[0]) { + /* content->text_version must match */ + char *s = strchr(content->text_version+1, '/'); + if (s == NULL) { + if (devname) + fprintf(stderr, Name ": %s is not a container and one is required.\n", + devname); + return 0; + } else if (strcmp(ident->member, s+1) != 0) { + if (devname) + fprintf(stderr, Name ": skipping wrong member %s is %s\n", + content->text_version, devname); + return 0; + } + } + return 1; +} + + int Assemble(struct supertype *st, char *mddev, - mddev_ident_t ident, - mddev_dev_t devlist, char *backup_file, + struct mddev_ident *ident, + struct mddev_dev *devlist, + char *backup_file, int invalid_backup, int readonly, int runstop, char *update, char *homehost, int require_homehost, - int verbose, int force) + int verbose, int force, int freeze_reshape) { /* * The task of Assemble is to find a collection of @@ -157,11 +220,13 @@ int Assemble(struct supertype *st, char *mddev, int change = 0; int inargv = 0; int report_missmatch; +#ifndef MDASSEMBLE int bitmap_done; +#endif int start_partial_ok = (runstop >= 0) && (force || devlist==NULL || auto_assem); unsigned int num_devs; - mddev_dev_t tmpdev; + struct mddev_dev *tmpdev; struct mdinfo info; struct mdinfo *content = NULL; char *avail; @@ -169,6 +234,7 @@ int Assemble(struct supertype *st, char *mddev, char *name = NULL; int trustworthy; char chosen_name[1024]; + struct domainlist *domains = NULL; if (get_linux_version() < 2004000) old_linux = 1; @@ -225,12 +291,13 @@ int Assemble(struct supertype *st, char *mddev, */ for (tmpdev = devlist; tmpdev; - tmpdev = tmpdev->next) { + tmpdev = tmpdev ? tmpdev->next : NULL) { char *devname = tmpdev->devname; int dfd; struct stat stb; - struct supertype *tst = dup_super(st); + struct supertype *tst; struct dev_policy *pol = NULL; + int found_container = 0; if (tmpdev->used > 1) continue; @@ -241,7 +308,9 @@ int Assemble(struct supertype *st, char *mddev, continue; } - dfd = dev_open(devname, O_RDONLY|O_EXCL); + tst = dup_super(st); + + dfd = dev_open(devname, O_RDONLY); if (dfd < 0) { if (report_missmatch) fprintf(stderr, Name ": cannot open device %s: %s\n", @@ -256,51 +325,100 @@ int Assemble(struct supertype *st, char *mddev, fprintf(stderr, Name ": %s is not a block device.\n", devname); tmpdev->used = 2; - } else if (!tst && (tst = guess_super(dfd)) == NULL) { - if (report_missmatch) - fprintf(stderr, Name ": no recogniseable superblock on %s\n", - devname); - tmpdev->used = 2; - } else if (tst->ss->load_super(tst,dfd, NULL)) { - if (report_missmatch) - fprintf(stderr, Name ": no RAID superblock on %s\n", - devname); - } else if (tst->ss->compare_super == NULL) { - if (report_missmatch) - fprintf(stderr, Name ": Cannot assemble %s metadata on %s\n", - tst->ss->name, devname); - tst->ss->free_super(tst); - tmpdev->used = 2; - } else if (auto_assem && st == NULL && - !conf_test_metadata(tst->ss->name, (pol = devnum_policy(stb.st_rdev)), - tst->ss->match_home(tst, homehost) == 1)) { - if (report_missmatch) - fprintf(stderr, Name ": %s has metadata type %s for which " - "auto-assembly is disabled\n", - devname, tst->ss->name); - tst->ss->free_super(tst); - tmpdev->used = 2; + } else if (must_be_container(dfd)) { + if (st) { + /* already found some components, this cannot + * be another one. + */ + if (report_missmatch) + fprintf(stderr, Name ": %s is a container, but we are looking for components\n", + devname); + tmpdev->used = 2; +#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) + } if (!tst && (tst = super_by_fd(dfd, NULL)) == NULL) { + if (report_missmatch) + fprintf(stderr, Name ": not a recognisable container: %s\n", + devname); + tmpdev->used = 2; +#endif + } else if (!tst->ss->load_container + || tst->ss->load_container(tst, dfd, NULL)) { + if (report_missmatch) + fprintf(stderr, Name ": no correct container type: %s\n", + devname); + tmpdev->used = 2; + } else if (auto_assem && + !conf_test_metadata(tst->ss->name, (pol = devnum_policy(stb.st_rdev)), + tst->ss->match_home(tst, homehost) == 1)) { + if (report_missmatch) + fprintf(stderr, Name ": %s has metadata type %s for which " + "auto-assembly is disabled\n", + devname, tst->ss->name); + tmpdev->used = 2; + } else + found_container = 1; } else { - content = &info; - memset(content, 0, sizeof(*content)); - tst->ss->getinfo_super(tst, content, NULL); + if (!tst && (tst = guess_super(dfd)) == NULL) { + if (report_missmatch) + fprintf(stderr, Name ": no recogniseable superblock on %s\n", + devname); + tmpdev->used = 2; + } else if (tst->ss->load_super(tst,dfd, NULL)) { + if (report_missmatch) + fprintf(stderr, Name ": no RAID superblock on %s\n", + devname); + tmpdev->used = 2; + } else if (tst->ss->compare_super == NULL) { + if (report_missmatch) + fprintf(stderr, Name ": Cannot assemble %s metadata on %s\n", + tst->ss->name, devname); + tmpdev->used = 2; + } else if (auto_assem && st == NULL && + !conf_test_metadata(tst->ss->name, (pol = devnum_policy(stb.st_rdev)), + tst->ss->match_home(tst, homehost) == 1)) { + if (report_missmatch) + fprintf(stderr, Name ": %s has metadata type %s for which " + "auto-assembly is disabled\n", + devname, tst->ss->name); + tmpdev->used = 2; + } } if (dfd >= 0) close(dfd); + if (tmpdev->used == 2) { + if (auto_assem || !inargv) + /* Ignore unrecognised devices during auto-assembly */ + goto loop; + if (ident->uuid_set || ident->name[0] || + ident->super_minor != UnSet) + /* Ignore unrecognised device if looking for + * specific array */ + goto loop; + + + fprintf(stderr, Name ": %s has no superblock - assembly aborted\n", + devname); + if (st) + st->ss->free_super(st); + dev_policy_free(pol); + domain_free(domains); + return 1; + } - if (tst && tst->sb && tst->ss->container_content - && tst->loaded_container) { + if (found_container) { /* tmpdev is a container. We need to be either * looking for a member, or auto-assembling */ - if (st) { - /* already found some components, this cannot - * be another one. - */ + /* should be safe to try an exclusive open now, we + * have rejected anything that some other mdadm might + * be looking at + */ + dfd = dev_open(devname, O_RDONLY | O_EXCL); + if (dfd < 0) { if (report_missmatch) - fprintf(stderr, Name ": %s is a container, but we are looking for components\n", - devname); + fprintf(stderr, Name ": %s is busy - skipping\n", devname); goto loop; } + close(dfd); if (ident->container) { if (ident->container[0] == '/' && @@ -313,6 +431,10 @@ int Assemble(struct supertype *st, char *mddev, if (ident->container[0] != '/') { /* we have a uuid */ int uuid[4]; + + content = &info; + tst->ss->getinfo_super(tst, content, NULL); + if (!parse_uuid(ident->container, uuid) || !same_uuid(content->uuid, uuid, tst->ss->swapuuid)) { if (report_missmatch) @@ -327,123 +449,33 @@ int Assemble(struct supertype *st, char *mddev, if (verbose > 0) fprintf(stderr, Name ": looking in container %s\n", devname); - next_member: - if (tmpdev->content) - content = tmpdev->content; - else - content = tst->ss->container_content(tst); - if (!content) - goto loop; /* empty container */ - - tmpdev->content = content->next; - if (tmpdev->content == NULL) - tmpdev->used = 2; - - } else if (ident->container || ident->member) { - /* No chance of this matching if we don't have - * a container */ - if (report_missmatch) - fprintf(stderr, Name "%s is not a container, and one is required.\n", - devname); - goto loop; - } - - if (ident->uuid_set && (!update || strcmp(update, "uuid")!= 0) && - (!tst || !tst->sb || - same_uuid(content->uuid, ident->uuid, tst->ss->swapuuid)==0)) { - if (report_missmatch) - fprintf(stderr, Name ": %s has wrong uuid.\n", - devname); - goto loop; - } - if (ident->name[0] && (!update || strcmp(update, "name")!= 0) && - (!tst || !tst->sb || - name_matches(content->name, ident->name, homehost)==0)) { - if (report_missmatch) - fprintf(stderr, Name ": %s has wrong name.\n", - devname); - goto loop; - } - if (ident->super_minor != UnSet && - (!tst || !tst->sb || - ident->super_minor != content->array.md_minor)) { - if (report_missmatch) - fprintf(stderr, Name ": %s has wrong super-minor.\n", - devname); - goto loop; - } - if (ident->level != UnSet && - (!tst || !tst->sb || - ident->level != content->array.level)) { - if (report_missmatch) - fprintf(stderr, Name ": %s has wrong raid level.\n", - devname); - goto loop; - } - if (ident->raid_disks != UnSet && - (!tst || !tst->sb || - ident->raid_disks!= content->array.raid_disks)) { - if (report_missmatch) - fprintf(stderr, Name ": %s requires wrong number of drives.\n", - devname); - goto loop; - } - if (auto_assem) { - if (tst == NULL || tst->sb == NULL) - continue; - } - /* If we are this far, then we are nearly commited to this device. - * If the super_block doesn't exist, or doesn't match others, - * then we probably cannot continue - * However if one of the arrays is for the homehost, and - * the other isn't that can disambiguate. - */ - if (!tst || !tst->sb) { - fprintf(stderr, Name ": %s has no superblock - assembly aborted\n", - devname); - if (st) - st->ss->free_super(st); - dev_policy_free(pol); - return 1; - } + for (content = tst->ss->container_content(tst, NULL); + content; + content = content->next) { - if (tst && tst->sb && tst->ss->container_content - && tst->loaded_container) { - /* we have the one container we need, don't keep - * looking. If the chosen member is active, skip. - */ - if (is_member_busy(content->text_version)) { - if (report_missmatch) - fprintf(stderr, Name ": member %s in %s is already assembled\n", + if (!ident_matches(ident, content, tst, + homehost, update, + report_missmatch ? devname : NULL)) + /* message already printed */; + else if (is_member_busy(content->text_version)) { + if (report_missmatch) + fprintf(stderr, Name ": member %s in %s is already assembled\n", + content->text_version, + devname); + } else if (content->array.state & (1<text_version, devname); - skip: - if (tmpdev->content) - goto next_member; - tst->ss->free_super(tst); - tst = NULL; - content = NULL; - if (auto_assem) - goto loop; - dev_policy_free(pol); - return 1; + } else + break; } - if (ident->member && ident->member[0]) { - char *s = strchr(content->text_version+1, '/'); - if (s == NULL) { - fprintf(stderr, Name ": badly formatted version: %s\n", - content->text_version); - goto skip; - } - if (strcmp(ident->member, s+1) != 0) { - if (report_missmatch) - fprintf(stderr, - Name ": skipping wrong member %s\n", - content->text_version); - goto skip; - } + if (!content) { + tmpdev->used = 2; + goto loop; /* empty container */ } + st = tst; tst = NULL; if (!auto_assem && inargv && tmpdev->next != NULL) { fprintf(stderr, Name ": %s is a container, but is not " @@ -451,70 +483,161 @@ int Assemble(struct supertype *st, char *mddev, devname); st->ss->free_super(st); dev_policy_free(pol); + domain_free(domains); return 1; } if (verbose > 0) fprintf(stderr, Name ": found match on member %s in %s\n", content->text_version, devname); - break; - } - if (st == NULL) - st = dup_super(tst); - if (st->minor_version == -1) - st->minor_version = tst->minor_version; - if (st->ss != tst->ss || - st->minor_version != tst->minor_version || - st->ss->compare_super(st, tst) != 0) { - /* Some mismatch. If exactly one array matches this host, - * we can resolve on that one. - * Or, if we are auto assembling, we just ignore the second - * for now. + + /* make sure we finished the loop */ + tmpdev = NULL; + goto loop; + } else { + + content = &info; + tst->ss->getinfo_super(tst, content, NULL); + + if (!ident_matches(ident, content, tst, + homehost, update, + report_missmatch ? devname : NULL)) + goto loop; + + /* should be safe to try an exclusive open now, we + * have rejected anything that some other mdadm might + * be looking at */ - if (auto_assem) + dfd = dev_open(devname, O_RDONLY | O_EXCL); + if (dfd < 0) { + if (report_missmatch) + fprintf(stderr, Name ": %s is busy - skipping\n", devname); goto loop; - if (homehost) { - int first = st->ss->match_home(st, homehost); - int last = tst->ss->match_home(tst, homehost); - if (first != last && - (first == 1 || last == 1)) { - /* We can do something */ - if (first) {/* just ignore this one */ - if (report_missmatch) - fprintf(stderr, Name ": %s misses out due to wrong homehost\n", - devname); - goto loop; - } else { /* reject all those sofar */ - mddev_dev_t td; - if (report_missmatch) - fprintf(stderr, Name ": %s overrides previous devices due to good homehost\n", - devname); - for (td=devlist; td != tmpdev; td=td->next) - if (td->used == 1) - td->used = 0; - tmpdev->used = 1; - goto loop; + } + close(dfd); + + if (st == NULL) + st = dup_super(tst); + if (st->minor_version == -1) + st->minor_version = tst->minor_version; + + if (memcmp(content->uuid, uuid_zero, + sizeof(int[4])) == 0) { + /* this is a floating spare. It cannot define + * an array unless there are no more arrays of + * this type to be found. It can be included + * in an array of this type though. + */ + tmpdev->used = 3; + goto loop; + } + + if (st->ss != tst->ss || + st->minor_version != tst->minor_version || + st->ss->compare_super(st, tst) != 0) { + /* Some mismatch. If exactly one array matches this host, + * we can resolve on that one. + * Or, if we are auto assembling, we just ignore the second + * for now. + */ + if (auto_assem) + goto loop; + if (homehost) { + int first = st->ss->match_home(st, homehost); + int last = tst->ss->match_home(tst, homehost); + if (first != last && + (first == 1 || last == 1)) { + /* We can do something */ + if (first) {/* just ignore this one */ + if (report_missmatch) + fprintf(stderr, Name ": %s misses out due to wrong homehost\n", + devname); + goto loop; + } else { /* reject all those sofar */ + struct mddev_dev *td; + if (report_missmatch) + fprintf(stderr, Name ": %s overrides previous devices due to good homehost\n", + devname); + for (td=devlist; td != tmpdev; td=td->next) + if (td->used == 1) + td->used = 0; + tmpdev->used = 1; + goto loop; + } } } + fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n", + devname); + tst->ss->free_super(tst); + st->ss->free_super(st); + dev_policy_free(pol); + domain_free(domains); + return 1; } - fprintf(stderr, Name ": superblock on %s doesn't match others - assembly aborted\n", - devname); - tst->ss->free_super(tst); - st->ss->free_super(st); - dev_policy_free(pol); - return 1; + tmpdev->used = 1; } - - tmpdev->used = 1; - loop: + /* Collect domain information from members only */ + if (tmpdev && tmpdev->used == 1) { + if (!pol) + pol = devnum_policy(stb.st_rdev); + domain_merge(&domains, pol, tst?tst->ss->name:NULL); + } dev_policy_free(pol); pol = NULL; - if (tmpdev->content) - goto next_member; if (tst) tst->ss->free_super(tst); } + /* Check if we found some imsm spares but no members */ + if ((auto_assem || + (ident->uuid_set && + memcmp(uuid_zero, ident->uuid,sizeof(uuid_zero)) == 0)) && + (!st || !st->sb)) + for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) { + if (tmpdev->used != 3) + continue; + tmpdev->used = 1; + content = &info; + + if (!st->sb) { + /* we need sb from one of the spares */ + int dfd = dev_open(tmpdev->devname, O_RDONLY); + if (dfd < 0 || + st->ss->load_super(st, dfd, NULL)) + tmpdev->used = 2; + if (dfd > 0) + close(dfd); + } + } + + /* Now reject spares that don't match domains of identified members */ + for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) { + struct stat stb; + if (tmpdev->used != 3) + continue; + if (stat(tmpdev->devname, &stb)< 0) { + fprintf(stderr, Name ": fstat failed for %s: %s\n", + tmpdev->devname, strerror(errno)); + tmpdev->used = 2; + } else { + struct dev_policy *pol = devnum_policy(stb.st_rdev); + int dt = domain_test(domains, pol, NULL); + if (inargv && dt != 0) + /* take this spare as domains match + * if there are any */ + tmpdev->used = 1; + else if (!inargv && dt == 1) + /* device wasn't explicitly listed, so need + * explicit domain match - which we have */ + tmpdev->used = 1; + else + /* if domains don't match mark as unused */ + tmpdev->used = 0; + dev_policy_free(pol); + } + } + domain_free(domains); + if (!st || !st->sb || !content) return 2; @@ -596,13 +719,16 @@ int Assemble(struct supertype *st, char *mddev, #ifndef MDASSEMBLE if (content != &info) { /* This is a member of a container. Try starting the array. */ - return assemble_container_content(st, mdfd, content, runstop, - chosen_name, verbose); + int err; + err = assemble_container_content(st, mdfd, content, runstop, + chosen_name, verbose, + backup_file, freeze_reshape); + close(mdfd); + return err; } + bitmap_done = 0; #endif /* Ok, no bad inconsistancy, we can try updating etc */ - bitmap_done = 0; - content->update_private = NULL; devices = malloc(num_devs * sizeof(*devices)); devmap = calloc(num_devs * content->array.raid_disks, 1); for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) { @@ -632,8 +758,6 @@ int Assemble(struct supertype *st, char *mddev, } dfd = dev_open(devname, O_RDWR|O_EXCL); - remove_partitions(dfd); - tst = dup_super(st); if (dfd < 0 || tst->ss->load_super(tst, dfd, NULL) != 0) { fprintf(stderr, Name ": cannot re-read metadata from %s - aborting\n", @@ -698,8 +822,6 @@ int Assemble(struct supertype *st, char *mddev, int dfd; dfd = dev_open(devname, O_RDWR|O_EXCL); - remove_partitions(dfd); - if (dfd < 0 || tst->ss->load_super(tst, dfd, NULL) != 0) { fprintf(stderr, Name ": cannot re-read metadata from %s - aborting\n", devname); @@ -791,8 +913,6 @@ int Assemble(struct supertype *st, char *mddev, } devcnt++; } - free(content->update_private); - content->update_private = NULL; if (devcnt == 0) { fprintf(stderr, Name ": no devices found for %s\n", @@ -831,11 +951,13 @@ int Assemble(struct supertype *st, char *mddev, if (content->array.level != LEVEL_MULTIPATH) if (!(devices[j].i.disk.state & (1<array.raid_disks) { - if (devices[j].i.recovery_start == MaxSector) { + if (devices[j].i.recovery_start == MaxSector || + (content->reshape_active && + ((i >= content->array.raid_disks - content->delta_disks) || + (i >= content->array.raid_disks - content->delta_disks - 1 + && content->array.level == 4)))) { okcnt++; avail[i]=1; } else @@ -862,9 +988,17 @@ int Assemble(struct supertype *st, char *mddev, } } free(devmap); - while (force && !enough(content->array.level, content->array.raid_disks, - content->array.layout, 1, - avail, okcnt)) { + while (force && + (!enough(content->array.level, content->array.raid_disks, + content->array.layout, 1, + avail) + || + (content->reshape_active && content->delta_disks > 0 && + !enough(content->array.level, (content->array.raid_disks + - content->delta_disks), + content->new_layout, 1, + avail) + ))) { /* Choose the newest best drive which is * not up-to-date, update the superblock * and add it. @@ -958,6 +1092,8 @@ int Assemble(struct supertype *st, char *mddev, continue; if (!devices[j].uptodate) continue; + if (devices[j].i.events < devices[most_recent].i.events) + continue; chosen_drive = j; if ((fd=dev_open(devices[j].devname, O_RDONLY|O_EXCL))< 0) { fprintf(stderr, Name ": Cannot open %s: %s\n", @@ -1029,7 +1165,7 @@ int Assemble(struct supertype *st, char *mddev, if (force && !clean && !enough(content->array.level, content->array.raid_disks, content->array.layout, clean, - avail, okcnt)) { + avail)) { change += st->ss->update_super(st, content, "force-array", devices[chosen_drive].devname, verbose, 0, NULL); @@ -1054,6 +1190,9 @@ int Assemble(struct supertype *st, char *mddev, free(devices); return 1; } + if (verbose >= 0) + fprintf(stderr, Name ": Marking array %s as 'clean'\n", + mddev); close(fd); } @@ -1082,8 +1221,19 @@ int Assemble(struct supertype *st, char *mddev, } else fdlist[i] = -1; } - if (!err) - err = Grow_restart(st, content, fdlist, bestcnt, backup_file, verbose > 0); + if (!err) { + if (st->ss->external && st->ss->recover_backup) + err = st->ss->recover_backup(st, content); + else + err = Grow_restart(st, content, fdlist, bestcnt, + backup_file, verbose > 0); + if (err && invalid_backup) { + if (verbose > 0) + fprintf(stderr, Name ": continuing" + " without restoring backup\n"); + err = 0; + } + } while (i>0) { i--; if (fdlist[i]>=0) close(fdlist[i]); @@ -1163,6 +1313,12 @@ int Assemble(struct supertype *st, char *mddev, j = chosen_drive; if (j >= 0 /* && devices[j].uptodate */) { + int dfd = dev_open(devices[j].devname, + O_RDWR|O_EXCL); + if (dfd >= 0) { + remove_partitions(dfd); + close(dfd); + } rv = add_disk(mdfd, st, content, &devices[j].i); if (rv) { @@ -1178,9 +1334,11 @@ int Assemble(struct supertype *st, char *mddev, sparecnt--; } else if (verbose > 0) fprintf(stderr, Name ": added %s " - "to %s as %d\n", + "to %s as %d%s\n", devices[j].devname, mddev, - devices[j].i.disk.raid_disk); + devices[j].i.disk.raid_disk, + devices[j].uptodate?"": + " (possibly out of date)"); } else if (verbose > 0 && i < content->array.raid_disks) fprintf(stderr, Name ": no uptodate device for " "slot %d of %s\n", @@ -1197,6 +1355,7 @@ int Assemble(struct supertype *st, char *mddev, content->array.raid_disks); fprintf(stderr, "\n"); } + st->ss->free_super(st); sysfs_uevent(content, "change"); wait_for(chosen_name, mdfd); close(mdfd); @@ -1207,7 +1366,7 @@ int Assemble(struct supertype *st, char *mddev, if (runstop == 1 || (runstop <= 0 && ( enough(content->array.level, content->array.raid_disks, - content->array.layout, clean, avail, okcnt) && + content->array.layout, clean, avail) && (okcnt + rebuilding_cnt >= req_cnt || start_partial_ok) ))) { /* This array is good-to-go. @@ -1218,9 +1377,14 @@ int Assemble(struct supertype *st, char *mddev, int rv; #ifndef MDASSEMBLE if (content->reshape_active && - content->delta_disks <= 0) - rv = Grow_continue(mdfd, st, content, backup_file); - else + content->delta_disks <= 0) { + rv = sysfs_set_str(content, NULL, + "array_state", "readonly"); + if (rv == 0) + rv = Grow_continue(mdfd, st, content, + backup_file, + freeze_reshape); + } else #endif rv = ioctl(mdfd, RUN_ARRAY, NULL); if (rv == 0) { @@ -1247,6 +1411,7 @@ int Assemble(struct supertype *st, char *mddev, sysfs_set_num(sra, NULL, "stripe_cache_size", (4 * content->array.chunk_size / 4096) + 1); + sysfs_free(sra); } } if (okcnt < (unsigned)content->array.raid_disks) { @@ -1258,7 +1423,7 @@ int Assemble(struct supertype *st, char *mddev, * might allow them to be included, or * they will become spares. */ - for (i = 0; i <= bestcnt; i++) { + for (i = 0; i < bestcnt; i++) { int j = best[i]; if (j >= 0 && !devices[j].uptodate) { if (!disk_action_allows(&devices[j].i, st->ss->name, act_re_add)) @@ -1307,13 +1472,13 @@ int Assemble(struct supertype *st, char *mddev, mddev, strerror(errno)); if (!enough(content->array.level, content->array.raid_disks, - content->array.layout, 1, avail, okcnt)) + content->array.layout, 1, avail)) fprintf(stderr, Name ": Not enough devices to " "start the array.\n"); else if (!enough(content->array.level, content->array.raid_disks, content->array.layout, clean, - avail, okcnt)) + avail)) fprintf(stderr, Name ": Not enough devices to " "start the array while not clean " "- consider --force.\n"); @@ -1341,12 +1506,12 @@ int Assemble(struct supertype *st, char *mddev, if (sparecnt) fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); if (!enough(content->array.level, content->array.raid_disks, - content->array.layout, 1, avail, okcnt)) + content->array.layout, 1, avail)) fprintf(stderr, " - not enough to start the array.\n"); else if (!enough(content->array.level, content->array.raid_disks, content->array.layout, clean, - avail, okcnt)) + avail)) fprintf(stderr, " - not enough to start the " "array while not clean - consider " "--force.\n"); @@ -1385,42 +1550,87 @@ int Assemble(struct supertype *st, char *mddev, #ifndef MDASSEMBLE int assemble_container_content(struct supertype *st, int mdfd, struct mdinfo *content, int runstop, - char *chosen_name, int verbose) + char *chosen_name, int verbose, + char *backup_file, int freeze_reshape) { struct mdinfo *dev, *sra; int working = 0, preexist = 0; + int expansion = 0; struct map_ent *map = NULL; + int old_raid_disks; + int start_reshape; sysfs_init(content, mdfd, 0); sra = sysfs_read(mdfd, 0, GET_VERSION); if (sra == NULL || strcmp(sra->text_version, content->text_version) != 0) if (sysfs_set_array(content, md_get_version(mdfd)) != 0) { - close(mdfd); + if (sra) + sysfs_free(sra); return 1; } + + /* There are two types of reshape: container wide or sub-array specific + * Check if metadata requests blocking container wide reshapes + */ + start_reshape = (content->reshape_active && + !((content->reshape_active == CONTAINER_RESHAPE) && + (content->array.state & (1<ss->external && content->recovery_blocked && start_reshape) + block_subarray(content); + if (sra) sysfs_free(sra); - + old_raid_disks = content->array.raid_disks - content->delta_disks; for (dev = content->devs; dev; dev = dev->next) - if (sysfs_add_disk(content, dev, 1) == 0) - working++; - else if (errno == EEXIST) + if (sysfs_add_disk(content, dev, 1) == 0) { + if (dev->disk.raid_disk >= old_raid_disks && + content->reshape_active) + expansion++; + else + working++; + } else if (errno == EEXIST) preexist++; - if (working == 0) { - close(mdfd); + if (working + expansion == 0) return 1;/* Nothing new, don't try to start */ - } - + map_update(&map, fd2devnum(mdfd), content->text_version, content->uuid, chosen_name); if (runstop > 0 || - (working + preexist) >= content->array.working_disks) { + (working + preexist + expansion) >= + content->array.working_disks) { int err; - switch(content->array.level) { + if (start_reshape) { + int spare = content->array.raid_disks + expansion; + if (restore_backup(st, content, + working, + spare, backup_file, verbose) == 1) + return 1; + + err = sysfs_set_str(content, NULL, + "array_state", "readonly"); + if (err) + return 1; + + if (st->ss->external) { + if (!mdmon_running(st->container_dev)) + start_mdmon(st->container_dev); + ping_monitor_by_id(st->container_dev); + if (mdmon_running(st->container_dev) && + st->update_tail == NULL) + st->update_tail = &st->updates; + } + + err = Grow_continue(mdfd, st, content, backup_file, + freeze_reshape); + } else switch(content->array.level) { case LEVEL_LINEAR: case LEVEL_MULTIPATH: case 0: @@ -1434,37 +1644,53 @@ int assemble_container_content(struct supertype *st, int mdfd, if (!err) { if (!mdmon_running(st->container_dev)) start_mdmon(st->container_dev); - ping_monitor(devnum2devname(st->container_dev)); + ping_monitor_by_id(st->container_dev); } break; } if (!err) sysfs_set_safemode(content, content->safe_mode_delay); + + /* Block subarray here if it is not reshaped now + * It has be blocked a little later to allow mdmon to switch in + * in to R/W state + */ + if (st->ss->external && content->recovery_blocked && + !start_reshape) + block_subarray(content); + if (verbose >= 0) { if (err) fprintf(stderr, Name - ": array %s now has %d devices", - chosen_name, working + preexist); + ": array %s now has %d device%s", + chosen_name, working + preexist, + working + preexist == 1 ? "":"s"); else fprintf(stderr, Name - ": Started %s with %d devices", - chosen_name, working + preexist); + ": Started %s with %d device%s", + chosen_name, working + preexist, + working + preexist == 1 ? "":"s"); if (preexist) fprintf(stderr, " (%d new)", working); + if (expansion) + fprintf(stderr, " ( + %d for expansion)", + expansion); fprintf(stderr, "\n"); } if (!err) wait_for(chosen_name, mdfd); - close(mdfd); - return 0; + return err; /* FIXME should have an O_EXCL and wait for read-auto */ } else { - if (verbose >= 0) + if (verbose >= 0) { fprintf(stderr, Name - ": %s assembled with %d devices but " - "not started\n", - chosen_name, working); - close(mdfd); + ": %s assembled with %d device%s", + chosen_name, preexist + working, + preexist + working == 1 ? "":"s"); + if (preexist) + fprintf(stderr, " (%d new)", working); + fprintf(stderr, " but not started\n"); + } return 1; } }