]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
systemd-sleep: use swaps in priority order
authorZach Smith <z@zxmth.us>
Mon, 15 Jul 2019 03:01:20 +0000 (20:01 -0700)
committerLennart Poettering <lennart@poettering.net>
Mon, 29 Jul 2019 10:59:17 +0000 (12:59 +0200)
In situations where hibernation is requested but resume= and
resume_offset= kernel parameters are not configured, systemd
will attempt to locate a suitable swap location by inspecting
/proc/swaps. This change will use the first suitable swap with
the highest configured priority.

TODO
src/shared/sleep-config.c
src/shared/sleep-config.h

diff --git a/TODO b/TODO
index af41aa57acd84b1f14e9856d1cbbeaec7707e690..8d0a4dabcf95ebf988085c88e7a91020f5693856 100644 (file)
--- a/TODO
+++ b/TODO
@@ -626,8 +626,6 @@ Features:
 * transient units:
   - add field to transient units that indicate whether systemd or somebody else saves/restores its settings, for integration with libvirt
 
-* Automatically configure swap partition to use for hibernation by looking for largest swap partition on the root disk?
-
 * when we detect low battery and no AC on boot, show pretty splash and refuse boot
 
 * libsystemd-journal, libsystemd-login, libudev: add calls to easily attach these objects to sd-event event loops
index 0efbd7c7bed68c6c0f3c4f3645f94db22e9a3fff..f3c54f5ca184f62d23f048e1680f2238d8699bcd 100644 (file)
@@ -165,8 +165,30 @@ int can_sleep_disk(char **types) {
 
 #define HIBERNATION_SWAP_THRESHOLD 0.98
 
-int find_hibernate_location(char **device, char **type, size_t *size, size_t *used) {
+/* entry in /proc/swaps */
+typedef struct SwapEntry {
+        char *device;
+        char *type;
+        uint64_t size;
+        uint64_t used;
+        int priority;
+} SwapEntry;
+
+static SwapEntry* swap_entry_free(SwapEntry *se) {
+        if (!se)
+                return NULL;
+
+        free(se->device);
+        free(se->type);
+
+        return mfree(se);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(SwapEntry*, swap_entry_free);
+
+int find_hibernate_location(char **device, char **type, uint64_t *size, uint64_t *used) {
         _cleanup_fclose_ FILE *f;
+        _cleanup_(swap_entry_freep) SwapEntry *selected_swap = NULL;
         unsigned i;
 
         f = fopen("/proc/swaps", "re");
@@ -178,62 +200,76 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us
 
         (void) fscanf(f, "%*s %*s %*s %*s %*s\n");
 
-        // TODO: sort swaps in priority order rather than using first successful option
         for (i = 1;; i++) {
-                _cleanup_free_ char *dev_field = NULL, *type_field = NULL;
-                size_t size_field, used_field;
+                _cleanup_(swap_entry_freep) SwapEntry *swap = NULL;
                 int k;
 
+                swap = new0(SwapEntry, 1);
+                if (!swap)
+                        return log_oom();
+
                 k = fscanf(f,
-                           "%ms "   /* device/file */
-                           "%ms "   /* type of swap */
-                           "%zu "   /* swap size */
-                           "%zu "   /* used */
-                           "%*i\n", /* priority */
-                           &dev_field, &type_field, &size_field, &used_field);
+                           "%ms "       /* device/file */
+                           "%ms "       /* type of swap */
+                           "%" PRIu64   /* swap size */
+                           "%" PRIu64   /* used */
+                           "%i\n",      /* priority */
+                           &swap->device, &swap->type, &swap->size, &swap->used, &swap->priority);
                 if (k == EOF)
                         break;
-                if (k != 4) {
+                if (k != 5) {
                         log_warning("Failed to parse /proc/swaps:%u", i);
                         continue;
                 }
 
-                if (streq(type_field, "file")) {
+                if (streq(swap->type, "file")) {
 
-                        if (endswith(dev_field, "\\040(deleted)")) {
-                                log_warning("Ignoring deleted swap file '%s'.", dev_field);
+                        if (endswith(swap->device, "\\040(deleted)")) {
+                                log_warning("Ignoring deleted swap file '%s'.", swap->device);
                                 continue;
                         }
 
-                } else if (streq(type_field, "partition")) {
+                } else if (streq(swap->type, "partition")) {
                         const char *fn;
 
-                        fn = path_startswith(dev_field, "/dev/");
+                        fn = path_startswith(swap->device, "/dev/");
                         if (fn && startswith(fn, "zram")) {
-                                log_debug("Ignoring compressed RAM swap device '%s'.", dev_field);
+                                log_debug("Ignoring compressed RAM swap device '%s'.", swap->device);
                                 continue;
                         }
                 }
 
-                if (device)
-                        *device = TAKE_PTR(dev_field);
-                if (type)
-                        *type = TAKE_PTR(type_field);
-                if (size)
-                        *size = size_field;
-                if (used)
-                        *used = used_field;
-                return 0;
+                /* prefer highest priority or swap with most remaining space when same priority */
+                if (!selected_swap || swap->priority > selected_swap->priority
+                    || ((swap->priority == selected_swap->priority)
+                        && (swap->size - swap->used) > (selected_swap->size - selected_swap->used))) {
+                        selected_swap = swap_entry_free(selected_swap);
+                        selected_swap = TAKE_PTR(swap);
+                }
         }
 
-        return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS),
-                               "No swap partitions were found.");
+        if (!selected_swap)
+                return log_debug_errno(SYNTHETIC_ERRNO(ENOSYS), "No swap partitions or files were found.");
+
+        /* use the swap entry with the highest priority */
+        if (device)
+                *device = TAKE_PTR(selected_swap->device);
+        if (type)
+                *type = TAKE_PTR(selected_swap->type);
+        if (size)
+                *size = selected_swap->size;
+        if (used)
+                *used = selected_swap->used;
+
+        log_debug("Highest priority swap entry found %s: %i", selected_swap->device, selected_swap->priority);
+
+        return 0;
 }
 
 static bool enough_swap_for_hibernation(void) {
         _cleanup_free_ char *active = NULL;
         unsigned long long act = 0;
-        size_t size = 0, used = 0;
+        uint64_t size = 0, used = 0;
         int r;
 
         if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0)
@@ -256,7 +292,7 @@ static bool enough_swap_for_hibernation(void) {
         }
 
         r = act <= (size - used) * HIBERNATION_SWAP_THRESHOLD;
-        log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%zu kB, used=%zu kB, threshold=%.2g%%",
+        log_debug("%s swap for hibernation, Active(anon)=%llu kB, size=%" PRIu64 " kB, used=%" PRIu64 " kB, threshold=%.2g%%",
                   r ? "Enough" : "Not enough", act, size, used, 100*HIBERNATION_SWAP_THRESHOLD);
 
         return r;
index 965fde93a260f633e39d0f3c118fed6f20730bbb..c1cbf43326ef3150e72296bc19675a035d3ca33f 100644 (file)
@@ -27,7 +27,7 @@ int sleep_settings(const char *verb, const SleepConfig *sleep_config, bool *ret_
 
 int read_fiemap(int fd, struct fiemap **ret);
 int parse_sleep_config(SleepConfig **sleep_config);
-int find_hibernate_location(char **device, char **type, size_t *size, size_t *used);
+int find_hibernate_location(char **device, char **type, uint64_t *size, uint64_t *used);
 
 int can_sleep(const char *verb);
 int can_sleep_disk(char **types);