]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
Incremental - avoid including wayward devices.
authorNeilBrown <neilb@suse.de>
Sun, 28 Nov 2010 22:40:15 +0000 (09:40 +1100)
committerNeilBrown <neilb@suse.de>
Sun, 28 Nov 2010 22:40:15 +0000 (09:40 +1100)
If a devices - typically in a mirrored set - is assembled
independently of the other devices, and then attempted to be brought
back into the set, it could contain inconsistent data.  It should not
be included.

So detect this situation by ensuring that the 'most recent' device is
believed to be active by every other device.  If a device is wayward,
it will only consider fellow wayward devices to be active and will
think all others are failed or missing.

This patches fixes --incremental, --assemble was done in an earlier
patch.

Signed-off-by: NeilBrown <neilb@suse.de>
Incremental.c
mdadm.h
util.c

index c6abf00ef6c3497d8de4c36affd8a459e9e17eaf..8cae1ee5cb9f9bf54490adc9993117e41de81cf8 100644 (file)
@@ -94,13 +94,13 @@ int Incremental(char *devname, int verbose, int runstop,
         */
        struct stat stb;
        struct mdinfo info, dinfo;
-       struct mdinfo *sra = NULL;
+       struct mdinfo *sra = NULL, *d;
        struct mddev_ident *match;
        char chosen_name[1024];
        int rv = 1;
        struct map_ent *mp, *map = NULL;
        int dfd = -1, mdfd = -1;
-       char *avail;
+       char *avail = NULL;
        int active_disks;
        int trustworthy;
        char *name_to_use;
@@ -462,7 +462,7 @@ int Incremental(char *devname, int verbose, int runstop,
                        rv = 0;
                return rv;
        }
-       avail = NULL;
+
        /* We have added something to the array, so need to re-read the
         * state.  Eventually this state should be kept up-to-date as
         * things change.
@@ -474,7 +474,6 @@ int Incremental(char *devname, int verbose, int runstop,
        if (enough(info.array.level, info.array.raid_disks,
                   info.array.layout, info.array.state & 1,
                   avail, active_disks) == 0) {
-               free(avail);
                if (verbose >= 0)
                        fprintf(stderr, Name
                             ": %s attached to %s, not enough to start (%d).\n",
@@ -483,7 +482,6 @@ int Incremental(char *devname, int verbose, int runstop,
                rv = 0;
                goto out;
        }
-       free(avail);
 
        /* 7b/ if yes, */
        /* - if number of OK devices match expected, or -R and there */
@@ -522,6 +520,12 @@ int Incremental(char *devname, int verbose, int runstop,
                        }
                        close(bmfd);
                }
+               /* Need to remove from the array any devices which
+                * 'count_active' discerned were too old or inappropriate
+                */
+               for (d = sra ? sra->devs : NULL ; d ; d = d->next)
+                       if (d->disk.state & (1<<MD_DISK_REMOVED))
+                               remove_disk(mdfd, st, sra, d);
 
                if ((sra == NULL || active_disks >= info.array.working_disks)
                    && trustworthy != FOREIGN)
@@ -563,6 +567,7 @@ int Incremental(char *devname, int verbose, int runstop,
                rv = 0;
        }
 out:
+       free(avail);
        if (dfd >= 0)
                close(dfd);
        if (mdfd >= 0)
@@ -697,14 +702,22 @@ static int count_active(struct supertype *st, struct mdinfo *sra,
 {
        /* count how many devices in sra think they are active */
        struct mdinfo *d;
-       int cnt = 0, cnt1 = 0;
+       int cnt = 0;
        __u64 max_events = 0;
        char *avail = NULL;
+       int *best;
+       char *devmap = NULL;
+       int numdevs = 0;
+       int devnum;
+       int b, i;
+       int raid_disks = 0;
 
        if (!sra)
                return 0;
 
-       for (d = sra->devs ; d ; d = d->next) {
+       for (d = sra->devs ; d ; d = d->next)
+               numdevs++;
+       for (d = sra->devs, devnum=0 ; d ; d = d->next, devnum++) {
                char dn[30];
                int dfd;
                int ok;
@@ -718,15 +731,21 @@ static int count_active(struct supertype *st, struct mdinfo *sra,
                close(dfd);
                if (ok != 0)
                        continue;
-               st->ss->getinfo_super(st, &info, NULL);
+               info.array.raid_disks = raid_disks;
+               st->ss->getinfo_super(st, &info, devmap + raid_disks * devnum);
                if (!avail) {
-                       avail = malloc(info.array.raid_disks);
+                       raid_disks = info.array.raid_disks;
+                       avail = calloc(raid_disks, 1);
                        if (!avail) {
                                fprintf(stderr, Name ": out of memory.\n");
                                exit(1);
                        }
-                       memset(avail, 0, info.array.raid_disks);
                        *availp = avail;
+
+                       best = calloc(raid_disks, sizeof(int));
+                       devmap = calloc(raid_disks * numdevs, 1);
+
+                       st->ss->getinfo_super(st, &info, devmap);
                }
 
                if (info.disk.state & (1<<MD_DISK_SYNC))
@@ -735,27 +754,28 @@ static int count_active(struct supertype *st, struct mdinfo *sra,
                                cnt++;
                                max_events = info.events;
                                avail[info.disk.raid_disk] = 2;
+                               best[info.disk.raid_disk] = devnum;
                                st->ss->getinfo_super(st, bestinfo, NULL);
                        } else if (info.events == max_events) {
-                               cnt++;
                                avail[info.disk.raid_disk] = 2;
+                               best[info.disk.raid_disk] = devnum;
                        } else if (info.events == max_events-1) {
-                               cnt1++;
-                               avail[info.disk.raid_disk] = 1;
+                               if (avail[info.disk.raid_disk] == 0) {
+                                       avail[info.disk.raid_disk] = 1;
+                                       best[info.disk.raid_disk] = devnum;
+                               }
                        } else if (info.events < max_events - 1)
                                ;
                        else if (info.events == max_events+1) {
                                int i;
-                               cnt1 = cnt;
-                               cnt = 1;
                                max_events = info.events;
-                               for (i=0; i<info.array.raid_disks; i++)
+                               for (i=0; i < raid_disks; i++)
                                        if (avail[i])
                                                avail[i]--;
                                avail[info.disk.raid_disk] = 2;
+                               best[info.disk.raid_disk] = devnum;
                                st->ss->getinfo_super(st, bestinfo, NULL);
                        } else { /* info.events much bigger */
-                               cnt = 1; cnt1 = 0;
                                memset(avail, 0, info.disk.raid_disk);
                                max_events = info.events;
                                avail[info.disk.raid_disk] = 2;
@@ -764,7 +784,31 @@ static int count_active(struct supertype *st, struct mdinfo *sra,
                }
                st->ss->free_super(st);
        }
-       return cnt + cnt1;
+       if (!avail)
+               return 0;
+       /* We need to reject any device that thinks the best device is
+        * failed or missing */
+       for (b = 0; b < raid_disks; b++)
+               if (avail[b] == 2)
+                       break;
+       cnt = 0;
+       for (i = 0 ; i < raid_disks ; i++) {
+               if (i != b && avail[i])
+                       if (devmap[raid_disks * best[i] + b] == 0) {
+                               /* This device thinks 'b' is failed -
+                                * don't use it */
+                               devnum = best[i];
+                               for (d=sra->devs ; devnum; d = d->next)
+                                       devnum--;
+                               d->disk.state |= (1 << MD_DISK_REMOVED);
+                               avail[i] = 0;
+                       }
+               if (avail[i])
+                       cnt++;
+       }
+       free(best);
+       free(devmap);
+       return cnt;
 }
 
 static int array_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
diff --git a/mdadm.h b/mdadm.h
index ff9c72f7a8ec2faf28547ef0bda0a828d29c4657..f9a53fe47c7b2aa89cad9083d0ffc926f8048e5e 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -1068,6 +1068,8 @@ extern int assemble_container_content(struct supertype *st, int mdfd,
 
 extern int add_disk(int mdfd, struct supertype *st,
                    struct mdinfo *sra, struct mdinfo *info);
+extern int remove_disk(int mdfd, struct supertype *st,
+                      struct mdinfo *sra, struct mdinfo *info);
 extern int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info);
 unsigned long long min_recovery_start(struct mdinfo *array);
 
diff --git a/util.c b/util.c
index b018d2998158c4096d415c2783b8174a295cfc86..2a206448e4e2b07586d4baa7968941ef108afb86 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1626,6 +1626,21 @@ int add_disk(int mdfd, struct supertype *st,
        return rv;
 }
 
+int remove_disk(int mdfd, struct supertype *st,
+               struct mdinfo *sra, struct mdinfo *info)
+{
+       int rv;
+       /* Remove the disk given by 'info' from the array */
+#ifndef MDASSEMBLE
+       if (st->ss->external)
+               rv = sysfs_set_str(sra, info, "slot", "none");
+       else
+#endif
+               rv = ioctl(mdfd, HOT_REMOVE_DISK, makedev(info->disk.major,
+                                                         info->disk.minor));
+       return rv;
+}
+
 int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info)
 {
        /* Initialise kernel's knowledge of array.