]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
mdmon: fix wrong array state when disk fails during mdmon startup
authorArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Tue, 7 May 2019 14:08:47 +0000 (16:08 +0200)
committerJes Sorensen <jsorensen@fb.com>
Mon, 20 May 2019 18:14:36 +0000 (14:14 -0400)
If a member drive disappears and is set faulty by the kernel during
mdmon startup, after ss->load_container() but before manage_new(), mdmon
will try to readd the faulty drive to the array and start rebuilding.
Metadata on the active drive is updated, but the faulty drive is not
removed from the array and is left in a "blocked" state and any write
request to the array will block. If the faulty drive reappears in the
system e.g. after a reboot, the array will not assemble because metadata
on the drives will be incompatible (at least on imsm).

Fix this by adding a new option for sysfs_read(): "GET_DEVS_ALL". This
is an extension for the "GET_DEVS" option and causes all member devices
to be returned, even if the associated block device has been removed.
Use this option in manage_new() to include the faulty device on the
active_array's devices list. Mdmon will then properly remove the faulty
device from the array and update the metadata to reflect the degraded
state.

Signed-off-by: Artur Paszkiewicz <artur.paszkiewicz@intel.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
managemon.c
mdadm.h
super-intel.c
sysfs.c

index 29b91bad863f267d96801fef34adc54c5ef56fdf..200cf83e3436a9b9890097b052b4fc379fb7e334 100644 (file)
@@ -678,7 +678,7 @@ static void manage_new(struct mdstat_ent *mdstat,
        mdi = sysfs_read(-1, mdstat->devnm,
                         GET_LEVEL|GET_CHUNK|GET_DISKS|GET_COMPONENT|
                         GET_SAFEMODE|GET_DEVS|GET_OFFSET|GET_SIZE|GET_STATE|
-                        GET_LAYOUT);
+                        GET_LAYOUT|GET_DEVS_ALL);
 
        if (!mdi)
                return;
diff --git a/mdadm.h b/mdadm.h
index 705bd9b53b3ae28e9cd403640a81e4705d4d467e..427cc523fae2a9cc41d92f8b69c64fa7a9a393be 100644 (file)
--- a/mdadm.h
+++ b/mdadm.h
@@ -647,6 +647,7 @@ enum sysfs_read_flags {
        GET_ERROR       = (1 << 24),
        GET_ARRAY_STATE = (1 << 25),
        GET_CONSISTENCY_POLICY  = (1 << 26),
+       GET_DEVS_ALL    = (1 << 27),
 };
 
 /* If fd >= 0, get the array it is open on,
index 2ba045aa3c396f9316f5f2a1bc8ac9b5ca71dde2..4fd5e84d0bc785e8cca476be2b8083000e0b0367 100644 (file)
@@ -8560,7 +8560,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state)
        disk = get_imsm_disk(super, ord_to_idx(ord));
 
        /* check for new failures */
-       if (state & DS_FAULTY) {
+       if (disk && (state & DS_FAULTY)) {
                if (mark_failure(super, dev, disk, ord_to_idx(ord)))
                        super->updates_pending++;
        }
diff --git a/sysfs.c b/sysfs.c
index df6fdda35cd829e35be45a22c0d203a1ee99b019..2dd9ab63b9ebfd382566866af965fd9ae648e163 100644 (file)
--- a/sysfs.c
+++ b/sysfs.c
@@ -313,17 +313,22 @@ struct mdinfo *sysfs_read(int fd, char *devnm, unsigned long options)
                        /* assume this is a stale reference to a hot
                         * removed device
                         */
-                       free(dev);
-                       continue;
+                       if (!(options & GET_DEVS_ALL)) {
+                               free(dev);
+                               continue;
+                       }
+               } else {
+                       sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
                }
-               sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
 
-               /* special case check for block devices that can go 'offline' */
-               strcpy(dbase, "block/device/state");
-               if (load_sys(fname, buf, sizeof(buf)) == 0 &&
-                   strncmp(buf, "offline", 7) == 0) {
-                       free(dev);
-                       continue;
+               if (!(options & GET_DEVS_ALL)) {
+                       /* special case check for block devices that can go 'offline' */
+                       strcpy(dbase, "block/device/state");
+                       if (load_sys(fname, buf, sizeof(buf)) == 0 &&
+                           strncmp(buf, "offline", 7) == 0) {
+                               free(dev);
+                               continue;
+                       }
                }
 
                /* finally add this disk to the array */