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