diff options
author | Mario Limonciello <superm1@gmail.com> | 2018-03-08 21:17:33 +0800 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2018-03-08 14:17:33 +0100 |
commit | c58493c00af97146d3b6c24da9c0371978124703 (patch) | |
tree | eef8dce2e68194b33df3faec0631bee8543a175b /src/sleep | |
parent | fc17f194ded93b51899aa0bbd4e66870d975fe5a (diff) | |
download | systemd-c58493c00af97146d3b6c24da9c0371978124703.tar.gz |
Introduce suspend-to-hibernate (#8274)
Suspend to Hibernate is a new sleep method that invokes suspend
for a predefined period of time before automatically waking up
and hibernating the system.
It's similar to HybridSleep however there isn't a performance
impact on every suspend cycle.
It's intended to use with systems that may have a higher power
drain in their supported suspend states to prevent battery and
data loss over an extended suspend cycle.
Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
Diffstat (limited to 'src/sleep')
-rw-r--r-- | src/sleep/sleep.c | 94 |
1 files changed, 90 insertions, 4 deletions
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 518032ec69..48e7c386f3 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -4,6 +4,7 @@ Copyright 2012 Lennart Poettering Copyright 2013 Zbigniew Jędrzejewski-Szmek + Copyright 2018 Dell Inc. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -25,12 +26,14 @@ #include "sd-messages.h" +#include "parse-util.h" #include "def.h" #include "exec-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" #include "sleep-config.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "util.h" @@ -135,6 +138,83 @@ static int execute(char **modes, char **states) { return r; } +static int read_wakealarm(uint64_t *result) { + _cleanup_free_ char *t = NULL; + + if (read_one_line_file("/sys/class/rtc/rtc0/since_epoch", &t) >= 0) + return safe_atou64(t, result); + return -EBADF; +} + +static int write_wakealarm(const char *str) { + + _cleanup_fclose_ FILE *f = NULL; + int r; + + f = fopen("/sys/class/rtc/rtc0/wakealarm", "we"); + if (!f) + return log_error_errno(errno, "Failed to open /sys/class/rtc/rtc0/wakealarm: %m"); + + r = write_string_stream(f, str, 0); + if (r < 0) + return log_error_errno(r, "Failed to write '%s' to /sys/class/rtc/rtc0/wakealarm: %m", str); + + return 0; +} + +static int execute_s2h(usec_t hibernate_delay_sec) { + + _cleanup_strv_free_ char **hibernate_modes = NULL, **hibernate_states = NULL, + **suspend_modes = NULL, **suspend_states = NULL; + usec_t orig_time, cmp_time; + char time_str[DECIMAL_STR_MAX(uint64_t)]; + int r; + + r = parse_sleep_config("suspend", &suspend_modes, &suspend_states, + NULL); + if (r < 0) + return r; + + r = parse_sleep_config("hibernate", &hibernate_modes, + &hibernate_states, NULL); + if (r < 0) + return r; + + r = read_wakealarm(&orig_time); + if (r < 0) + return log_error_errno(errno, "Failed to read time: %d", r); + + orig_time += hibernate_delay_sec / USEC_PER_SEC; + xsprintf(time_str, "%" PRIu64, orig_time); + + r = write_wakealarm(time_str); + if (r < 0) + return r; + + log_debug("Set RTC wake alarm for %s", time_str); + + r = execute(suspend_modes, suspend_states); + if (r < 0) + return r; + + r = read_wakealarm(&cmp_time); + if (r < 0) + return log_error_errno(errno, "Failed to read time: %d", r); + + /* reset RTC */ + r = write_wakealarm("0"); + if (r < 0) + return r; + + log_debug("Woke up at %"PRIu64, cmp_time); + + /* if woken up after alarm time, hibernate */ + if (cmp_time >= orig_time) + r = execute(hibernate_modes, hibernate_states); + + return r; +} + static void help(void) { printf("%s COMMAND\n\n" "Suspend the system, hibernate the system, or both.\n\n" @@ -144,6 +224,8 @@ static void help(void) { " suspend Suspend the system\n" " hibernate Hibernate the system\n" " hybrid-sleep Both hibernate and suspend the system\n" + " suspend-to-hibernate Initially suspend and then hibernate\n" + " the system after a fixed period of time\n" , program_invocation_short_name); } @@ -189,7 +271,8 @@ static int parse_argv(int argc, char *argv[]) { if (!streq(arg_verb, "suspend") && !streq(arg_verb, "hibernate") && - !streq(arg_verb, "hybrid-sleep")) { + !streq(arg_verb, "hybrid-sleep") && + !streq(arg_verb, "suspend-to-hibernate")) { log_error("Unknown command '%s'.", arg_verb); return -EINVAL; } @@ -199,6 +282,7 @@ static int parse_argv(int argc, char *argv[]) { int main(int argc, char *argv[]) { _cleanup_strv_free_ char **modes = NULL, **states = NULL; + usec_t delay = 0; int r; log_set_target(LOG_TARGET_AUTO); @@ -209,12 +293,14 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - r = parse_sleep_config(arg_verb, &modes, &states); + r = parse_sleep_config(arg_verb, &modes, &states, &delay); if (r < 0) goto finish; - r = execute(modes, states); - + if (streq(arg_verb, "suspend-to-hibernate")) + r = execute_s2h(delay); + else + r = execute(modes, states); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } |