shutdown: handle gracefully if a device disappears while we detach it

Let's gracefully handle cases where a device disappears in the time we
between our discovery and when we want to detach it, due to "auto-clear"
or a similar logic.

The loopback case already handled this quite OK, do the same for MD and
swap too.

Switch to ERRNO_IS_DEVICE_ABSENT() for all checks, just in case.

Also improve debug logging for all these cases, so we know exactly what
is going on.

This is inspired by #37160, but shouldn't really fix anything there, I
am pretty sure the ENODEV seen in that output stems from the STOP_ARRAY
call, not from the open().

Note that this does not change anything for the device mapper case,
because the DM subsystem does not return useful error codes to
userspace, hence everything is a complete mess there.
This commit is contained in:
Lennart Poettering
2025-04-17 14:39:34 +02:00
committed by Yu Watanabe
parent 7baf24c949
commit 2791b2bc3d
3 changed files with 30 additions and 11 deletions

View File

@@ -17,6 +17,7 @@
#include "blockdev-util.h"
#include "detach-loopback.h"
#include "device-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "shutdown.h"
@@ -106,8 +107,12 @@ static int delete_loopback(const char *device) {
fd = open(device, O_RDONLY|O_CLOEXEC);
if (fd < 0) {
log_debug_errno(errno, "Failed to open loopback device %s: %m", device);
return errno == ENOENT ? 0 : -errno;
if (ERRNO_IS_DEVICE_ABSENT(errno)) {
log_debug_errno(errno, "Tried to open loopback device '%s', but device disappeared by now, ignoring: %m", device);
return 0;
}
return log_debug_errno(errno, "Failed to open loopback device '%s': %m", device);
}
/* Loopback block devices don't sync in-flight blocks when we clear the fd, hence sync explicitly

View File

@@ -131,12 +131,21 @@ static int delete_md(RaidDevice *m) {
assert(m->path);
fd = open(m->path, O_RDONLY|O_CLOEXEC|O_EXCL);
if (fd < 0)
return -errno;
if (fd < 0) {
if (ERRNO_IS_DEVICE_ABSENT(errno)) {
log_debug_errno(errno, "Tried to open MD device '%s', but device disappeared by now, ignoring: %m", m->path);
return 0;
}
return log_debug_errno(errno, "Failed to open MD device '%s': %m", m->path);
}
(void) sync_with_progress(fd);
return RET_NERRNO(ioctl(fd, STOP_ARRAY, NULL));
if (ioctl(fd, STOP_ARRAY, NULL) < 0)
return log_debug_errno(errno, "Failed to issue STOP_ARRAY on MD device '%s': %m", m->path);
return 1;
}
static int md_points_list_detach(RaidDevice **head, bool *changed, bool last_try) {
@@ -164,8 +173,9 @@ static int md_points_list_detach(RaidDevice **head, bool *changed, bool last_try
n_failed++;
continue;
}
if (r > 0)
*changed = true;
*changed = true;
raid_device_free(head, m);
}

View File

@@ -7,6 +7,7 @@
#include "alloc-util.h"
#include "detach-swap.h"
#include "errno-util.h"
#include "libmount-util.h"
static void swap_device_free(SwapDevice **head, SwapDevice *m) {
@@ -74,20 +75,23 @@ int swap_list_get(const char *swaps, SwapDevice **head) {
}
static int swap_points_list_off(SwapDevice **head, bool *changed) {
int n_failed = 0;
int n_failed = 0, r;
assert(head);
assert(changed);
LIST_FOREACH(swap_device, m, *head) {
log_info("Deactivating swap %s.", m->path);
if (swapoff(m->path) < 0) {
log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
r = RET_NERRNO(swapoff(m->path));
if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
log_debug_errno(r, "Tried to deactivate swap '%s', but swap disappeared by now, ignoring: %m", m->path);
else if (r < 0) {
log_warning_errno(r, "Could not deactivate swap %s: %m", m->path);
n_failed++;
continue;
}
} else
*changed = true;
*changed = true;
swap_device_free(head, m);
}