]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Assemble.c
Switch open_subarray to use the new load_container
[thirdparty/mdadm.git] / Assemble.c
index 014d644b42667a5755b2edb5736c15d9775f360b..37b8413377047914c28f03001785b8f74a6cf98e 100644 (file)
@@ -145,12 +145,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;
@@ -182,7 +183,7 @@ 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) {
@@ -211,7 +212,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;
 
@@ -230,6 +230,7 @@ int Assemble(struct supertype *st, char *mddev,
                int dfd;
                struct stat stb;
                struct supertype *tst = dup_super(st);
+               struct dev_policy *pol = NULL;
 
                if (tmpdev->used > 1) continue;
 
@@ -260,21 +261,29 @@ int Assemble(struct supertype *st, char *mddev,
                                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)) {
+                          !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 (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);
+                       tst->ss->getinfo_super(tst, content, NULL);
                }
                if (dfd >= 0) close(dfd);
 
@@ -322,7 +331,7 @@ int Assemble(struct supertype *st, char *mddev,
                        if (tmpdev->content)
                                content = tmpdev->content;
                        else
-                               content = tst->ss->container_content(tst);
+                               content = tst->ss->container_content(tst, NULL);
                        if (!content)
                                goto loop; /* empty container */
 
@@ -395,6 +404,7 @@ int Assemble(struct supertype *st, char *mddev,
                                devname);
                        if (st)
                                st->ss->free_super(st);
+                       dev_policy_free(pol);
                        return 1;
                }
 
@@ -416,6 +426,7 @@ int Assemble(struct supertype *st, char *mddev,
                                content = NULL;
                                if (auto_assem)
                                        goto loop;
+                               dev_policy_free(pol);
                                return 1;
                        }
                        if (ident->member && ident->member[0]) {
@@ -434,11 +445,12 @@ int Assemble(struct supertype *st, char *mddev,
                                }
                        }
                        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;
                        }
                        if (verbose > 0)
@@ -488,12 +500,15 @@ int Assemble(struct supertype *st, char *mddev,
                                devname);
                        tst->ss->free_super(tst);
                        st->ss->free_super(st);
+                       dev_policy_free(pol);
                        return 1;
                }
 
                tmpdev->used = 1;
 
        loop:
+               dev_policy_free(pol);
+               pol = NULL;
                if (tmpdev->content)
                        goto next_member;
                if (tst)
@@ -505,7 +520,7 @@ int Assemble(struct supertype *st, char *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;
        name = content->name;
@@ -547,7 +562,6 @@ int Assemble(struct supertype *st, char *mddev,
                            chosen_name);
        if (mdfd < 0) {
                st->ss->free_super(st);
-               free(devices);
                if (auto_assem)
                        goto try_again;
                return 1;
@@ -573,7 +587,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;
@@ -590,6 +603,8 @@ int Assemble(struct supertype *st, char *mddev,
        /* 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;
@@ -600,6 +615,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 &&
@@ -619,29 +635,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) {
@@ -662,8 +700,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);
                }
@@ -699,9 +746,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];
@@ -733,6 +780,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
@@ -751,13 +800,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.
@@ -767,7 +818,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
@@ -783,16 +835,33 @@ int Assemble(struct supertype *st, char *mddev,
                                        sparecnt++;
                                continue;
                        }
+               /* If this devices thinks that 'most_recent' has failed, then
+                * we must reject this device.
+                */
+               if (j != most_recent &&
+                   content->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) {
+                                       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)) {
@@ -802,12 +871,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; i<content->array.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))
@@ -861,7 +931,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; i<content->array.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 &&
@@ -893,6 +963,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)) {
@@ -900,6 +971,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);
@@ -907,9 +979,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
@@ -970,6 +1043,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)) {
@@ -977,6 +1051,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);
@@ -1018,6 +1093,7 @@ int Assemble(struct supertype *st, char *mddev,
                        if (backup_file == NULL)
                                fprintf(stderr,"      Possibly you needed to specify the --backup-file\n");
                        close(mdfd);
+                       free(devices);
                        return err;
                }
        }
@@ -1041,13 +1117,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) {
@@ -1056,13 +1136,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);
@@ -1108,7 +1192,7 @@ 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");
@@ -1116,6 +1200,7 @@ int Assemble(struct supertype *st, char *mddev,
                        sysfs_uevent(content, "change");
                        wait_for(chosen_name, mdfd);
                        close(mdfd);
+                       free(devices);
                        return 0;
                }
 
@@ -1123,7 +1208,7 @@ 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)
                             ))) {
                        /* This array is good-to-go.
                         * If a reshape is in progress then we might need to
@@ -1142,8 +1227,10 @@ int Assemble(struct supertype *st, char *mddev,
                                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");
@@ -1162,6 +1249,29 @@ 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);
                                if (auto_assem) {
@@ -1190,6 +1300,7 @@ int Assemble(struct supertype *st, char *mddev,
                                                usecs <<= 1;
                                        }
                                }
+                               free(devices);
                                return 0;
                        }
                        fprintf(stderr, Name ": failed to RUN_ARRAY %s: %s\n",
@@ -1210,19 +1321,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,
@@ -1236,7 +1351,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);
@@ -1246,6 +1361,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
@@ -1262,6 +1378,7 @@ int Assemble(struct supertype *st, char *mddev,
 
        }
        close(mdfd);
+       free(devices);
        return 0;
 }