"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");
#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"
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) {
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;
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;