4262b8
From 53d80a42f7077a087b976f7dfcdb34f107f2ce69 Mon Sep 17 00:00:00 2001
4262b8
From: Hubert Kario <hubert@kario.pl>
4262b8
Date: Sun, 20 Sep 2020 18:59:58 +0200
4262b8
Subject: [PATCH] Try stopping MD RAID devices in shutdown too
4262b8
4262b8
Currently the systemd-shutdown command attempts to stop swaps, DM
4262b8
(crypt, LVM2) and loop devices, but it doesn't attempt to stop MD
4262b8
RAID devices, which means that if the RAID is set up on crypt,
4262b8
loop, etc. device, it won't be able to stop those underlying devices.
4262b8
4262b8
This code extends the shutdown application to also attempt stopping
4262b8
the MD RAID devices.
4262b8
4262b8
Signed-off-by: Hubert Kario <hubert@kario.pl>
4262b8
(cherry picked from commit 0b220a5f2a31844eaa1f5426bab02d41d54f471c)
4262b8
4262b8
Resolves: #2120608
4262b8
---
4262b8
 src/core/shutdown.c |  37 +++++++++----
4262b8
 src/core/umount.c   | 125 ++++++++++++++++++++++++++++++++++++++++++++
4262b8
 src/core/umount.h   |   2 +
4262b8
 3 files changed, 154 insertions(+), 10 deletions(-)
4262b8
4262b8
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
4262b8
index 038345b752..b8a983986a 100644
4262b8
--- a/src/core/shutdown.c
4262b8
+++ b/src/core/shutdown.c
4262b8
@@ -251,7 +251,7 @@ static void sync_with_progress(void) {
4262b8
 }
4262b8
 
4262b8
 int main(int argc, char *argv[]) {
4262b8
-        bool need_umount, need_swapoff, need_loop_detach, need_dm_detach;
4262b8
+        bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, need_md_detach;
4262b8
         bool in_container, use_watchdog = false, can_initrd;
4262b8
         _cleanup_free_ char *cgroup = NULL;
4262b8
         char *arguments[3];
4262b8
@@ -331,6 +331,7 @@ int main(int argc, char *argv[]) {
4262b8
         need_swapoff = !in_container;
4262b8
         need_loop_detach = !in_container;
4262b8
         need_dm_detach = !in_container;
4262b8
+        need_md_detach = !in_container;
4262b8
         can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0;
4262b8
 
4262b8
         /* Unmount all mountpoints, swaps, and loopback devices */
4262b8
@@ -383,6 +384,18 @@ int main(int argc, char *argv[]) {
4262b8
                                 log_error_errno(r, "Failed to detach loop devices: %m");
4262b8
                 }
4262b8
 
4262b8
+                if (need_md_detach) {
4262b8
+                        log_info("Stopping MD devices.");
4262b8
+                        r = md_detach_all(&changed, umount_log_level);
4262b8
+                        if (r == 0) {
4262b8
+                                need_md_detach = false;
4262b8
+                                log_info("All MD devices stopped.");
4262b8
+                        } else if (r > 0)
4262b8
+                                log_info("Not all MD devices stopped, %d left.", r);
4262b8
+                        else
4262b8
+                                log_error_errno(r, "Failed to stop MD devices: %m");
4262b8
+                }
4262b8
+
4262b8
                 if (need_dm_detach) {
4262b8
                         log_info("Detaching DM devices.");
4262b8
                         r = dm_detach_all(&changed, umount_log_level);
4262b8
@@ -395,8 +408,9 @@ int main(int argc, char *argv[]) {
4262b8
                                 log_error_errno(r, "Failed to detach DM devices: %m");
4262b8
                 }
4262b8
 
4262b8
-                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) {
4262b8
-                        log_info("All filesystems, swaps, loop devices and DM devices detached.");
4262b8
+                if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach
4262b8
+                            && !need_md_detach) {
4262b8
+                        log_info("All filesystems, swaps, loop devices, MD devices and DM devices detached.");
4262b8
                         /* Yay, done */
4262b8
                         break;
4262b8
                 }
4262b8
@@ -414,19 +428,21 @@ int main(int argc, char *argv[]) {
4262b8
                 /* If in this iteration we didn't manage to
4262b8
                  * unmount/deactivate anything, we simply give up */
4262b8
                 if (!changed) {
4262b8
-                        log_info("Cannot finalize remaining%s%s%s%s continuing.",
4262b8
+                        log_info("Cannot finalize remaining%s%s%s%s%s continuing.",
4262b8
                                  need_umount ? " file systems," : "",
4262b8
                                  need_swapoff ? " swap devices," : "",
4262b8
                                  need_loop_detach ? " loop devices," : "",
4262b8
-                                 need_dm_detach ? " DM devices," : "");
4262b8
+                                 need_dm_detach ? " DM devices," : "",
4262b8
+                                 need_md_detach ? " MD devices," : "");
4262b8
                         break;
4262b8
                 }
4262b8
 
4262b8
-                log_debug("Couldn't finalize remaining %s%s%s%s trying again.",
4262b8
+                log_debug("Couldn't finalize remaining %s%s%s%s%s trying again.",
4262b8
                           need_umount ? " file systems," : "",
4262b8
                           need_swapoff ? " swap devices," : "",
4262b8
                           need_loop_detach ? " loop devices," : "",
4262b8
-                          need_dm_detach ? " DM devices," : "");
4262b8
+                          need_dm_detach ? " DM devices," : "",
4262b8
+                          need_md_detach ? " MD devices," : "");
4262b8
         }
4262b8
 
4262b8
         /* We're done with the watchdog. */
4262b8
@@ -455,12 +471,13 @@ int main(int argc, char *argv[]) {
4262b8
 
4262b8
         }
4262b8
 
4262b8
-        if (need_umount || need_swapoff || need_loop_detach || need_dm_detach)
4262b8
-                log_error("Failed to finalize %s%s%s%s ignoring",
4262b8
+        if (need_umount || need_swapoff || need_loop_detach || need_dm_detach || need_md_detach)
4262b8
+                log_error("Failed to finalize%s%s%s%s%s ignoring.",
4262b8
                           need_umount ? " file systems," : "",
4262b8
                           need_swapoff ? " swap devices," : "",
4262b8
                           need_loop_detach ? " loop devices," : "",
4262b8
-                          need_dm_detach ? " DM devices," : "");
4262b8
+                          need_dm_detach ? " DM devices," : "",
4262b8
+                          need_md_detach ? " MD devices," : "");
4262b8
 
4262b8
         /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be
4262b8
          * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we
4262b8
diff --git a/src/core/umount.c b/src/core/umount.c
4262b8
index 3f02bf141a..ed90c6b1fc 100644
4262b8
--- a/src/core/umount.c
4262b8
+++ b/src/core/umount.c
4262b8
@@ -5,6 +5,8 @@
4262b8
 
4262b8
 #include <errno.h>
4262b8
 #include <fcntl.h>
4262b8
+#include <linux/major.h>
4262b8
+#include <linux/raid/md_u.h>
4262b8
 #include <linux/loop.h>
4262b8
 #include <string.h>
4262b8
 #include <sys/mount.h>
4262b8
@@ -332,6 +334,66 @@ static int dm_list_get(MountPoint **head) {
4262b8
         return 0;
4262b8
 }
4262b8
 
4262b8
+static int md_list_get(MountPoint **head) {
4262b8
+        _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
4262b8
+        struct udev_list_entry *item = NULL, *first = NULL;
4262b8
+        _cleanup_(udev_unrefp) struct udev *udev = NULL;
4262b8
+        int r;
4262b8
+
4262b8
+        assert(head);
4262b8
+
4262b8
+        udev = udev_new();
4262b8
+        if (!udev)
4262b8
+                return -ENOMEM;
4262b8
+
4262b8
+        e = udev_enumerate_new(udev);
4262b8
+        if (!e)
4262b8
+                return -ENOMEM;
4262b8
+
4262b8
+        r = udev_enumerate_add_match_subsystem(e, "block");
4262b8
+        if (r < 0)
4262b8
+                return r;
4262b8
+
4262b8
+        r = udev_enumerate_add_match_sysname(e, "md*");
4262b8
+        if (r < 0)
4262b8
+                return r;
4262b8
+
4262b8
+        first = udev_enumerate_get_list_entry(e);
4262b8
+        udev_list_entry_foreach(item, first) {
4262b8
+                _cleanup_(udev_device_unrefp) struct udev_device *d;
4262b8
+                _cleanup_free_ char *p = NULL;
4262b8
+                const char *dn;
4262b8
+                MountPoint *m;
4262b8
+                dev_t devnum;
4262b8
+
4262b8
+                d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
4262b8
+                if (!d)
4262b8
+                        return -ENOMEM;
4262b8
+
4262b8
+                devnum = udev_device_get_devnum(d);
4262b8
+                dn = udev_device_get_devnode(d);
4262b8
+                if (major(devnum) == 0 || !dn)
4262b8
+                        continue;
4262b8
+
4262b8
+                p = strdup(dn);
4262b8
+                if (!p)
4262b8
+                        return -ENOMEM;
4262b8
+
4262b8
+                m = new(MountPoint, 1);
4262b8
+                if (!m)
4262b8
+                        return -ENOMEM;
4262b8
+
4262b8
+                *m = (MountPoint) {
4262b8
+                        .path = TAKE_PTR(p),
4262b8
+                        .devnum = devnum,
4262b8
+                };
4262b8
+
4262b8
+                LIST_PREPEND(mount_point, *head, m);
4262b8
+        }
4262b8
+
4262b8
+        return 0;
4262b8
+}
4262b8
+
4262b8
 static int delete_loopback(const char *device) {
4262b8
         _cleanup_close_ int fd = -1;
4262b8
         int r;
4262b8
@@ -379,6 +441,23 @@ static int delete_dm(dev_t devnum) {
4262b8
         return 0;
4262b8
 }
4262b8
 
4262b8
+static int delete_md(MountPoint *m) {
4262b8
+
4262b8
+        _cleanup_close_ int fd = -1;
4262b8
+
4262b8
+        assert(major(m->devnum) != 0);
4262b8
+        assert(m->path != 0);
4262b8
+
4262b8
+        fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL);
4262b8
+        if (fd < 0)
4262b8
+                return -errno;
4262b8
+
4262b8
+        if (ioctl(fd, STOP_ARRAY, NULL) < 0)
4262b8
+                return -errno;
4262b8
+
4262b8
+        return 0;
4262b8
+}
4262b8
+
4262b8
 static bool nonunmountable_path(const char *path) {
4262b8
         return path_equal(path, "/")
4262b8
 #if ! HAVE_SPLIT_USR
4262b8
@@ -618,6 +697,37 @@ static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_lo
4262b8
         return n_failed;
4262b8
 }
4262b8
 
4262b8
+static int md_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
4262b8
+        MountPoint *m, *n;
4262b8
+        int n_failed = 0, r;
4262b8
+        dev_t rootdev = 0;
4262b8
+
4262b8
+        assert(head);
4262b8
+        assert(changed);
4262b8
+
4262b8
+        (void) get_block_device("/", &rootdev);
4262b8
+
4262b8
+        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
4262b8
+                if (major(rootdev) != 0 && rootdev == m->devnum) {
4262b8
+                        n_failed ++;
4262b8
+                        continue;
4262b8
+                }
4262b8
+
4262b8
+                log_info("Stopping MD %s (%u:%u).", m->path, major(m->devnum), minor(m->devnum));
4262b8
+                r = delete_md(m);
4262b8
+                if (r < 0) {
4262b8
+                        log_full_errno(umount_log_level, r, "Could not stop MD %s: %m", m->path);
4262b8
+                        n_failed++;
4262b8
+                        continue;
4262b8
+                }
4262b8
+
4262b8
+                *changed = true;
4262b8
+                mount_point_free(head, m);
4262b8
+        }
4262b8
+
4262b8
+        return n_failed;
4262b8
+}
4262b8
+
4262b8
 static int umount_all_once(bool *changed, int umount_log_level) {
4262b8
         int r;
4262b8
         _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
4262b8
@@ -696,3 +806,18 @@ int dm_detach_all(bool *changed, int umount_log_level) {
4262b8
 
4262b8
         return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
4262b8
 }
4262b8
+
4262b8
+int md_detach_all(bool *changed, int umount_log_level) {
4262b8
+        _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, md_list_head);
4262b8
+        int r;
4262b8
+
4262b8
+        assert(changed);
4262b8
+
4262b8
+        LIST_HEAD_INIT(md_list_head);
4262b8
+
4262b8
+        r = md_list_get(&md_list_head);
4262b8
+        if (r < 0)
4262b8
+                return r;
4262b8
+
4262b8
+        return md_points_list_detach(&md_list_head, changed, umount_log_level);
4262b8
+}
4262b8
diff --git a/src/core/umount.h b/src/core/umount.h
4262b8
index 6f2b24d195..b01062484f 100644
4262b8
--- a/src/core/umount.h
4262b8
+++ b/src/core/umount.h
4262b8
@@ -15,6 +15,8 @@ int loopback_detach_all(bool *changed, int umount_log_level);
4262b8
 
4262b8
 int dm_detach_all(bool *changed, int umount_log_level);
4262b8
 
4262b8
+int md_detach_all(bool *changed, int umount_log_level);
4262b8
+
4262b8
 /* This is exported just for testing */
4262b8
 typedef struct MountPoint {
4262b8
         char *path;