hibernate-util: introduce hibernation_is_safe

After 7470b80763, 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.
This commit is contained in:
Mike Yuan
2023-10-16 13:10:01 +08:00
parent 596873c10c
commit 805deec039
5 changed files with 52 additions and 21 deletions

View File

@@ -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");

View File

@@ -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;
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 r;
resume_set = r > 0;
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.");
if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
return true;
r = find_suitable_hibernation_device_full(NULL, &size, &used);
r = get_proc_meminfo_active(&active);
if (r < 0)
return false;
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;
}
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;
}
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) {

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;