]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sleep: add SleepMemMode= setting for configuring /sys/power/mem_sleep 31986/head
authorMike Yuan <me@yhndnzj.com>
Wed, 27 Mar 2024 11:45:34 +0000 (19:45 +0800)
committerMike Yuan <me@yhndnzj.com>
Thu, 28 Mar 2024 09:19:35 +0000 (17:19 +0800)
The setting is used when /sys/power/state is set to 'mem'
(common for suspend) or /sys/power/disk is set to 'suspend'
(hybrid-sleep). We default to kernel choice here, i.e.
respect what's set through 'mem_sleep_default=' kernel
cmdline option.

man/systemd-sleep.conf.xml
src/shared/sleep-config.c
src/shared/sleep-config.h
src/sleep/sleep.c
src/sleep/sleep.conf

index b4db11a6b3bc110985c1b177b3310ab38b4b477a..7a343975d75aa0a33accbdffb8e60aaf54a0d58c 100644 (file)
         <xi:include href="version-info.xml" xpointer="v203"/></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>SleepMemMode=</varname></term>
+
+        <listitem><para>The string to be written to <filename>/sys/power/mem_sleep</filename>
+        when <option>SuspendState=mem</option> or <command>hybrid-sleep</command> is used.
+        More than one value can be specified by separating multiple values with whitespace. They will be
+        tried in turn, until one is written without error. If none of the writes succeed, the operation will
+        be aborted. Defaults to empty, i.e. the kernel default or kernel command line option
+        <varname>mem_sleep_default=</varname> is respected.</para>
+
+        <para>The allowed set of values is determined by the kernel and is shown in the file itself (use
+        <command>cat /sys/power/mem_sleep</command> to display). See the kernel documentation page
+        <ulink url="https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation">
+          Basic sysfs Interfaces for System Suspend and Hibernation</ulink> for more details.</para>
+
+        <xi:include href="version-info.xml" xpointer="v256"/></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>HibernateDelaySec=</varname></term>
 
index be20f0aae269c23b51a62ed39e11438317d5ae8a..4a90ad67bd0b104ce212bbc4c707f4e4907f8ae5 100644 (file)
@@ -54,6 +54,8 @@ SleepConfig* sleep_config_free(SleepConfig *sc) {
                 strv_free(sc->modes[i]);
         }
 
+        strv_free(sc->mem_modes);
+
         return mfree(sc);
 }
 
@@ -140,6 +142,8 @@ int parse_sleep_config(SleepConfig **ret) {
                 { "Sleep", "HybridSleepState",          config_parse_warn_compat, DISABLED_LEGACY, NULL                         },
                 { "Sleep", "HybridSleepMode",           config_parse_warn_compat, DISABLED_LEGACY, NULL                         },
 
+                { "Sleep", "SleepMemMode",              config_parse_sleep_mode,  0,               &sc->mem_modes               },
+
                 { "Sleep", "HibernateDelaySec",         config_parse_sec,         0,               &sc->hibernate_delay_usec    },
                 { "Sleep", "SuspendEstimationSec",      config_parse_sec,         0,               &sc->suspend_estimation_usec },
                 {}
@@ -344,6 +348,16 @@ static int sleep_supported_internal(
                 return false;
         }
 
+        if (SLEEP_NEEDS_MEM_SLEEP(sleep_config, operation)) {
+                r = sleep_mode_supported("/sys/power/mem_sleep", sleep_config->mem_modes);
+                if (r < 0)
+                        return r;
+                if (r == 0) {
+                        *ret_support = SLEEP_STATE_OR_MODE_NOT_SUPPORTED;
+                        return false;
+                }
+        }
+
         if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
                 r = sleep_mode_supported("/sys/power/disk", sleep_config->modes[operation]);
                 if (r < 0)
index 12239a8a019eee40c5e7de3b2a72d9e21c4108cf..75d9c4a6221efdda50733993875a2a0392a3393e 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 #pragma once
 
+#include "strv.h"
 #include "time-util.h"
 
 typedef enum SleepOperation {
@@ -28,7 +29,8 @@ typedef struct SleepConfig {
         bool allow[_SLEEP_OPERATION_MAX];
 
         char **states[_SLEEP_OPERATION_CONFIG_MAX];
-        char **modes[_SLEEP_OPERATION_CONFIG_MAX]; /* Power mode after writing hibernation image */
+        char **modes[_SLEEP_OPERATION_CONFIG_MAX];  /* Power mode after writing hibernation image (/sys/power/disk) */
+        char **mem_modes;                           /* /sys/power/mem_sleep */
 
         usec_t hibernate_delay_usec;
         usec_t suspend_estimation_usec;
@@ -39,6 +41,18 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(SleepConfig*, sleep_config_free);
 
 int parse_sleep_config(SleepConfig **sleep_config);
 
+static inline bool SLEEP_NEEDS_MEM_SLEEP(const SleepConfig *sc, SleepOperation operation) {
+        assert(sc);
+        assert(operation >= 0 && operation < _SLEEP_OPERATION_CONFIG_MAX);
+
+        /* As per https://docs.kernel.org/admin-guide/pm/sleep-states.html#basic-sysfs-interfaces-for-system-suspend-and-hibernation,
+         * /sys/power/mem_sleep is honored if /sys/power/state is set to "mem" (common for suspend)
+         * or /sys/power/disk is set to "suspend" (hybrid-sleep). */
+
+        return strv_contains(sc->states[operation], "mem") ||
+               strv_contains(sc->modes[operation], "suspend");
+}
+
 typedef enum SleepSupport {
         SLEEP_SUPPORTED,
         SLEEP_DISABLED,                    /* Disabled in SleepConfig.allow */
index 72489b31a0d487d54d02c0a8e6489a93c6ad063a..693484e886563e95dd323cdddcb8f8d727b8a563 100644 (file)
@@ -237,6 +237,12 @@ static int execute(
         if (state_fd < 0)
                 return log_error_errno(errno, "Failed to open /sys/power/state: %m");
 
+        if (SLEEP_NEEDS_MEM_SLEEP(sleep_config, operation)) {
+                r = write_mode("/sys/power/mem_sleep", sleep_config->mem_modes);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write mode to /sys/power/mem_sleep: %m");
+        }
+
         /* Configure hibernation settings if we are supposed to hibernate */
         if (SLEEP_OPERATION_IS_HIBERNATION(operation)) {
                 _cleanup_(hibernation_device_done) HibernationDevice hibernation_device = {};
index fad95b389779fbbddf3f246cd60b5907a8248277..d12f97099afe0383dfccdaf82e438be523cbbc4b 100644 (file)
@@ -23,5 +23,6 @@
 #AllowHybridSleep=yes
 #SuspendState=mem standby freeze
 #HibernateMode=platform shutdown
+#SleepMemMode=
 #HibernateDelaySec=
 #SuspendEstimationSec=60min