Harald Hoyer d4ee25
From 3559039a97e1d3e28dd9b38202d3499652a58036 Mon Sep 17 00:00:00 2001
Harald Hoyer d4ee25
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Harald Hoyer d4ee25
Date: Fri, 13 Sep 2013 19:41:52 -0400
Harald Hoyer d4ee25
Subject: [PATCH] Advertise hibernation only if there's enough free swap
Harald Hoyer d4ee25
Harald Hoyer d4ee25
Condition that is checked is taken from upower:
Harald Hoyer d4ee25
  active(anon) < free swap * 0.98
Harald Hoyer d4ee25
Harald Hoyer d4ee25
This is really stupid, because the kernel knows the situation better,
Harald Hoyer d4ee25
e.g. there could be two swap files, and then hibernation would be
Harald Hoyer d4ee25
impossible despite passing this check, or the kernel could start
Harald Hoyer d4ee25
supporting compressed swap and/or compressed hibernation images, and
Harald Hoyer d4ee25
then this this check would be too stringent. Nevertheless, until
Harald Hoyer d4ee25
we have something better, this should at least return a true negative
Harald Hoyer d4ee25
if there's no swap.
Harald Hoyer d4ee25
Harald Hoyer d4ee25
Logging of capabilities in the journal is changed to not strip leading
Harald Hoyer d4ee25
zeros. I consider this more readable anyway.
Harald Hoyer d4ee25
Harald Hoyer d4ee25
http://cgit.freedesktop.org/upower/tree/src/up-daemon.c#n613
Harald Hoyer d4ee25
https://bugzilla.redhat.com/show_bug.cgi?id=1007059
Harald Hoyer d4ee25
---
Harald Hoyer d4ee25
 src/shared/fileio.c       | 34 ++++++++++++++++++++++++++++++++++
Harald Hoyer d4ee25
 src/shared/fileio.h       |  2 ++
Harald Hoyer d4ee25
 src/shared/logs-show.c    |  2 +-
Harald Hoyer d4ee25
 src/shared/sleep-config.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
Harald Hoyer d4ee25
 src/shared/util.c         | 23 +----------------------
Harald Hoyer d4ee25
 src/test/test-fileio.c    | 20 ++++++++++++++++++++
Harald Hoyer d4ee25
 src/test/test-sleep.c     | 16 ++++++++--------
Harald Hoyer d4ee25
 7 files changed, 110 insertions(+), 32 deletions(-)
Harald Hoyer d4ee25
Harald Hoyer d4ee25
diff --git a/src/shared/fileio.c b/src/shared/fileio.c
Harald Hoyer d4ee25
index 77fd059..4e2b444 100644
Harald Hoyer d4ee25
--- a/src/shared/fileio.c
Harald Hoyer d4ee25
+++ b/src/shared/fileio.c
Harald Hoyer d4ee25
@@ -648,3 +648,37 @@ int executable_is_script(const char *path, char **interpreter) {
Harald Hoyer d4ee25
         *interpreter = ans;
Harald Hoyer d4ee25
         return 1;
Harald Hoyer d4ee25
 }
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+/**
Harald Hoyer d4ee25
+ * Retrieve one field from a file like /proc/self/status.
Harald Hoyer d4ee25
+ * pattern should start with '\n' and end with ':'. Whitespace
Harald Hoyer d4ee25
+ * after ':' will be skipped. field must be freed afterwards.
Harald Hoyer d4ee25
+ */
Harald Hoyer d4ee25
+int get_status_field(const char *filename, const char *pattern, char **field) {
Harald Hoyer d4ee25
+        _cleanup_free_ char *status = NULL;
Harald Hoyer d4ee25
+        char *t;
Harald Hoyer d4ee25
+        size_t len;
Harald Hoyer d4ee25
+        int r;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        assert(filename);
Harald Hoyer d4ee25
+        assert(field);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        r = read_full_file(filename, &status, NULL);
Harald Hoyer d4ee25
+        if (r < 0)
Harald Hoyer d4ee25
+                return r;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        t = strstr(status, pattern);
Harald Hoyer d4ee25
+        if (!t)
Harald Hoyer d4ee25
+                return -ENOENT;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        t += strlen(pattern);
Harald Hoyer d4ee25
+        t += strspn(t, WHITESPACE);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        len = strcspn(t, WHITESPACE);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        *field = strndup(t, len);
Harald Hoyer d4ee25
+        if (!*field)
Harald Hoyer d4ee25
+                return -ENOMEM;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        return 0;
Harald Hoyer d4ee25
+}
Harald Hoyer d4ee25
diff --git a/src/shared/fileio.h b/src/shared/fileio.h
Harald Hoyer d4ee25
index a0aae28..59e4150 100644
Harald Hoyer d4ee25
--- a/src/shared/fileio.h
Harald Hoyer d4ee25
+++ b/src/shared/fileio.h
Harald Hoyer d4ee25
@@ -37,3 +37,5 @@ int load_env_file(const char *fname, const char *separator, char ***l);
Harald Hoyer d4ee25
 int write_env_file(const char *fname, char **l);
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
 int executable_is_script(const char *path, char **interpreter);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+int get_status_field(const char *filename, const char *pattern, char **field);
Harald Hoyer d4ee25
diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c
Harald Hoyer d4ee25
index 87633e7..f50777c 100644
Harald Hoyer d4ee25
--- a/src/shared/logs-show.c
Harald Hoyer d4ee25
+++ b/src/shared/logs-show.c
Harald Hoyer d4ee25
@@ -201,7 +201,7 @@ static int output_short(
Harald Hoyer d4ee25
         assert(j);
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
         /* Set the threshold to one bigger than the actual print
Harald Hoyer d4ee25
-         * treshold, so that if the line is actually longer than what
Harald Hoyer d4ee25
+         * threshold, so that if the line is actually longer than what
Harald Hoyer d4ee25
          * we're willing to print, ellipsization will occur. This way
Harald Hoyer d4ee25
          * we won't output a misleading line without any indication of
Harald Hoyer d4ee25
          * truncation.
Harald Hoyer d4ee25
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
Harald Hoyer d4ee25
index cd3238b..5ec7cce 100644
Harald Hoyer d4ee25
--- a/src/shared/sleep-config.c
Harald Hoyer d4ee25
+++ b/src/shared/sleep-config.c
Harald Hoyer d4ee25
@@ -163,6 +163,46 @@ int can_sleep_disk(char **types) {
Harald Hoyer d4ee25
         return false;
Harald Hoyer d4ee25
 }
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
+#define HIBERNATION_SWAP_THRESHOLD 0.98
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+static bool enough_memory_for_hibernation(void) {
Harald Hoyer d4ee25
+        _cleanup_free_ char *active = NULL, *swapfree = NULL;
Harald Hoyer d4ee25
+        unsigned long long act, swap;
Harald Hoyer d4ee25
+        int r;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        r = get_status_field("/proc/meminfo", "\nSwapFree:", &swapfree);
Harald Hoyer d4ee25
+        if (r < 0) {
Harald Hoyer d4ee25
+                log_error("Failed to retrieve SwapFree from /proc/meminfo: %s", strerror(-r));
Harald Hoyer d4ee25
+                return false;
Harald Hoyer d4ee25
+        }
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        r = safe_atollu(swapfree, &swap;;
Harald Hoyer d4ee25
+        if (r < 0) {
Harald Hoyer d4ee25
+                log_error("Failed to parse SwapFree from /proc/meminfo: %s: %s",
Harald Hoyer d4ee25
+                          swapfree, strerror(-r));
Harald Hoyer d4ee25
+                return false;
Harald Hoyer d4ee25
+        }
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        r = get_status_field("/proc/meminfo", "\nActive(anon):", &active);
Harald Hoyer d4ee25
+        if (r < 0) {
Harald Hoyer d4ee25
+                log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r));
Harald Hoyer d4ee25
+                return false;
Harald Hoyer d4ee25
+        }
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        r = safe_atollu(active, &act;;
Harald Hoyer d4ee25
+        if (r < 0) {
Harald Hoyer d4ee25
+                log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s",
Harald Hoyer d4ee25
+                          active, strerror(-r));
Harald Hoyer d4ee25
+                return false;
Harald Hoyer d4ee25
+        }
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        r = act <= swap * HIBERNATION_SWAP_THRESHOLD;
Harald Hoyer d4ee25
+        log_debug("Hibernation is %spossible, Active(anon)=%llu kB, SwapFree=%llu kB, threshold=%.2g%%",
Harald Hoyer d4ee25
+                  r ? "" : "im", act, swap, 100*HIBERNATION_SWAP_THRESHOLD);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        return r;
Harald Hoyer d4ee25
+}
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
 int can_sleep(const char *verb) {
Harald Hoyer d4ee25
         _cleanup_strv_free_ char **modes = NULL, **states = NULL;
Harald Hoyer d4ee25
         int r;
Harald Hoyer d4ee25
@@ -175,5 +215,8 @@ int can_sleep(const char *verb) {
Harald Hoyer d4ee25
         if (r < 0)
Harald Hoyer d4ee25
                 return false;
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
-        return can_sleep_state(states) && can_sleep_disk(modes);
Harald Hoyer d4ee25
+        if (!can_sleep_state(states) || !can_sleep_disk(modes))
Harald Hoyer d4ee25
+                return false;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        return streq(verb, "suspend") || enough_memory_for_hibernation();
Harald Hoyer d4ee25
 }
Harald Hoyer d4ee25
diff --git a/src/shared/util.c b/src/shared/util.c
Harald Hoyer d4ee25
index 9a075fa..f6f3b18 100644
Harald Hoyer d4ee25
--- a/src/shared/util.c
Harald Hoyer d4ee25
+++ b/src/shared/util.c
Harald Hoyer d4ee25
@@ -694,9 +694,6 @@ int is_kernel_thread(pid_t pid) {
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
 int get_process_capeff(pid_t pid, char **capeff) {
Harald Hoyer d4ee25
         const char *p;
Harald Hoyer d4ee25
-        _cleanup_free_ char *status = NULL;
Harald Hoyer d4ee25
-        char *t = NULL;
Harald Hoyer d4ee25
-        int r;
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
         assert(capeff);
Harald Hoyer d4ee25
         assert(pid >= 0);
Harald Hoyer d4ee25
@@ -706,25 +703,7 @@ int get_process_capeff(pid_t pid, char **capeff) {
Harald Hoyer d4ee25
         else
Harald Hoyer d4ee25
                 p = procfs_file_alloca(pid, "status");
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
-        r = read_full_file(p, &status, NULL);
Harald Hoyer d4ee25
-        if (r < 0)
Harald Hoyer d4ee25
-                return r;
Harald Hoyer d4ee25
-
Harald Hoyer d4ee25
-        t = strstr(status, "\nCapEff:\t");
Harald Hoyer d4ee25
-        if (!t)
Harald Hoyer d4ee25
-                return -ENOENT;
Harald Hoyer d4ee25
-
Harald Hoyer d4ee25
-        for (t += strlen("\nCapEff:\t"); t[0] == '0'; t++)
Harald Hoyer d4ee25
-                continue;
Harald Hoyer d4ee25
-
Harald Hoyer d4ee25
-        if (t[0] == '\n')
Harald Hoyer d4ee25
-                t--;
Harald Hoyer d4ee25
-
Harald Hoyer d4ee25
-        *capeff = strndup(t, strchr(t, '\n') - t);
Harald Hoyer d4ee25
-        if (!*capeff)
Harald Hoyer d4ee25
-                return -ENOMEM;
Harald Hoyer d4ee25
-
Harald Hoyer d4ee25
-        return 0;
Harald Hoyer d4ee25
+        return get_status_field(p, "\nCapEff:", capeff);
Harald Hoyer d4ee25
 }
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
 int get_process_exe(pid_t pid, char **name) {
Harald Hoyer d4ee25
diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c
Harald Hoyer d4ee25
index 1184e7e..4a4ed79 100644
Harald Hoyer d4ee25
--- a/src/test/test-fileio.c
Harald Hoyer d4ee25
+++ b/src/test/test-fileio.c
Harald Hoyer d4ee25
@@ -229,9 +229,29 @@ static void test_executable_is_script(void) {
Harald Hoyer d4ee25
         unlink(t);
Harald Hoyer d4ee25
 }
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
+static void test_status_field(void) {
Harald Hoyer d4ee25
+        _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL;
Harald Hoyer d4ee25
+        unsigned long long total, buffers;
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        assert_se(get_status_field("/proc/self/status", "\nThreads:", &t) == 0);
Harald Hoyer d4ee25
+        puts(t);
Harald Hoyer d4ee25
+        assert_se(streq(t, "1"));
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        assert_se(get_status_field("/proc/meminfo", "MemTotal:", &p) == 0);
Harald Hoyer d4ee25
+        puts(p);
Harald Hoyer d4ee25
+        assert_se(safe_atollu(p, &total) == 0);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        assert_se(get_status_field("/proc/meminfo", "\nBuffers:", &s) == 0);
Harald Hoyer d4ee25
+        puts(s);
Harald Hoyer d4ee25
+        assert_se(safe_atollu(s, &buffers) == 0);
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
+        assert(buffers < total);
Harald Hoyer d4ee25
+}
Harald Hoyer d4ee25
+
Harald Hoyer d4ee25
 int main(int argc, char *argv[]) {
Harald Hoyer d4ee25
         test_parse_env_file();
Harald Hoyer d4ee25
         test_parse_multiline_env_file();
Harald Hoyer d4ee25
         test_executable_is_script();
Harald Hoyer d4ee25
+        test_status_field();
Harald Hoyer d4ee25
         return 0;
Harald Hoyer d4ee25
 }
Harald Hoyer d4ee25
diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c
Harald Hoyer d4ee25
index c3cb9c5..545dfab 100644
Harald Hoyer d4ee25
--- a/src/test/test-sleep.c
Harald Hoyer d4ee25
+++ b/src/test/test-sleep.c
Harald Hoyer d4ee25
@@ -40,14 +40,14 @@ int main(int argc, char* argv[]) {
Harald Hoyer d4ee25
                 **shutdown = strv_new("shutdown", NULL),
Harald Hoyer d4ee25
                 **freez = strv_new("freeze", NULL);
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
-        log_info("Can Standby: %s", yes_no(can_sleep_state(standby) > 0));
Harald Hoyer d4ee25
-        log_info("Can Suspend: %s", yes_no(can_sleep_state(mem) > 0));
Harald Hoyer d4ee25
-        log_info("Can Hibernate: %s", yes_no(can_sleep_state(disk) > 0));
Harald Hoyer d4ee25
-        log_info("Can Hibernate+Suspend (Hybrid-Sleep): %s", yes_no(can_sleep_disk(suspend) > 0));
Harald Hoyer d4ee25
-        log_info("Can Hibernate+Reboot: %s", yes_no(can_sleep_disk(reboot) > 0));
Harald Hoyer d4ee25
-        log_info("Can Hibernate+Platform: %s", yes_no(can_sleep_disk(platform) > 0));
Harald Hoyer d4ee25
-        log_info("Can Hibernate+Shutdown: %s", yes_no(can_sleep_disk(shutdown) > 0));
Harald Hoyer d4ee25
-        log_info("Can Freeze: %s", yes_no(can_sleep_disk(freez) > 0));
Harald Hoyer d4ee25
+        log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0));
Harald Hoyer d4ee25
+        log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0));
Harald Hoyer d4ee25
+        log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0));
Harald Hoyer d4ee25
+        log_info("Hibernate+Suspend (Hybrid-Sleep) configured: %s", yes_no(can_sleep_disk(suspend) > 0));
Harald Hoyer d4ee25
+        log_info("Hibernate+Reboot configured: %s", yes_no(can_sleep_disk(reboot) > 0));
Harald Hoyer d4ee25
+        log_info("Hibernate+Platform configured: %s", yes_no(can_sleep_disk(platform) > 0));
Harald Hoyer d4ee25
+        log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0));
Harald Hoyer d4ee25
+        log_info("Freeze configured: %s", yes_no(can_sleep_state(freez) > 0));
Harald Hoyer d4ee25
 
Harald Hoyer d4ee25
         log_info("Suspend configured and possible: %s", yes_no(can_sleep("suspend") > 0));
Harald Hoyer d4ee25
         log_info("Hibernation configured and possible: %s", yes_no(can_sleep("hibernate") > 0));