summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZach Smith <z@zxmth.us>2019-07-15 05:01:20 +0200
committerLennart Poettering <lennart@poettering.net>2019-07-29 12:59:17 +0200
commit88bc86fcf895da0d51ddaf93d17b4280f4e60d74 (patch)
treee1ff1dde5f76a8476de739c852f89d01d5ec79bd
parentmore 243 news (diff)
downloadsystemd-88bc86fcf895da0d51ddaf93d17b4280f4e60d74.tar.xz
systemd-88bc86fcf895da0d51ddaf93d17b4280f4e60d74.zip
systemd-sleep: use swaps in priority order
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.
-rw-r--r--TODO2
-rw-r--r--src/shared/sleep-config.c96
-rw-r--r--src/shared/sleep-config.h2
3 files changed, 67 insertions, 33 deletions
diff --git a/TODO b/TODO
index af41aa57ac..8d0a4dabcf 100644
--- 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
diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c
index 0efbd7c7be..f3c54f5ca1 100644
--- a/src/shared/sleep-config.c
+++ b/src/shared/sleep-config.c
@@ -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;
diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h
index 965fde93a2..c1cbf43326 100644
--- a/src/shared/sleep-config.h
+++ b/src/shared/sleep-config.h
@@ -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);