From 69ab80881552d5f79ca95f6b3be48ad122ab1ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 13 Sep 2013 19:41:52 -0400 Subject: 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/sleep-config.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'src/shared/sleep-config.c') diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index cd3238b405..5ec7cce458 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(); } -- cgit v1.2.1 From 442e00839e4fc3c11506f5c8a9477b465865aecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 15 Sep 2013 08:40:16 -0400 Subject: Assume that /proc/meminfo can be missing Travis tests are failing, probably because /proc/meminfo is not available in the test environment. The same might be true in some virtualized systems, so just treat missing /proc/meminfo as a sign that hibernation is not possible. --- src/shared/sleep-config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/shared/sleep-config.c') diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 5ec7cce458..148c4dc617 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -172,7 +172,8 @@ static bool enough_memory_for_hibernation(void) { r = get_status_field("/proc/meminfo", "\nSwapFree:", &swapfree); if (r < 0) { - log_error("Failed to retrieve SwapFree from /proc/meminfo: %s", strerror(-r)); + log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, + "Failed to retrieve SwapFree from /proc/meminfo: %s", strerror(-r)); return false; } -- cgit v1.2.1 From 9fb3675e7ef0c6b7a1780980e51492c44fd1faaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 17 Sep 2013 15:12:16 -0500 Subject: Use first partition in /proc/swaps for hibernation test It seems that the kernel uses the first configured partition for hibernation. If it is too full, hibernation will fail. Test that directly. --- src/shared/sleep-config.c | 80 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 17 deletions(-) (limited to 'src/shared/sleep-config.c') diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 148c4dc617..d068bfce3c 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -165,24 +165,70 @@ int can_sleep_disk(char **types) { #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; +static int hibernation_partition_size(size_t *size, size_t *used) { + _cleanup_fclose_ FILE *f; + int i; + + assert(size); + assert(used); + + f = fopen("/proc/swaps", "r"); + if (!f) { + log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, + "Failed to retrieve open /proc/swaps: %m"); + assert(errno > 0); + return -errno; + } - r = get_status_field("/proc/meminfo", "\nSwapFree:", &swapfree); - if (r < 0) { - log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, - "Failed to retrieve SwapFree from /proc/meminfo: %s", strerror(-r)); - return false; + (void) fscanf(f, "%*s %*s %*s %*s %*s\n"); + + for (i = 1;; i++) { + _cleanup_free_ char *dev = NULL, *d = NULL, *type = NULL; + size_t size_field, used_field; + int k; + + k = fscanf(f, + "%ms " /* device/file */ + "%ms " /* type of swap */ + "%zd " /* swap size */ + "%zd " /* used */ + "%*i\n", /* priority */ + &dev, &type, &size_field, &used_field); + if (k != 4) { + if (k == EOF) + break; + + log_warning("Failed to parse /proc/swaps:%u", i); + continue; + } + + d = cunescape(dev); + if (!d) + return -ENOMEM; + + if (!streq(type, "partition")) { + log_debug("Partition %s has type %s, ignoring.", d, type); + continue; + } + + *size = size_field; + *used = used_field; + return 0; } - r = safe_atollu(swapfree, &swap); - if (r < 0) { - log_error("Failed to parse SwapFree from /proc/meminfo: %s: %s", - swapfree, strerror(-r)); + log_debug("No swap partitions were found."); + return -ENOSYS; +} + +static bool enough_memory_for_hibernation(void) { + _cleanup_free_ char *active = NULL; + unsigned long long act; + size_t size, used; + int r; + + r = hibernation_partition_size(&size, &used); + if (r < 0) return false; - } r = get_status_field("/proc/meminfo", "\nActive(anon):", &active); if (r < 0) { @@ -197,9 +243,9 @@ static bool enough_memory_for_hibernation(void) { 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); + r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD; + log_debug("Hibernation is %spossible, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%", + r ? "" : "im", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD); return r; } -- cgit v1.2.1