From: Mike Yuan Date: Wed, 27 Mar 2024 11:45:34 +0000 (+0800) Subject: sleep: add SleepMemMode= setting for configuring /sys/power/mem_sleep X-Git-Tag: v256-rc1~361^2 X-Git-Url: http://git-history.diyao.me/?a=commitdiff_plain;h=a2124b35e92178fd028d7c29ecdd5e80ea7f7b8f;p=systemd%2F.git sleep: add SleepMemMode= setting for configuring /sys/power/mem_sleep 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. --- diff --git a/man/systemd-sleep.conf.xml b/man/systemd-sleep.conf.xml index b4db11a6b3..7a343975d7 100644 --- a/man/systemd-sleep.conf.xml +++ b/man/systemd-sleep.conf.xml @@ -195,6 +195,24 @@ + + SleepMemMode= + + The string to be written to /sys/power/mem_sleep + when or hybrid-sleep 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 + mem_sleep_default= is respected. + + The allowed set of values is determined by the kernel and is shown in the file itself (use + cat /sys/power/mem_sleep to display). See the kernel documentation page + + Basic sysfs Interfaces for System Suspend and Hibernation for more details. + + + + HibernateDelaySec= diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index be20f0aae2..4a90ad67bd 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -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) diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 12239a8a01..75d9c4a622 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -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 */ diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 72489b31a0..693484e886 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -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 = {}; diff --git a/src/sleep/sleep.conf b/src/sleep/sleep.conf index fad95b3897..d12f97099a 100644 --- a/src/sleep/sleep.conf +++ b/src/sleep/sleep.conf @@ -23,5 +23,6 @@ #AllowHybridSleep=yes #SuspendState=mem standby freeze #HibernateMode=platform shutdown +#SleepMemMode= #HibernateDelaySec= #SuspendEstimationSec=60min