]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sleep: always write resume_offset if possible
authorMike Yuan <me@yhndnzj.com>
Tue, 18 Apr 2023 16:09:08 +0000 (00:09 +0800)
committerMike Yuan <me@yhndnzj.com>
Fri, 23 Jun 2023 15:57:49 +0000 (23:57 +0800)
There's no need to conditionalize this.
Setting resume_offset=0 doesn't harm, and can even help
by overriding potentially existing half-written settings.

src/shared/sleep-util.c
src/shared/sleep-util.h
src/sleep/sleep.c

index eb9a43f5078bc85bf7a64ef173434412e2da6fe4..1027ecce05cac9b1f9b1655d38b3a4db2dd89aab 100644 (file)
@@ -1028,6 +1028,52 @@ int read_fiemap(int fd, struct fiemap **ret) {
         return 0;
 }
 
+int write_resume_config(dev_t devno, uint64_t offset, const char *device) {
+        char offset_str[DECIMAL_STR_MAX(uint64_t)];
+        _cleanup_free_ char *path = NULL;
+        const char *devno_str;
+        int r;
+
+        devno_str = FORMAT_DEVNUM(devno);
+        xsprintf(offset_str, "%" PRIu64, offset);
+
+        if (!device) {
+                r = device_path_make_canonical(S_IFBLK, devno, &path);
+                if (r < 0)
+                        return log_error_errno(r,
+                                               "Failed to format canonical device path for devno '" DEVNUM_FORMAT_STR "': %m",
+                                               DEVNUM_FORMAT_VAL(devno));
+                device = path;
+        }
+
+        /* We write the offset first since it's safer. Note that this file is only available in 4.17+, so
+         * fail gracefully if it doesn't exist and we're only overwriting it with 0. */
+        r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
+        if (r == -ENOENT) {
+                if (offset != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "Can't configure hibernation offset %" PRIu64 ", kernel does not support /sys/power/resume_offset. Refusing.",
+                                               offset);
+
+                log_warning_errno(r, "/sys/power/resume_offset is unavailable, skipping writing swap file offset.");
+        } else if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to write swap file offset %s to /sys/power/resume_offset for device '%s': %m",
+                                       offset_str, device);
+        else
+                log_debug("Wrote resume_offset=%s for device '%s' to /sys/power/resume_offset.",
+                          offset_str, device);
+
+        r = write_string_file("/sys/power/resume", devno_str, WRITE_STRING_FILE_DISABLE_BUFFER);
+        if (r < 0)
+                return log_error_errno(r,
+                                       "Failed to write device '%s' (%s) to /sys/power/resume: %m",
+                                       device, devno_str);
+        log_debug("Wrote resume=%s for device '%s' to /sys/power/resume.", devno_str, device);
+
+        return 0;
+}
+
 static int can_sleep_internal(const SleepConfig *sleep_config, SleepOperation operation, bool check_allowed);
 
 static bool can_s2h(const SleepConfig *sleep_config) {
index 5f8bf090f92f83fad4c84886d1d91ef770108fa1..6480ca863738ba30761929319c152eee3eafb62d 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <linux/fiemap.h>
+#include <sys/types.h>
 
 #include "hashmap.h"
 #include "time-util.h"
@@ -63,6 +64,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(HibernateLocation*, hibernate_location_free);
 int read_fiemap(int fd, struct fiemap **ret);
 int parse_sleep_config(SleepConfig **sleep_config);
 int find_hibernate_location(HibernateLocation **ret_hibernate_location);
+int write_resume_config(dev_t devno, uint64_t offset, const char *device);
 
 int can_sleep(SleepOperation operation);
 int can_sleep_disk(char **types);
index 46f46914b56560b8599c9274434ed4bb4df3a80f..1d34d98453028b6e11e7f5bace4c6fdaaadc2ccb 100644 (file)
 static SleepOperation arg_operation = _SLEEP_OPERATION_INVALID;
 
 static int write_hibernate_location_info(const HibernateLocation *hibernate_location) {
-        char offset_str[DECIMAL_STR_MAX(uint64_t)];
-        const char *resume_str;
-        int r;
-
         assert(hibernate_location);
         assert(hibernate_location->swap);
+        assert(IN_SET(hibernate_location->swap->type, SWAP_BLOCK, SWAP_FILE));
 
-        resume_str = FORMAT_DEVNUM(hibernate_location->devno);
-
-        r = write_string_file("/sys/power/resume", resume_str, WRITE_STRING_FILE_DISABLE_BUFFER);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to write partition device to /sys/power/resume for '%s': '%s': %m",
-                                       hibernate_location->swap->path, resume_str);
-
-        log_debug("Wrote resume= value for %s to /sys/power/resume: %s", hibernate_location->swap->path, resume_str);
-
-        /* if it's a swap partition, we're done */
-        if (hibernate_location->swap->type == SWAP_BLOCK)
-                return 0;
-
-        assert(hibernate_location->swap->type == SWAP_FILE);
-
-        /* Only available in 4.17+ */
-        if (hibernate_location->offset > 0 && access("/sys/power/resume_offset", W_OK) < 0) {
-                if (errno == ENOENT) {
-                        log_debug("Kernel too old, can't configure resume_offset for %s, ignoring: %" PRIu64,
-                                  hibernate_location->swap->path, hibernate_location->offset);
-                        return 0;
-                }
-
-                return log_debug_errno(errno, "/sys/power/resume_offset not writable: %m");
-        }
-
-        xsprintf(offset_str, "%" PRIu64, hibernate_location->offset);
-        r = write_string_file("/sys/power/resume_offset", offset_str, WRITE_STRING_FILE_DISABLE_BUFFER);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to write swap file offset to /sys/power/resume_offset for '%s': '%s': %m",
-                                       hibernate_location->swap->path, offset_str);
-
-        log_debug("Wrote resume_offset= value for %s to /sys/power/resume_offset: %s", hibernate_location->swap->path, offset_str);
-
-        return 0;
+        return write_resume_config(hibernate_location->devno, hibernate_location->offset, hibernate_location->swap->path);
 }
 
 static int write_mode(char **modes) {
@@ -212,12 +175,14 @@ static int execute(
 
         /* Configure hibernation settings if we are supposed to hibernate */
         if (!strv_isempty(modes)) {
+                bool resume_set;
+
                 r = find_hibernate_location(&hibernate_location);
                 if (r < 0)
                         return log_error_errno(r, "Failed to find location to hibernate to: %m");
-                if (r == 0) { /* 0 means: no hibernation location was configured in the kernel so far, let's
-                               * do it ourselves then. > 0 means: kernel already had a configured hibernation
-                               * location which we shouldn't touch. */
+                resume_set = r > 0;
+
+                if (!resume_set) {
                         r = write_hibernate_location_info(hibernate_location);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to prepare for hibernation: %m");