X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=Assemble.c;h=a152d656d8049d96c5497b6ee5176b2507cbbbc5;hb=976915080efc2e829fff8efaffb3dc76dc294975;hp=4933520d04f2b502128869a843b48d1ca6b6f39f;hpb=8a659c33212f85cc0dcecc759b7b36b0bb2b4fe4;p=thirdparty%2Fmdadm.git diff --git a/Assemble.c b/Assemble.c index 4933520d..a152d656 100644 --- a/Assemble.c +++ b/Assemble.c @@ -1,7 +1,7 @@ /* * mdadm - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001-2006 Neil Brown + * Copyright (C) 2001-2009 Neil Brown * * * This program is free software; you can redistribute it and/or modify @@ -19,12 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Neil Brown - * Email: - * Paper: Neil Brown - * School of Computer Science and Engineering - * The University of New South Wales - * Sydney, 2052 - * Australia + * Email: */ #include "mdadm.h" @@ -75,11 +70,72 @@ 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) { + 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 readonly, int runstop, - char *update, char *homehost, + char *update, char *homehost, int require_homehost, int verbose, int force) { /* @@ -140,7 +196,7 @@ int Assemble(struct supertype *st, char *mddev, int clean; int auto_assem = (mddev == NULL && !ident->uuid_set && ident->super_minor == UnSet && ident->name[0] == 0 - && ident->container == NULL && ident->member == NULL); + && (ident->container == NULL || ident->member == NULL)); int old_linux = 0; int vers = vers; /* Keep gcc quite - it really is initialised */ struct { @@ -150,12 +206,13 @@ int Assemble(struct supertype *st, char *mddev, */ struct mdinfo i; } *devices; + char *devmap; int *best = NULL; /* indexed by raid_disk */ - unsigned int bestcnt = 0; + int bestcnt = 0; int devcnt = 0; - unsigned int okcnt, sparecnt; + unsigned int okcnt, sparecnt, rebuilding_cnt; unsigned int req_cnt; - unsigned int i; + int i; int most_recent = 0; int chosen_drive; int change = 0; @@ -165,10 +222,9 @@ int Assemble(struct supertype *st, char *mddev, 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; - mdu_array_info_t tmp_inf; char *avail; int nextspare = 0; char *name = NULL; @@ -188,7 +244,9 @@ int Assemble(struct supertype *st, char *mddev, if (!devlist && ident->uuid_set == 0 && - ident->super_minor < 0 && + (ident->super_minor < 0 || ident->super_minor == UnSet) && + ident->name[0] == 0 && + (ident->container == NULL || ident->member == NULL) && ident->devices == NULL) { fprintf(stderr, Name ": No identity information available for %s - cannot assemble.\n", mddev ? mddev : "further assembly"); @@ -215,7 +273,6 @@ int Assemble(struct supertype *st, char *mddev, num_devs++; tmpdev = tmpdev->next; } - devices = malloc(num_devs * sizeof(*devices)); if (!st && ident->st) st = ident->st; @@ -229,11 +286,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 dev_policy *pol = NULL; + int found_container = 0; if (tmpdev->used > 1) continue; @@ -259,27 +318,7 @@ 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 { - content = &info; - memset(content, 0, sizeof(*content)); - tst->ss->getinfo_super(tst, content); - } - if (dfd >= 0) close(dfd); - - if (tst && tst->sb && tst->ss->container_content - && tst->loaded_container) { - /* tmpdev is a container. We need to be either - * looking for a member, or auto-assembling - */ + } else if (must_be_container(dfd)) { if (st) { /* already found some components, this cannot * be another one. @@ -287,8 +326,77 @@ int Assemble(struct supertype *st, char *mddev, if (report_missmatch) fprintf(stderr, Name ": %s is a container, but we are looking for components\n", devname); - goto loop; + tmpdev->used = 2; + } 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; + } else if (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 { + 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) + /* 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); + return 1; + } + + if (found_container) { + /* tmpdev is a container. We need to be either + * looking for a member, or auto-assembling + */ if (ident->container) { if (ident->container[0] == '/' && @@ -301,6 +409,11 @@ int Assemble(struct supertype *st, char *mddev, if (ident->container[0] != '/') { /* we have a uuid */ int uuid[4]; + + content = &info; + memset(content, 0, sizeof(*content)); + tst->ss->getinfo_super(tst, content, NULL); + if (!parse_uuid(ident->container, uuid) || !same_uuid(content->uuid, uuid, tst->ss->swapuuid)) { if (report_missmatch) @@ -312,161 +425,108 @@ int Assemble(struct supertype *st, char *mddev, } /* It is worth looking inside this container. */ - next_member: - if (tmpdev->content) - content = tmpdev->content; - else - content = tst->ss->container_content(tst); - - 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", + if (verbose > 0) + fprintf(stderr, Name ": looking in container %s\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); - 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", - content->text_version, - devname); - tst->ss->free_super(tst); - tst = NULL; - content = NULL; - if (auto_assem) - goto loop; - return 1; + 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 + break; } + if (!content) { + tmpdev->used = 2; + goto loop; /* empty container */ + } + st = tst; tst = NULL; - if (!auto_assem && tmpdev->next != NULL) { + if (!auto_assem && inargv && tmpdev->next != NULL) { fprintf(stderr, Name ": %s is a container, but is not " "only device given: confused and aborting\n", devname); st->ss->free_super(st); + dev_policy_free(pol); return 1; } - 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. - */ - if (auto_assem) + if (verbose > 0) + fprintf(stderr, Name ": found match on member %s in %s\n", + content->text_version, devname); + + /* make sure we finished the loop */ + tmpdev = NULL; + goto loop; + } else { + + content = &info; + memset(content, 0, sizeof(*content)); + tst->ss->getinfo_super(tst, content, NULL); + + if (!ident_matches(ident, content, tst, + homehost, update, + report_missmatch ? devname : NULL)) 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; + + 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. + */ + 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); + 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); - return 1; + tmpdev->used = 1; } - - tmpdev->used = 1; - loop: - if (tmpdev->content) - goto next_member; + dev_policy_free(pol); + pol = NULL; if (tst) tst->ss->free_super(tst); } @@ -474,16 +534,14 @@ int Assemble(struct supertype *st, char *mddev, if (!st || !st->sb || !content) return 2; - /* Now need to open array the device. Use create_mddev */ + /* Now need to open the array device. Use create_mddev */ if (content == &info) - st->ss->getinfo_super(st, content); + st->ss->getinfo_super(st, content, NULL); trustworthy = FOREIGN; - switch (st->ss->match_home(st, homehost)) { - case 0: - trustworthy = FOREIGN; - name = content->name; - break; + name = content->name; + switch (st->ss->match_home(st, homehost) + ?: st->ss->match_home(st, "any")) { case 1: trustworthy = LOCAL; name = strchr(content->name, ':'); @@ -492,27 +550,34 @@ int Assemble(struct supertype *st, char *mddev, else name = content->name; break; - case -1: - trustworthy = FOREIGN; - break; } - if (!auto_assem && trustworthy == FOREIGN) - /* If the array is listed in mdadm or on + if (!auto_assem) + /* If the array is listed in mdadm.conf or on * command line, then we trust the name * even if the array doesn't look local */ trustworthy = LOCAL; - if (content->name[0] == 0 && + if (name[0] == 0 && content->array.level == LEVEL_CONTAINER) { name = content->text_version; trustworthy = METADATA; } + + if (name[0] && trustworthy != LOCAL && + ! require_homehost && + conf_name_is_free(name)) + trustworthy = LOCAL; + + if (trustworthy == LOCAL && + strchr(name, ':')) + /* Ignore 'host:' prefix of name */ + name = strchr(name, ':')+1; + mdfd = create_mddev(mddev, name, ident->autof, trustworthy, chosen_name); if (mdfd < 0) { st->ss->free_super(st); - free(devices); if (auto_assem) goto try_again; return 1; @@ -525,7 +590,7 @@ int Assemble(struct supertype *st, char *mddev, close(mdfd); return 1; } - if (ioctl(mdfd, GET_ARRAY_INFO, &tmp_inf)==0) { + if (mddev_busy(fd2devnum(mdfd))) { fprintf(stderr, Name ": %s already active, cannot restart it!\n", mddev); for (tmpdev = devlist ; @@ -538,7 +603,6 @@ int Assemble(struct supertype *st, char *mddev, close(mdfd); mdfd = -3; st->ss->free_super(st); - free(devices); if (auto_assem) goto try_again; return 1; @@ -554,6 +618,9 @@ int Assemble(struct supertype *st, char *mddev, #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) { char *devname = tmpdev->devname; struct stat stb; @@ -564,6 +631,7 @@ int Assemble(struct supertype *st, char *mddev, /* prepare useful information in info structures */ struct stat stb2; struct supertype *tst; + int err; fstat(mdfd, &stb2); if (strcmp(update, "uuid")==0 && @@ -583,29 +651,51 @@ int Assemble(struct supertype *st, char *mddev, remove_partitions(dfd); tst = dup_super(st); - tst->ss->load_super(tst, dfd, NULL); - tst->ss->getinfo_super(tst, content); + if (dfd < 0 || tst->ss->load_super(tst, dfd, NULL) != 0) { + fprintf(stderr, Name ": cannot re-read metadata from %s - aborting\n", + devname); + if (dfd >= 0) + close(dfd); + close(mdfd); + free(devices); + free(devmap); + return 1; + } + tst->ss->getinfo_super(tst, content, devmap + devcnt * content->array.raid_disks); memcpy(content->uuid, ident->uuid, 16); strcpy(content->name, ident->name); content->array.md_minor = minor(stb2.st_rdev); - tst->ss->update_super(tst, content, update, - devname, verbose, - ident->uuid_set, homehost); + if (strcmp(update, "byteorder") == 0) + err = 0; + else + err = tst->ss->update_super(tst, content, update, + devname, verbose, + ident->uuid_set, + homehost); + if (err < 0) { + fprintf(stderr, + Name ": --update=%s not understood" + " for %s metadata\n", + update, tst->ss->name); + tst->ss->free_super(tst); + free(tst); + close(mdfd); + close(dfd); + free(devices); + free(devmap); + return 1; + } if (strcmp(update, "uuid")==0 && !ident->uuid_set) { ident->uuid_set = 1; memcpy(ident->uuid, content->uuid, 16); } - if (dfd < 0) - fprintf(stderr, Name ": Cannot open %s for superblock update\n", - devname); - else if (tst->ss->store_super(tst, dfd)) + if (tst->ss->store_super(tst, dfd)) fprintf(stderr, Name ": Could not re-write superblock on %s.\n", devname); - if (dfd >= 0) - close(dfd); + close(dfd); if (strcmp(update, "uuid")==0 && ident->bitmap_fd >= 0 && !bitmap_done) { @@ -626,8 +716,17 @@ int Assemble(struct supertype *st, char *mddev, remove_partitions(dfd); - tst->ss->load_super(tst, dfd, NULL); - tst->ss->getinfo_super(tst, content); + if (dfd < 0 || tst->ss->load_super(tst, dfd, NULL) != 0) { + fprintf(stderr, Name ": cannot re-read metadata from %s - aborting\n", + devname); + if (dfd >= 0) + close(dfd); + close(mdfd); + free(devices); + free(devmap); + return 1; + } + tst->ss->getinfo_super(tst, content, devmap + devcnt * content->array.raid_disks); tst->ss->free_super(tst); close(dfd); } @@ -647,7 +746,7 @@ int Assemble(struct supertype *st, char *mddev, > devices[most_recent].i.events) most_recent = devcnt; } - if (content->array.level == -4) + if (content->array.level == LEVEL_MULTIPATH) /* with multipath, the raid_disk from the superblock is meaningless */ i = devcnt; else @@ -663,9 +762,9 @@ int Assemble(struct supertype *st, char *mddev, } if (i < 10000) { if (i >= bestcnt) { - unsigned int newbestcnt = i+10; + int newbestcnt = i+10; int *newbest = malloc(sizeof(int)*newbestcnt); - unsigned int c; + int c; for (c=0; c < newbestcnt; c++) if (c < bestcnt) newbest[c] = best[c]; @@ -697,6 +796,8 @@ int Assemble(struct supertype *st, char *mddev, "the\n DEVICE list in mdadm.conf" ); close(mdfd); + free(devices); + free(devmap); return 1; } if (best[i] == -1 @@ -706,6 +807,8 @@ 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", @@ -713,13 +816,15 @@ int Assemble(struct supertype *st, char *mddev, if (st) st->ss->free_super(st); close(mdfd); + free(devices); + free(devmap); return 1; } if (update && strcmp(update, "byteorder")==0) st->minor_version = 90; - st->ss->getinfo_super(st, content); + st->ss->getinfo_super(st, content, NULL); clean = content->array.state & 1; /* now we have some devices that might be suitable. @@ -729,7 +834,8 @@ int Assemble(struct supertype *st, char *mddev, memset(avail, 0, content->array.raid_disks); okcnt = 0; sparecnt=0; - for (i=0; i< bestcnt ;i++) { + rebuilding_cnt=0; + for (i=0; i< bestcnt; i++) { int j = best[i]; int event_margin = 1; /* always allow a difference of '1' * like the kernel does @@ -738,23 +844,42 @@ int Assemble(struct supertype *st, char *mddev, /* note: we ignore error flags in multipath arrays * as they don't make sense */ - if (content->array.level != -4) - if (!(devices[j].i.disk.state & (1<array.level != LEVEL_MULTIPATH) + if (!(devices[j].i.disk.state & (1<array.raid_disks > 0 && + devices[most_recent].i.disk.raid_disk >= 0 && + devmap[j * content->array.raid_disks + devices[most_recent].i.disk.raid_disk] == 0) { + if (verbose > -1) + fprintf(stderr, Name ": ignoring %s as it reports %s as failed\n", + devices[j].devname, devices[most_recent].devname); + best[i] = -1; + continue; + } if (devices[j].i.events+event_margin >= devices[most_recent].i.events) { devices[j].uptodate = 1; if (i < content->array.raid_disks) { - okcnt++; - avail[i]=1; + if (devices[j].i.recovery_start == MaxSector || + (content->reshape_active && + j >= content->array.raid_disks - content->delta_disks)) { + okcnt++; + avail[i]=1; + } else + rebuilding_cnt++; } else sparecnt++; } } + free(devmap); while (force && !enough(content->array.level, content->array.raid_disks, content->array.layout, 1, avail, okcnt)) { @@ -764,12 +889,13 @@ int Assemble(struct supertype *st, char *mddev, */ int fd; struct supertype *tst; - long long current_events; + unsigned long long current_events; chosen_drive = -1; - for (i=0; iarray.raid_disks && i < bestcnt; i++) { + for (i = 0; i < content->array.raid_disks && i < bestcnt; i++) { int j = best[i]; if (j>=0 && !devices[j].uptodate && + devices[j].i.recovery_start == MaxSector && (chosen_drive < 0 || devices[j].i.events > devices[chosen_drive].i.events)) @@ -823,7 +949,7 @@ int Assemble(struct supertype *st, char *mddev, /* If there are any other drives of the same vintage, * add them in as well. We can't lose and we might gain */ - for (i=0; iarray.raid_disks && i < bestcnt ; i++) { + for (i = 0; i < content->array.raid_disks && i < bestcnt ; i++) { int j = best[i]; if (j >= 0 && !devices[j].uptodate && @@ -855,6 +981,7 @@ int Assemble(struct supertype *st, char *mddev, fprintf(stderr, Name ": Cannot open %s: %s\n", devices[j].devname, strerror(errno)); close(mdfd); + free(devices); return 1; } if (st->ss->load_super(st,fd, NULL)) { @@ -862,6 +989,7 @@ int Assemble(struct supertype *st, char *mddev, fprintf(stderr, Name ": RAID superblock has disappeared from %s\n", devices[j].devname); close(mdfd); + free(devices); return 1; } close(fd); @@ -869,9 +997,10 @@ int Assemble(struct supertype *st, char *mddev, if (st->sb == NULL) { fprintf(stderr, Name ": No suitable drives found for %s\n", mddev); close(mdfd); + free(devices); return 1; } - st->ss->getinfo_super(st, content); + st->ss->getinfo_super(st, content, NULL); #ifndef MDASSEMBLE sysfs_init(content, mdfd, 0); #endif @@ -932,6 +1061,7 @@ int Assemble(struct supertype *st, char *mddev, fprintf(stderr, Name ": Could not open %s for write - cannot Assemble array.\n", devices[chosen_drive].devname); close(mdfd); + free(devices); return 1; } if (st->ss->store_super(st, fd)) { @@ -939,6 +1069,7 @@ int Assemble(struct supertype *st, char *mddev, fprintf(stderr, Name ": Could not re-write superblock on %s\n", devices[chosen_drive].devname); close(mdfd); + free(devices); return 1; } close(fd); @@ -952,6 +1083,10 @@ int Assemble(struct supertype *st, char *mddev, if (content->reshape_active) { int err = 0; int *fdlist = malloc(sizeof(int)* bestcnt); + if (verbose > 0) + fprintf(stderr, Name ":%s has an active reshape - checking " + "if critical section needs to be restored\n", + chosen_name); for (i=0; i= 0) { @@ -966,14 +1101,17 @@ int Assemble(struct supertype *st, char *mddev, fdlist[i] = -1; } if (!err) - err = Grow_restart(st, content, fdlist, bestcnt, backup_file); + err = Grow_restart(st, content, fdlist, bestcnt, backup_file, verbose > 0); while (i>0) { i--; if (fdlist[i]>=0) close(fdlist[i]); } if (err) { fprintf(stderr, Name ": Failed to restore critical section for reshape, sorry.\n"); + if (backup_file == NULL) + fprintf(stderr," Possibly you needed to specify the --backup-file\n"); close(mdfd); + free(devices); return err; } } @@ -997,13 +1135,17 @@ int Assemble(struct supertype *st, char *mddev, if (rv) { fprintf(stderr, Name ": failed to set array info for %s: %s\n", mddev, strerror(errno)); + ioctl(mdfd, STOP_ARRAY, NULL); close(mdfd); + free(devices); return 1; } if (ident->bitmap_fd >= 0) { if (ioctl(mdfd, SET_BITMAP_FILE, ident->bitmap_fd) != 0) { fprintf(stderr, Name ": SET_BITMAP_FILE failed.\n"); + ioctl(mdfd, STOP_ARRAY, NULL); close(mdfd); + free(devices); return 1; } } else if (ident->bitmap_file) { @@ -1012,13 +1154,17 @@ int Assemble(struct supertype *st, char *mddev, if (bmfd < 0) { fprintf(stderr, Name ": Could not open bitmap file %s\n", ident->bitmap_file); + ioctl(mdfd, STOP_ARRAY, NULL); close(mdfd); + free(devices); return 1; } if (ioctl(mdfd, SET_BITMAP_FILE, bmfd) != 0) { fprintf(stderr, Name ": Failed to set bitmapfile for %s\n", mddev); close(bmfd); + ioctl(mdfd, STOP_ARRAY, NULL); close(mdfd); + free(devices); return 1; } close(bmfd); @@ -1064,13 +1210,15 @@ int Assemble(struct supertype *st, char *mddev, fprintf(stderr, Name ": Container %s has been " "assembled with %d drive%s", mddev, okcnt+sparecnt, okcnt+sparecnt==1?"":"s"); - if (okcnt < content->array.raid_disks) + if (okcnt < (unsigned)content->array.raid_disks) fprintf(stderr, " (out of %d)", content->array.raid_disks); fprintf(stderr, "\n"); } sysfs_uevent(content, "change"); + wait_for(chosen_name, mdfd); close(mdfd); + free(devices); return 0; } @@ -1078,14 +1226,29 @@ int Assemble(struct supertype *st, char *mddev, (runstop <= 0 && ( enough(content->array.level, content->array.raid_disks, content->array.layout, clean, avail, okcnt) && - (okcnt >= req_cnt || start_partial_ok) + (okcnt + rebuilding_cnt >= req_cnt || start_partial_ok) ))) { - if (ioctl(mdfd, RUN_ARRAY, NULL)==0) { + /* This array is good-to-go. + * If a reshape is in progress then we might need to + * continue monitoring it. In that case we start + * it read-only and let the grow code make it writable. + */ + int rv; +#ifndef MDASSEMBLE + if (content->reshape_active && + content->delta_disks <= 0) + rv = Grow_continue(mdfd, st, content, backup_file); + else +#endif + rv = ioctl(mdfd, RUN_ARRAY, NULL); + if (rv == 0) { if (verbose >= 0) { fprintf(stderr, Name ": %s has been started with %d drive%s", mddev, okcnt, okcnt==1?"":"s"); - if (okcnt < content->array.raid_disks) + if (okcnt < (unsigned)content->array.raid_disks) fprintf(stderr, " (out of %d)", content->array.raid_disks); + if (rebuilding_cnt) + fprintf(stderr, "%s %d rebuilding", sparecnt?",":" and", rebuilding_cnt); if (sparecnt) fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); fprintf(stderr, ".\n"); @@ -1104,8 +1267,31 @@ int Assemble(struct supertype *st, char *mddev, (4 * content->array.chunk_size / 4096) + 1); } } + if (okcnt < (unsigned)content->array.raid_disks) { + /* If any devices did not get added + * because the kernel rejected them based + * on event count, try adding them + * again providing the action policy is + * 're-add' or greater. The bitmap + * might allow them to be included, or + * they will become spares. + */ + 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)) + continue; + rv = add_disk(mdfd, st, content, + &devices[j].i); + if (rv == 0 && verbose >= 0) + fprintf(stderr, + Name ": %s has been re-added.\n", + devices[j].devname); + } + } + } + wait_for(mddev, mdfd); close(mdfd); - wait_for(mddev); if (auto_assem) { int usecs = 1; /* There is a nasty race with 'mdadm --monitor'. @@ -1132,6 +1318,7 @@ int Assemble(struct supertype *st, char *mddev, usecs <<= 1; } } + free(devices); return 0; } fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n", @@ -1152,19 +1339,23 @@ int Assemble(struct supertype *st, char *mddev, if (auto_assem) ioctl(mdfd, STOP_ARRAY, NULL); close(mdfd); + free(devices); return 1; } if (runstop == -1) { fprintf(stderr, Name ": %s assembled from %d drive%s", mddev, okcnt, okcnt==1?"":"s"); - if (okcnt != content->array.raid_disks) + if (okcnt != (unsigned)content->array.raid_disks) fprintf(stderr, " (out of %d)", content->array.raid_disks); fprintf(stderr, ", but not started.\n"); close(mdfd); + free(devices); return 0; } if (verbose >= -1) { fprintf(stderr, Name ": %s assembled from %d drive%s", mddev, okcnt, okcnt==1?"":"s"); + if (rebuilding_cnt) + fprintf(stderr, "%s %d rebuilding", sparecnt?", ":" and ", rebuilding_cnt); if (sparecnt) fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s"); if (!enough(content->array.level, content->array.raid_disks, @@ -1178,7 +1369,7 @@ int Assemble(struct supertype *st, char *mddev, "array while not clean - consider " "--force.\n"); else { - if (req_cnt == content->array.raid_disks) + if (req_cnt == (unsigned)content->array.raid_disks) fprintf(stderr, " - need all %d to start it", req_cnt); else fprintf(stderr, " - need %d of %d to start", req_cnt, content->array.raid_disks); @@ -1188,6 +1379,7 @@ int Assemble(struct supertype *st, char *mddev, if (auto_assem) ioctl(mdfd, STOP_ARRAY, NULL); close(mdfd); + free(devices); return 1; } else { /* The "chosen_drive" is a good choice, and if necessary, the superblock has @@ -1204,6 +1396,7 @@ int Assemble(struct supertype *st, char *mddev, } close(mdfd); + free(devices); return 0; } @@ -1228,45 +1421,58 @@ int assemble_container_content(struct supertype *st, int mdfd, sysfs_free(sra); for (dev = content->devs; dev; dev = dev->next) - if (sysfs_add_disk(content, dev) == 0) + if (sysfs_add_disk(content, dev, 1) == 0) working++; else if (errno == EEXIST) preexist++; if (working == 0) { close(mdfd); return 1;/* Nothing new, don't try to start */ - } else if (runstop > 0 || + } + + map_update(&map, fd2devnum(mdfd), + content->text_version, + content->uuid, chosen_name); + + if (runstop > 0 || (working + preexist) >= content->array.working_disks) { + int err; - map_update(&map, fd2devnum(mdfd), - content->text_version, - content->uuid, chosen_name); switch(content->array.level) { case LEVEL_LINEAR: case LEVEL_MULTIPATH: case 0: - sysfs_set_str(content, NULL, "array_state", - "active"); + err = sysfs_set_str(content, NULL, "array_state", + "active"); break; default: - sysfs_set_str(content, NULL, "array_state", + err = sysfs_set_str(content, NULL, "array_state", "readonly"); /* start mdmon if needed. */ - if (!mdmon_running(st->container_dev)) - start_mdmon(st->container_dev); - ping_monitor(devnum2devname(st->container_dev)); + if (!err) { + if (!mdmon_running(st->container_dev)) + start_mdmon(st->container_dev); + ping_monitor(devnum2devname(st->container_dev)); + } break; } - sysfs_set_safemode(content, content->safe_mode_delay); + if (!err) + sysfs_set_safemode(content, content->safe_mode_delay); if (verbose >= 0) { - fprintf(stderr, Name - ": Started %s with %d devices", - chosen_name, working + preexist); + if (err) + fprintf(stderr, Name + ": array %s now has %d devices", + chosen_name, working + preexist); + else + fprintf(stderr, Name + ": Started %s with %d devices", + chosen_name, working + preexist); if (preexist) fprintf(stderr, " (%d new)", working); fprintf(stderr, "\n"); } - wait_for(chosen_name); + if (!err) + wait_for(chosen_name, mdfd); close(mdfd); return 0; /* FIXME should have an O_EXCL and wait for read-auto */