diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 06c9710c6e..83c4bf4b3d 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -308,7 +308,7 @@ static void bump_sysctl_printk_log_level(int min_level) { } int main(int argc, char *argv[]) { - bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, in_container, use_watchdog = false, can_initrd; + bool need_umount, need_swapoff, need_loop_detach, need_dm_detach, need_md_detach, in_container, use_watchdog = false, can_initrd; _cleanup_free_ char *cgroup = NULL; char *arguments[3], *watchdog_device; int cmd, r, umount_log_level = LOG_INFO; @@ -399,6 +399,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 */ @@ -451,6 +452,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); @@ -463,8 +476,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; } @@ -482,19 +496,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. */ @@ -524,12 +540,13 @@ int main(int argc, char *argv[]) { log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); } - 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/shutdown/umount.c b/src/shutdown/umount.c index 8a5e80eeaa..0eb694c1af 100644 --- a/src/shutdown/umount.c +++ b/src/shutdown/umount.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -326,6 +328,58 @@ static int dm_list_get(MountPoint **head) { return 0; } +static int md_list_get(MountPoint **head) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d; + int r; + + assert(head); + + r = sd_device_enumerator_new(&e); + if (r < 0) + return r; + + r = sd_device_enumerator_allow_uninitialized(e); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_subsystem(e, "block", true); + if (r < 0) + return r; + + r = sd_device_enumerator_add_match_sysname(e, "md*"); + if (r < 0) + return r; + + FOREACH_DEVICE(e, d) { + _cleanup_free_ char *p = NULL; + const char *dn; + MountPoint *m; + dev_t devnum; + + if (sd_device_get_devnum(d, &devnum) < 0 || + sd_device_get_devname(d, &dn) < 0) + 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; struct loop_info64 info; @@ -412,6 +466,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 @@ -635,6 +706,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) { _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); int r; @@ -713,3 +815,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/shutdown/umount.h b/src/shutdown/umount.h index 6f2b24d195..b01062484f 100644 --- a/src/shutdown/umount.h +++ b/src/shutdown/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;