From 40eb83a8fe535897b17760988849fa061a8f4929 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 May 2024 12:42:40 +0800 Subject: [PATCH 1/3] hibernate-util,logind: emit a clear error if the specified resume dev is missing Currently, SLEEP_NOT_ENOUGH_SWAP_SPACE (ENOSPC) is returned on all sorts of error conditions. But one important case that's worth differentiating from that is when the resume device is manually specified yet missing. Closes #32644 --- src/login/logind-dbus.c | 4 ++++ src/shared/hibernate-util.c | 12 ++++++------ src/shared/sleep-config.c | 18 +++++++++++++----- src/shared/sleep-config.h | 1 + 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 38bfaa56be..ef8cad0a31 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2156,6 +2156,10 @@ static int method_do_shutdown_or_sleep( return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not running on EFI and resume= is not set, or noresume is set. No available method to resume from hibernation"); + case SLEEP_RESUME_DEVICE_MISSING: + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "Specified resume device is missing or is not an active swap device"); + case SLEEP_NOT_ENOUGH_SWAP_SPACE: return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough suitable swap space for hibernation available on compatible block devices and file systems"); diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c index cb26a9ab27..ce49344deb 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -394,7 +394,7 @@ 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(ENOSPC), "Cannot find swap entry corresponding to /sys/power/resume."); + return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find swap entry corresponding to /sys/power/resume."); } if (ret_device) { @@ -452,11 +452,11 @@ int hibernation_is_safe(void) { bypass_space_check = getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0; r = find_suitable_hibernation_device_full(NULL, &size, &used); - if (r == -ENOSPC && bypass_space_check) - /* If we don't have any available swap space at all, and SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK - * is set, skip all remaining checks since we can't do that properly anyway. It is quite - * possible that the user is using a setup similar to #30083. When we actually perform - * hibernation in sleep.c we'll check everything again. */ + if (IN_SET(r, -ENOSPC, -ESTALE) && bypass_space_check) + /* If we don't have any available swap space at all, or the specified resume device is missing, + * and $SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK is set, skip all remaining checks since + * we can't do that properly anyway. It is quite possible that the user is using a setup + * similar to #30083. When we actually perform hibernation in sleep.c we'll check everything again. */ return 0; if (r < 0) return r; diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index aac145836b..0a4303f3eb 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -368,16 +368,24 @@ static int sleep_supported_internal( } r = hibernation_is_safe(); - if (r == -ENOTRECOVERABLE) { + switch (r) { + + case -ENOTRECOVERABLE: *ret_support = SLEEP_RESUME_NOT_SUPPORTED; return false; - } - if (r == -ENOSPC) { + + case -ESTALE: + *ret_support = SLEEP_RESUME_DEVICE_MISSING; + return false; + + case -ENOSPC: *ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE; return false; + + default: + if (r < 0) + return r; } - if (r < 0) - return r; } else assert(!sleep_config->modes[operation]); diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 75d9c4a622..be0287e2e4 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -59,6 +59,7 @@ typedef enum SleepSupport { SLEEP_NOT_CONFIGURED, /* SleepConfig.states is not configured */ SLEEP_STATE_OR_MODE_NOT_SUPPORTED, /* SleepConfig.states/modes are not supported by kernel */ SLEEP_RESUME_NOT_SUPPORTED, + SLEEP_RESUME_DEVICE_MISSING, /* resume= is specified, but the device cannot be found in /proc/swaps */ SLEEP_NOT_ENOUGH_SWAP_SPACE, SLEEP_ALARM_NOT_SUPPORTED, /* CLOCK_BOOTTIME_ALARM is unsupported by kernel (only used by s2h) */ } SleepSupport; From 3fce141c1bf9a949cdcd81fc77f4b3dc8aca98f0 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 May 2024 12:52:35 +0800 Subject: [PATCH 2/3] hibernate-util,logind: also differentiate the case of misconfigured resume --- src/login/logind-dbus.c | 4 ++++ src/shared/hibernate-util.c | 2 +- src/shared/sleep-config.c | 4 ++++ src/shared/sleep-config.h | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ef8cad0a31..53e966562e 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2160,6 +2160,10 @@ static int method_do_shutdown_or_sleep( return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Specified resume device is missing or is not an active swap device"); + case SLEEP_RESUME_MISCONFIGURED: + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "Invalid resume config: resume= is not populated yet resume_offset= is"); + case SLEEP_NOT_ENOUGH_SWAP_SPACE: return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough suitable swap space for hibernation available on compatible block devices and file systems"); diff --git a/src/shared/hibernate-util.c b/src/shared/hibernate-util.c index ce49344deb..7c21157580 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -159,7 +159,7 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) { } if (devno == 0 && offset > 0 && offset != UINT64_MAX) - return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM), "Found populated /sys/power/resume_offset (%" PRIu64 ") but /sys/power/resume is not set, refusing.", offset); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 0a4303f3eb..3d4d331710 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -378,6 +378,10 @@ static int sleep_supported_internal( *ret_support = SLEEP_RESUME_DEVICE_MISSING; return false; + case -ENOMEDIUM: + *ret_support = SLEEP_RESUME_MISCONFIGURED; + return false; + case -ENOSPC: *ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE; return false; diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index be0287e2e4..b59bce8fc4 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -60,6 +60,7 @@ typedef enum SleepSupport { SLEEP_STATE_OR_MODE_NOT_SUPPORTED, /* SleepConfig.states/modes are not supported by kernel */ SLEEP_RESUME_NOT_SUPPORTED, SLEEP_RESUME_DEVICE_MISSING, /* resume= is specified, but the device cannot be found in /proc/swaps */ + SLEEP_RESUME_MISCONFIGURED, /* resume= is not set yet resume_offset= is configured */ SLEEP_NOT_ENOUGH_SWAP_SPACE, SLEEP_ALARM_NOT_SUPPORTED, /* CLOCK_BOOTTIME_ALARM is unsupported by kernel (only used by s2h) */ } SleepSupport; From 4f344de792b2e592bf9725a23f6821a3ed46f550 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 8 May 2024 13:41:05 +0800 Subject: [PATCH 3/3] systemctl: do not fall back to StartUnit automatically for sleep operations In the majority of cases, this is caused by sleep_supported() returning error. Hence it's very likely that it would fail again, so the fallback is not really useful. Instead, honor the --force option for these verbs. --- man/systemctl.xml | 51 ++++++++++++++++--------- src/systemctl/systemctl-start-special.c | 5 ++- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 287decffb2..def2f8e011 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1843,6 +1843,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err Suspend the system. This will trigger activation of the special target unit suspend.target. This command is asynchronous, and will return after the suspend operation is successfully enqueued. It will not wait for the suspend/resume cycle to complete. + + If is specified, and systemd-logind returned + error for the operation, the error will be ignored and the operation will be tried again directly + through starting the target unit. @@ -1853,6 +1857,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err Hibernate the system. This will trigger activation of the special target unit hibernate.target. This command is asynchronous, and will return after the hibernation operation is successfully enqueued. It will not wait for the hibernate/thaw cycle to complete. + + This command honors in the same way as suspend. @@ -1864,6 +1870,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err hybrid-sleep.target. This command is asynchronous, and will return after the hybrid sleep operation is successfully enqueued. It will not wait for the sleep/wake-up cycle to complete. + This command honors in the same way as suspend. + @@ -1872,12 +1880,15 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err suspend-then-hibernate - Suspend the system and hibernate it after the delay specified in systemd-sleep.conf. - This will trigger activation of the special target unit suspend-then-hibernate.target. - This command is asynchronous, and will return after the hybrid sleep operation is successfully enqueued. + Suspend the system and hibernate it when the battery is low, or when the delay specified + in systemd-sleep.conf elapsed. This will trigger activation of the special + target unit suspend-then-hibernate.target. This command is asynchronous, + and will return after the hybrid sleep operation is successfully enqueued. It will not wait for the sleep/wake-up or hibernate/thaw cycle to complete. - + This command honors in the same way as suspend. + + @@ -2531,25 +2542,27 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - When used with enable, overwrite - any existing conflicting symlinks. + When used with enable, overwrite any existing conflicting symlinks. - When used with edit, create all of the - specified units which do not already exist. + When used with edit, create all of the specified units which do not already exist. - When used with halt, poweroff, reboot or - kexec, execute the selected operation without shutting down all units. However, all - processes will be killed forcibly and all file systems are unmounted or remounted read-only. This is hence a - drastic but relatively safe option to request an immediate reboot. If is specified - twice for these operations (with the exception of kexec), they will be executed - immediately, without terminating any processes or unmounting any file systems. + When used with suspend, hibernate, hybrid-sleep, + or suspend-then-hibernate, the error returned by systemd-logind + will be ignored, and the operation will be performed directly through starting the corresponding units. + + + When used with halt, poweroff, reboot, + or kexec, execute the selected operation without shutting down all units. However, + all processes will be killed forcibly and all file systems are unmounted or remounted read-only. + This is hence a drastic but relatively safe option to request an immediate reboot. If + is specified twice for these operations (with the exception of kexec), they will + be executed immediately, without terminating any processes or unmounting any file systems. - Specifying - twice with any of these operations might result in data loss. Note that when - is specified twice the selected operation is executed by - systemctl itself, and the system manager is not contacted. This means the command should - succeed even when the system manager has crashed. + Specifying twice with any of these operations might result in data loss. + Note that when is specified twice the selected operation is executed by + systemctl itself, and the system manager is not contacted. This means the command + should succeed even when the system manager has crashed. diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index 4b99d0c629..95cf00fc81 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -223,8 +223,11 @@ int verb_start_special(int argc, char *argv[], void *userdata) { case ACTION_HYBRID_SLEEP: case ACTION_SUSPEND_THEN_HIBERNATE: + /* For sleep operations, do not automatically fall back to low-level operation for + * errors other than logind not available. There's a high chance that logind did + * some extra sanity check and that didn't pass. */ r = logind_reboot(a); - if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) + if (r >= 0 || (r != -ENOSYS && arg_force == 0)) return r; arg_no_block = true;