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.
This commit is contained in:
Mike Yuan 2024-03-27 19:45:34 +08:00
parent b786128322
commit a2124b35e9
No known key found for this signature in database
GPG key ID: 417471C0A40F58B3
5 changed files with 54 additions and 1 deletions

View file

@ -195,6 +195,24 @@
<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>

View 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)

View 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 */

View 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 = {};

View file

@ -23,5 +23,6 @@
#AllowHybridSleep=yes
#SuspendState=mem standby freeze
#HibernateMode=platform shutdown
#SleepMemMode=
#HibernateDelaySec=
#SuspendEstimationSec=60min