From 2002d8cdaee105a7ab6bc89d480a9ded2198247a Mon Sep 17 00:00:00 2001 From: Zach Smith Date: Sat, 8 Jun 2019 20:44:34 -0700 Subject: [PATCH] systemd-sleep: (bug) use resume_offset value if set Use hibernation configuration as defined in /sys/power/resume and /sys/power/resume_offset if present before inspecting /proc/swaps and attempting to calculate swapfile offset --- TODO | 4 ---- src/shared/sleep-config.c | 1 + src/sleep/sleep.c | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/TODO b/TODO index d54e5ddeaf..02299bb7ca 100644 --- a/TODO +++ b/TODO @@ -56,10 +56,6 @@ Features: with a nice speed-up on services that have many processes running in the same cgroup. -* clean up sleep.c: - - Make sure resume= and resume_offset= on the kernel cmdline always take - precedence - * make MAINPID= message reception checks even stricter: if service uses User=, then check sending UID and ignore message if it doesn't match the user or root. diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 4026655809..0efbd7c7be 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -178,6 +178,7 @@ 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; diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index c38aa4ccf6..435e5592e6 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -80,6 +80,7 @@ static int write_hibernate_location_info(void) { if (r < 0) return log_debug_errno(errno, "Unable to stat %s: %m", device); + // TODO check for btrfs and fail if offset is not provided; calculation will fail r = read_fiemap(fd, &fiemap); if (r < 0) return log_debug_errno(r, "Unable to read extent map for '%s': %m", device); @@ -93,11 +94,15 @@ static int write_hibernate_location_info(void) { if (r < 0) return log_debug_errno(r, "Failed to write offset '%s': %m", offset_str); + log_debug("Wrote calculated resume_offset value to /sys/power/resume_offset: %s", offset_str); + xsprintf(device_str, "%lx", (unsigned long)stb.st_dev); r = write_string_file("/sys/power/resume", device_str, WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) return log_debug_errno(r, "Failed to write device '%s': %m", device_str); + log_debug("Wrote device id to /sys/power/resume: %s", device_str); + return 0; } @@ -143,6 +148,32 @@ static int write_state(FILE **f, char **states) { return r; } +static int configure_hibernation(void) { + _cleanup_free_ char *resume = NULL, *resume_offset = NULL; + int r; + + /* check for proper hibernation configuration */ + r = read_one_line_file("/sys/power/resume", &resume); + if (r < 0) + return log_debug_errno(r, "Error reading from /sys/power/resume: %m"); + + r = read_one_line_file("/sys/power/resume_offset", &resume_offset); + if (r < 0) + return log_debug_errno(r, "Error reading from /sys/power/resume_offset: %m"); + + if (!streq(resume_offset, "0") && !streq(resume, "0:0")) { + log_debug("Hibernating using device id and offset read from /sys/power/resume: %s and /sys/power/resume_offset: %s", resume, resume_offset); + return 0; + } else if (!streq(resume, "0:0")) { + log_debug("Hibernating using device id read from /sys/power/resume: %s", resume); + return 0; + } else if (!streq(resume_offset, "0")) + log_debug("Found offset in /sys/power/resume_offset: %s; no device id found in /sys/power/resume; ignoring offset", resume_offset); + + /* if hibernation is not properly configured, attempt to calculate and write values */ + return write_hibernate_location_info(); +} + static int execute(char **modes, char **states) { char *arguments[] = { NULL, @@ -168,9 +199,10 @@ static int execute(char **modes, char **states) { /* Configure the hibernation mode */ if (!strv_isempty(modes)) { - r = write_hibernate_location_info(); + r = configure_hibernation(); if (r < 0) - return log_error_errno(r, "Failed to write hibernation disk offset: %m"); + return log_error_errno(r, "Failed to prepare for hibernation: %m"); + r = write_mode(modes); if (r < 0) return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;