]> git.ipfire.org Git - thirdparty/mdadm.git/blobdiff - Assemble.c
Support external metadata recovery-resume
[thirdparty/mdadm.git] / Assemble.c
index 0aff1be8350c85afb7747d83f746949ed30214b7..014d644b42667a5755b2edb5736c15d9775f360b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * mdadm - manage Linux "md" devices aka RAID arrays.
  *
- * Copyright (C) 2001-2006 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
  *
  *
  *    This program is free software; you can redistribute it and/or modify
  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  *    Author: Neil Brown
- *    Email: <neilb@cse.unsw.edu.au>
- *    Paper: Neil Brown
- *           School of Computer Science and Engineering
- *           The University of New South Wales
- *           Sydney, 2052
- *           Australia
+ *    Email: <neilb@suse.de>
  */
 
 #include       "mdadm.h"
@@ -79,7 +74,7 @@ int Assemble(struct supertype *st, char *mddev,
             mddev_ident_t ident,
             mddev_dev_t 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 +135,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 {
@@ -168,7 +163,6 @@ int Assemble(struct supertype *st, char *mddev,
        mddev_dev_t tmpdev;
        struct mdinfo info;
        struct mdinfo *content = NULL;
-       mdu_array_info_t tmp_inf;
        char *avail;
        int nextspare = 0;
        char *name = NULL;
@@ -189,6 +183,8 @@ int Assemble(struct supertype *st, char *mddev,
        if (!devlist &&
            ident->uuid_set == 0 &&
            ident->super_minor < 0 &&
+           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");
@@ -264,6 +260,13 @@ int Assemble(struct supertype *st, char *mddev,
                                fprintf(stderr, Name ": no recogniseable superblock on %s\n",
                                        devname);
                        tmpdev->used = 2;
+               } else if (auto_assem && st == NULL &&
+                          !conf_test_metadata(tst->ss->name)) {
+                       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 if (tst->ss->load_super(tst,dfd, NULL)) {
                        if (report_missmatch)
                                fprintf( stderr, Name ": no RAID superblock on %s\n",
@@ -312,15 +315,20 @@ int Assemble(struct supertype *st, char *mddev,
                        }
                        /* It is worth looking inside this container.
                         */
+                       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 = 1;
+                               tmpdev->used = 2;
 
                } else if (ident->container || ident->member) {
                        /* No chance of this matching if we don't have
@@ -396,14 +404,35 @@ int Assemble(struct supertype *st, char *mddev,
                         * 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);
+                       skip:
+                               if (tmpdev->content)
+                                       goto next_member;
+                               tst->ss->free_super(tst);
+                               tst = NULL;
+                               content = NULL;
                                if (auto_assem)
                                        goto loop;
-                               fprintf(stderr, Name ": member %s in %s is already assembled\n",
-                                       content->text_version,
-                                       devname);
-                               tst->ss->free_super(tst);
                                return 1;
                        }
+                       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;
+                               }
+                       }
                        st = tst; tst = NULL;
                        if (!auto_assem && tmpdev->next != NULL) {
                                fprintf(stderr, Name ": %s is a container, but is not "
@@ -412,6 +441,9 @@ int Assemble(struct supertype *st, char *mddev,
                                st->ss->free_super(st);
                                return 1;
                        }
+                       if (verbose > 0)
+                               fprintf(stderr, Name ": found match on member %s in %s\n",
+                                       content->text_version, devname);
                        break;
                }
                if (st == NULL)
@@ -471,16 +503,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);
 
        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, ':');
@@ -489,22 +519,30 @@ 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) {
@@ -522,7 +560,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 ;
@@ -551,6 +589,7 @@ int Assemble(struct supertype *st, char *mddev,
 #endif
        /* Ok, no bad inconsistancy, we can try updating etc */
        bitmap_done = 0;
+       content->update_private = NULL;
        for (tmpdev = devlist; tmpdev; tmpdev=tmpdev->next) if (tmpdev->used == 1) {
                char *devname = tmpdev->devname;
                struct stat stb;
@@ -644,7 +683,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
@@ -703,6 +742,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",
@@ -735,8 +776,8 @@ 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<<MD_DISK_SYNC))) {
+               if (content->array.level != LEVEL_MULTIPATH)
+                       if (!(devices[j].i.disk.state & (1<<MD_DISK_ACTIVE))) {
                                if (!(devices[j].i.disk.state
                                      & (1<<MD_DISK_FAULTY)))
                                        sparecnt++;
@@ -767,7 +808,6 @@ int Assemble(struct supertype *st, char *mddev,
                        int j = best[i];
                        if (j>=0 &&
                            !devices[j].uptodate &&
-                           devices[j].i.events > 0 &&
                            (chosen_drive < 0 ||
                             devices[j].i.events
                             > devices[chosen_drive].i.events))
@@ -825,7 +865,6 @@ int Assemble(struct supertype *st, char *mddev,
                        int j = best[i];
                        if (j >= 0 &&
                            !devices[j].uptodate &&
-                           devices[j].i.events > 0 &&
                            devices[j].i.events == current_events) {
                                chosen_drive = j;
                                goto add_another;
@@ -889,6 +928,8 @@ int Assemble(struct supertype *st, char *mddev,
                        continue;
 
                devices[j].i.disk.state = desired_state;
+               if (!(devices[j].i.array.state & 1))
+                       clean = 0;
 
                if (st->ss->update_super(st, &devices[j].i, "assemble", NULL,
                                         verbose, 0, NULL)) {
@@ -949,6 +990,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<bestcnt; i++) {
                        int j = best[i];
                        if (j >= 0) {
@@ -963,13 +1008,15 @@ 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);
                        return err;
                }
@@ -1067,6 +1114,7 @@ int Assemble(struct supertype *st, char *mddev,
                                fprintf(stderr, "\n");
                        }
                        sysfs_uevent(content, "change");
+                       wait_for(chosen_name, mdfd);
                        close(mdfd);
                        return 0;
                }
@@ -1077,7 +1125,20 @@ int Assemble(struct supertype *st, char *mddev,
                              content->array.layout, clean, avail, okcnt) &&
                       (okcnt >= 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");
@@ -1087,6 +1148,21 @@ int Assemble(struct supertype *st, char *mddev,
                                                fprintf(stderr, " and %d spare%s", sparecnt, sparecnt==1?"":"s");
                                        fprintf(stderr, ".\n");
                                }
+                               if (content->reshape_active &&
+                                   content->array.level >= 4 &&
+                                   content->array.level <= 6) {
+                                       /* might need to increase the size
+                                        * of the stripe cache - default is 256
+                                        */
+                                       if (256 < 4 * (content->array.chunk_size/4096)) {
+                                               struct mdinfo *sra = sysfs_read(mdfd, 0, 0);
+                                               if (sra)
+                                                       sysfs_set_num(sra, NULL,
+                                                                     "stripe_cache_size",
+                                                                     (4 * content->array.chunk_size / 4096) + 1);
+                                       }
+                               }
+                               wait_for(mddev, mdfd);
                                close(mdfd);
                                if (auto_assem) {
                                        int usecs = 1;
@@ -1202,57 +1278,78 @@ int assemble_container_content(struct supertype *st, int mdfd,
 
        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)
+               if (sysfs_set_array(content, md_get_version(mdfd)) != 0) {
+                       close(mdfd);
                        return 1;
+               }
        if (sra)
                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)
-               /* Nothing new, don't try to start */ ;
-       else if (runstop > 0 ||
+       if (working == 0) {
+               close(mdfd);
+               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) {
+               int err;
+
                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");
                }
+               if (!err)
+                       wait_for(chosen_name, mdfd);
+               close(mdfd);
+               return 0;
                /* FIXME should have an O_EXCL and wait for read-auto */
-       } else
+       } else {
                if (verbose >= 0)
                        fprintf(stderr, Name
                                ": %s assembled with %d devices but "
                                "not started\n",
                                chosen_name, working);
-       map_update(&map, fd2devnum(mdfd),
-                  content->text_version,
-                  content->uuid, chosen_name);
-
-       return 0;
+               close(mdfd);
+               return 1;
+       }
 }
 #endif