From 805deec039e8cfe8d4dedef8d02691aebf88b1bc Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Mon, 16 Oct 2023 13:10:01 +0800 Subject: [PATCH] hibernate-util: introduce hibernation_is_safe After 7470b80763ac0f598ca1ef73d44763967119c18d, we refuse to hibernate if we fail to write HibernateLocation EFI variable and resume= is not set. Let's teach sleep_supported to follow the practice too. --- src/login/logind-dbus.c | 4 +++ src/shared/hibernate-util.c | 49 +++++++++++++++++++++++++------------ src/shared/hibernate-util.h | 2 +- src/shared/sleep-config.c | 15 +++++++++--- src/shared/sleep-config.h | 1 + 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index e17d48bf262..f45a944f17e 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1944,6 +1944,10 @@ static int method_do_shutdown_or_sleep( "Sleep verb '%s' is not configured or configuration is not supported by kernel", sleep_operation_to_string(a->sleep_operation)); + case SLEEP_RESUME_NOT_SUPPORTED: + return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, + "Not running on EFI and resume= is not set. No available method to resume from hibernation"); + 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 ba5e5fe857a..4a05320a40e 100644 --- a/src/shared/hibernate-util.c +++ b/src/shared/hibernate-util.c @@ -14,6 +14,7 @@ #include "btrfs-util.h" #include "device-util.h" #include "devnum-util.h" +#include "efivars.h" #include "env-util.h" #include "errno-util.h" #include "fd-util.h" @@ -402,36 +403,52 @@ int find_suitable_hibernation_device_full(HibernationDevice *ret_device, uint64_ return resume_config_devno > 0; } -bool enough_swap_for_hibernation(void) { +static int get_proc_meminfo_active(unsigned long long *ret) { _cleanup_free_ char *active_str = NULL; unsigned long long active; - uint64_t size, used; int r; - if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) - return true; + r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active_str); + if (r < 0) + return log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m"); + + r = safe_atollu(active_str, &active); + if (r < 0) + return log_debug_errno(r, "Failed to parse Active(anon) '%s' from /proc/meminfo: %m", active_str); + + *ret = active; + return 0; +} + +int hibernation_is_safe(void) { + unsigned long long active; + uint64_t size, used; + bool resume_set; + int r; r = find_suitable_hibernation_device_full(NULL, &size, &used); if (r < 0) - return false; + return r; + resume_set = r > 0; - r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active_str); - if (r < 0) { - log_debug_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m"); - return false; - } + if (!resume_set && !is_efi_boot()) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Not running on EFI and resume= is not set. Hibernation is not safe."); - r = safe_atollu(active_str, &active); - if (r < 0) { - log_debug_errno(r, "Failed to parse Active(anon) '%s' from /proc/meminfo: %m", active_str); - return false; - } + if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) + return true; + + r = get_proc_meminfo_active(&active); + if (r < 0) + return r; r = active <= (size - used) * HIBERNATION_SWAP_THRESHOLD; log_debug("Detected %s swap for hibernation: Active(anon)=%llu kB, size=%" PRIu64 " kB, used=%" PRIu64 " kB, threshold=%.2g%%", r ? "enough" : "not enough", active, size, used, 100 * HIBERNATION_SWAP_THRESHOLD); + if (!r) + return -ENOSPC; - return r; + return resume_set; } int write_resume_config(dev_t devno, uint64_t offset, const char *device) { diff --git a/src/shared/hibernate-util.h b/src/shared/hibernate-util.h index 8480026ce18..2ae10fb1001 100644 --- a/src/shared/hibernate-util.h +++ b/src/shared/hibernate-util.h @@ -18,7 +18,7 @@ static inline int find_suitable_hibernation_device(HibernationDevice *ret) { return find_suitable_hibernation_device_full(ASSERT_PTR(ret), NULL, NULL); } -bool enough_swap_for_hibernation(void); +int hibernation_is_safe(void); int write_resume_config(dev_t devno, uint64_t offset, const char *device); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index e5e96041ed0..deba2b1c588 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -278,9 +278,18 @@ static int sleep_supported_internal( return false; } - if (IN_SET(operation, SLEEP_HIBERNATE, SLEEP_HYBRID_SLEEP) && !enough_swap_for_hibernation()) { - *ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE; - return false; + if (IN_SET(operation, SLEEP_HIBERNATE, SLEEP_HYBRID_SLEEP)) { + r = hibernation_is_safe(); + if (r == -ENOTRECOVERABLE) { + *ret_support = SLEEP_RESUME_NOT_SUPPORTED; + return false; + } + if (r == -ENOSPC) { + *ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE; + return false; + } + if (r < 0) + return r; } *ret_support = SLEEP_SUPPORTED; diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 756b0e13ae2..bc10a5b39bb 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -40,6 +40,7 @@ typedef enum SleepSupport { SLEEP_DISABLED, /* Disabled in SleepConfig.allow */ 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_NOT_ENOUGH_SWAP_SPACE, SLEEP_ALARM_NOT_SUPPORTED, /* CLOCK_BOOTTIME_ALARM is unsupported by kernel (only used by s2h) */ } SleepSupport; -- 2.47.3