From 6d3b2273b1e14d79548a018674ad6e5a5b8b8009 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Thu, 2 Jan 2025 04:13:23 +0100 Subject: [PATCH 1/5] shared/hibernate-util: don't attempt to fiemap fd if no backing dev available Prompted by #35798 Co-authored-by: Yu Watanabe --- src/shared/hibernate-util.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c index 1213fdc2c7..49ce787d02 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -235,6 +235,8 @@ static int swap_entry_get_resume_config(SwapEntry *swap) { r = get_block_device_fd(fd, &swap->devno); if (r < 0) return r; + if (r == 0) + return -EMEDIUMTYPE; r = fd_is_fs_type(fd, BTRFS_SUPER_MAGIC); if (r < 0) @@ -362,14 +364,16 @@ int find_suitable_hibernation_device_full(HibernationDevice *ret_device, uint64_ FOREACH_ARRAY(swap, entries.swaps, entries.n_swaps) { r = swap_entry_get_resume_config(swap); - if (r < 0) - return log_debug_errno(r, "Failed to get devno and offset for swap '%s': %m", swap->path); - if (swap->devno == 0) { + if (r == -EMEDIUMTYPE) { assert(swap->swapfile); - log_debug("Swap file '%s' is not backed by block device, ignoring: %m", swap->path); + log_debug_errno(r, "Unable to acquire backing block device for swap file '%s' (maybe on a RAID btrfs?), ignoring.", + swap->path); continue; } + if (r < 0) + return log_debug_errno(r, "Failed to get devno and offset for swap '%s': %m", swap->path); + assert(swap->devno > 0); if (resume_config_devno > 0) { if (swap->devno == resume_config_devno && From bd3d361580715c7a6204c74df9185cc81d67f0c2 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Thu, 2 Jan 2025 04:17:47 +0100 Subject: [PATCH 2/5] shared/hibernate-util: handle the case where no swap has available backing dev This also makes find_suitable_hibernation_device() report more accurate error (ENOSPC -> ESTALE) if there's no swap space on the system at all but resume= is set. Fixes #35798 Replaces #35801 --- src/shared/hibernate-util.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c index 49ce787d02..25d742ed1f 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -359,8 +359,6 @@ int find_suitable_hibernation_device_full(HibernationDevice *ret_device, uint64_ r = read_swap_entries(&entries); if (r < 0) return r; - if (entries.n_swaps == 0) - return log_debug_errno(SYNTHETIC_ERRNO(ENOSPC), "No swap space available for hibernation."); FOREACH_ARRAY(swap, entries.swaps, entries.n_swaps) { r = swap_entry_get_resume_config(swap); @@ -396,9 +394,10 @@ int find_suitable_hibernation_device_full(HibernationDevice *ret_device, uint64_ } if (!entry) { - /* No need to check n_swaps == 0, since it's rejected early */ - assert(resume_config_devno > 0); - return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find swap entry corresponding to /sys/power/resume."); + if (resume_config_devno > 0) + return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find swap entry corresponding to /sys/power/resume."); + + return log_debug_errno(SYNTHETIC_ERRNO(ENOSPC), "No swap space available for hibernation."); } if (ret_device) { From 8b0818fa455314cf07f3b556f9f45b4f21fd6997 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Thu, 2 Jan 2025 04:22:46 +0100 Subject: [PATCH 3/5] shared/blockdev-util: shorten the code a bit If btrfs_get_block_device_fd() returns 0, ret is already initialized. --- src/shared/blockdev-util.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c index 53d60bd0dd..57dfe3a19c 100644 --- a/src/shared/blockdev-util.c +++ b/src/shared/blockdev-util.c @@ -263,9 +263,7 @@ int get_block_device_fd(int fd, dev_t *ret) { } r = btrfs_get_block_device_fd(fd, ret); - if (r > 0) - return 1; - if (r != -ENOTTY) /* not btrfs */ + if (r != -ENOTTY) /* ENOTTY: not btrfs */ return r; *ret = 0; From 733bc1aee8bc9d3f0317b4b17864e61b18726c73 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Thu, 2 Jan 2025 15:44:59 +0100 Subject: [PATCH 4/5] README: document a bunch of new kernel APIs we utilize --- README | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README b/README index a0818bff2b..ffe70b80c0 100644 --- a/README +++ b/README @@ -34,12 +34,19 @@ REQUIREMENTS: ≥ 4.6 for cgroup namespaces ≥ 4.9 for RENAME_NOREPLACE support in vfat ≥ 4.10 for cgroup-bpf egress and ingress hooks + ≥ 4.11 for nsfs ≥ 4.15 for cgroup-bpf device hook and cpu controller in cgroup v2 ≥ 4.17 for cgroup-bpf socket address hooks ≥ 4.20 for PSI (used by systemd-oomd) + ≥ 5.2 for cgroup freezer ≥ 5.3 for bounded loops in BPF program - ≥ 5.4 for pidfd and signed Verity images + ≥ 5.4 for pidfd, new mount API, and signed Verity images ≥ 5.7 for CLONE_INTO_CGROUP, BPF links and the BPF LSM hook + ≥ 5.8 for LOOP_CONFIGURE and STATX_ATTR_MOUNT_ROOT + ≥ 6.3 for MFD_EXEC/MFD_NOEXEC_SEAL and tmpfs noswap option + ≥ 6.5 for name_to_handle_at() AT_HANDLE_FID, SO_PEERPIDFD/SO_PASSPIDFD, + and MOVE_MOUNT_BENEATH + ≥ 6.9 for pidfs ⛔ Kernel versions below 4.3 ("minimum baseline") are not supported at all, and are missing required functionality (e.g. CLOCK_BOOTTIME From de69879b62d8c95d2f7b84a950d1b9cd783101e9 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Thu, 2 Jan 2025 04:32:00 +0100 Subject: [PATCH 5/5] shared/hibernate-util: drop support for kernels lacking /sys/power/resume_offset The current fallback path is actually unreliable, given the kernel *supports* setting the resume offset through cmdline after all, but just not exposed under /sys/. For v258 let's drop it hence. I didn't bump the baseline to 4.17, but merely documented new requirement in README, because there's certainly more compat stuff to drop between 4.3 and 4.17, and README is a useful list for things to kill. We'll get to 5.4 eventually. --- README | 2 +- src/shared/hibernate-util.c | 44 +++++++++++-------------------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/README b/README index ffe70b80c0..23bf094f78 100644 --- a/README +++ b/README @@ -36,7 +36,7 @@ REQUIREMENTS: ≥ 4.10 for cgroup-bpf egress and ingress hooks ≥ 4.11 for nsfs ≥ 4.15 for cgroup-bpf device hook and cpu controller in cgroup v2 - ≥ 4.17 for cgroup-bpf socket address hooks + ≥ 4.17 for cgroup-bpf socket address hooks and /sys/power/resume_offset ≥ 4.20 for PSI (used by systemd-oomd) ≥ 5.2 for cgroup freezer ≥ 5.3 for bounded loops in BPF program diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c index 25d742ed1f..658128d8bd 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -146,19 +146,14 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) { return log_debug_errno(r, "Failed to parse /sys/power/resume devno '%s': %m", devno_str); r = read_one_line_file("/sys/power/resume_offset", &offset_str); - if (r == -ENOENT) { - log_debug_errno(r, "Kernel does not expose resume_offset, skipping."); - offset = UINT64_MAX; - } else if (r < 0) + if (r < 0) return log_debug_errno(r, "Failed to read /sys/power/resume_offset: %m"); - else { - r = safe_atou64(offset_str, &offset); - if (r < 0) - return log_debug_errno(r, - "Failed to parse /sys/power/resume_offset '%s': %m", offset_str); - } - if (devno == 0 && offset > 0 && offset != UINT64_MAX) + r = safe_atou64(offset_str, &offset); + if (r < 0) + return log_debug_errno(r, "Failed to parse /sys/power/resume_offset '%s': %m", offset_str); + + if (devno == 0 && offset > 0) return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM), "Found populated /sys/power/resume_offset (%" PRIu64 ") but /sys/power/resume is not set, refusing.", offset); @@ -375,10 +370,9 @@ int find_suitable_hibernation_device_full(HibernationDevice *ret_device, uint64_ if (resume_config_devno > 0) { if (swap->devno == resume_config_devno && - (!swap->swapfile || resume_config_offset == UINT64_MAX || swap->offset == resume_config_offset)) { + (!swap->swapfile || swap->offset == resume_config_offset)) { /* /sys/power/resume (resume=) is set, and the calculated swap file offset - * matches with /sys/power/resume_offset. If /sys/power/resume_offset is not - * exposed, we can't do proper check anyway, so use the found swap file too. */ + * matches with /sys/power/resume_offset. */ entry = swap; break; } @@ -496,28 +490,16 @@ int write_resume_config(dev_t devno, uint64_t offset, const char *device) { devno_str = FORMAT_DEVNUM(devno); xsprintf(offset_str, "%" PRIu64, offset); - /* We write the offset first since it's safer. Note that this file is only available in 4.17+, so - * fail gracefully if it doesn't exist and we're only overwriting it with 0. */ r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER); - if (r == -ENOENT) { - if (offset != 0) - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "Can't configure swap file offset %s, kernel does not support /sys/power/resume_offset. Refusing.", - offset_str); - - log_warning_errno(r, "/sys/power/resume_offset is unavailable, skipping writing swap file offset."); - } else if (r < 0) - return log_error_errno(r, - "Failed to write swap file offset %s to /sys/power/resume_offset for device '%s': %m", + if (r < 0) + return log_error_errno(r, "Failed to write swap file offset %s to /sys/power/resume_offset for device '%s': %m", offset_str, device); - else - log_debug("Wrote resume_offset=%s for device '%s' to /sys/power/resume_offset.", - offset_str, device); + log_debug("Wrote resume_offset=%s for device '%s' to /sys/power/resume_offset.", + offset_str, device); r = write_string_file("/sys/power/resume", devno_str, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) - return log_error_errno(r, - "Failed to write device '%s' (%s) to /sys/power/resume: %m", + return log_error_errno(r, "Failed to write device '%s' (%s) to /sys/power/resume: %m", device, devno_str); log_debug("Wrote resume=%s for device '%s' to /sys/power/resume.", devno_str, device);