shared/hibernate-util: handle the case where no swap has available backing dev (#35802)
Fixes https://github.com/systemd/systemd/issues/35798 Replaces https://github.com/systemd/systemd/pull/35801
This commit is contained in:
@@ -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.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
|
||||
≥ 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
+25
-40
@@ -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);
|
||||
@@ -235,6 +230,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)
|
||||
@@ -357,26 +354,25 @@ 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);
|
||||
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 &&
|
||||
(!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;
|
||||
}
|
||||
@@ -392,9 +388,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) {
|
||||
@@ -493,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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user