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