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