Skip to content

Commit

Permalink
Introduce suspend-to-hibernate (#8274)
Browse files Browse the repository at this point in the history
Suspend to Hibernate is a new sleep method that invokes suspend
for a predefined period of time before automatically waking up
and hibernating the system.

It's similar to HybridSleep however there isn't a performance
impact on every suspend cycle.

It's intended to use with systems that may have a higher power
drain in their supported suspend states to prevent battery and
data loss over an extended suspend cycle.

Signed-off-by: Mario Limonciello <mario.limonciello@dell.com>
  • Loading branch information
superm1 authored and keszybz committed Mar 8, 2018
1 parent fc17f19 commit c58493c
Show file tree
Hide file tree
Showing 20 changed files with 315 additions and 46 deletions.
6 changes: 4 additions & 2 deletions man/logind.conf.xml
Expand Up @@ -175,7 +175,8 @@
<literal>kexec</literal>,
<literal>suspend</literal>,
<literal>hibernate</literal>,
<literal>hybrid-sleep</literal>, and
<literal>hybrid-sleep</literal>,
<literal>suspend-to-hibernate</literal>, and
<literal>lock</literal>.
Defaults to <literal>ignore</literal>.</para>

Expand Down Expand Up @@ -224,7 +225,8 @@
<literal>kexec</literal>,
<literal>suspend</literal>,
<literal>hibernate</literal>,
<literal>hybrid-sleep</literal>, and
<literal>hybrid-sleep</literal>,
<literal>suspend-to-hibernate</literal>, and
<literal>lock</literal>.
If <literal>ignore</literal>, logind will never handle these
keys. If <literal>lock</literal>, all running sessions will be
Expand Down
1 change: 1 addition & 0 deletions man/rules/meson.build
Expand Up @@ -626,6 +626,7 @@ manpages = [
'8',
['systemd-hibernate.service',
'systemd-hybrid-sleep.service',
'systemd-suspend-to-hibernate.service',
'systemd-sleep'],
''],
['systemd-sysctl.service', '8', ['systemd-sysctl'], ''],
Expand Down
33 changes: 28 additions & 5 deletions man/systemd-sleep.conf.xml
Expand Up @@ -60,7 +60,7 @@
<refsect1>
<title>Description</title>

<para><command>systemd</command> supports three general
<para><command>systemd</command> supports four general
power-saving modes:</para>

<variablelist>
Expand Down Expand Up @@ -102,6 +102,17 @@
suspend-to-both by the kernel.
</para></listitem>
</varlistentry>

<varlistentry>
<term>suspend-to-hibernate</term>

<listitem><para>A low power state where the system is initially suspended
(the state is stored in RAM). If not interrupted within the delay specified by
<command>HibernateDelaySec=</command>, the system will be woken using an RTC
alarm and hibernated (the state is then stored on disk).
</para></listitem>
</varlistentry>

</variablelist>

<para>Settings in these files determine what strings
Expand Down Expand Up @@ -134,8 +145,9 @@
<filename>/sys/power/disk</filename> by,
respectively,
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
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
Expand All @@ -152,14 +164,24 @@
<filename>/sys/power/state</filename> by,
respectively,
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
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
neither succeeds, the operation will be aborted.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>HibernateDelaySec=</varname></term>

<listitem><para>The amount of time in seconds
that will pass before the system is automatically
put into hibernate when using
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

Expand All @@ -180,6 +202,7 @@ SuspendState=freeze</programlisting></para>
<citerefentry><refentrytitle>systemd-suspend.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-hybrid-sleep.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-suspend-to-hibernate.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
Expand Down
17 changes: 12 additions & 5 deletions man/systemd-suspend.service.xml
Expand Up @@ -50,6 +50,7 @@
<refname>systemd-suspend.service</refname>
<refname>systemd-hibernate.service</refname>
<refname>systemd-hybrid-sleep.service</refname>
<refname>systemd-suspend-to-hibernate.service</refname>
<refname>systemd-sleep</refname>
<refpurpose>System sleep state logic</refpurpose>
</refnamediv>
Expand All @@ -58,6 +59,7 @@
<para><filename>systemd-suspend.service</filename></para>
<para><filename>systemd-hibernate.service</filename></para>
<para><filename>systemd-hybrid-sleep.service</filename></para>
<para><filename>systemd-suspend-to-hibernate.service</filename></para>
<para><filename>/usr/lib/systemd/system-sleep</filename></para>
</refsynopsisdiv>

Expand All @@ -72,16 +74,19 @@
hibernation. Finally,
<filename>systemd-hybrid-sleep.service</filename> is pulled in by
<filename>hybrid-sleep.target</filename> to execute hybrid
hibernation with system suspend.</para>
hibernation with system suspend and pulled in by
<filename>suspend-to-hibernate.target</filename> to execute system suspend
with a timeout that will activate hibernate later.</para>

<para>Immediately before entering system suspend and/or
hibernation <filename>systemd-suspend.service</filename> (and the
other mentioned units, respectively) will run all executables in
<filename>/usr/lib/systemd/system-sleep/</filename> and pass two
arguments to them. The first argument will be
<literal>pre</literal>, the second either
<literal>suspend</literal>, <literal>hibernate</literal>, or
<literal>hybrid-sleep</literal> depending on the chosen action.
<literal>suspend</literal>, <literal>hibernate</literal>,
<literal>hybrid-sleep</literal>, or <literal>suspend-to-hibernate</literal>
depending on the chosen action.
Immediately after leaving system suspend and/or hibernation the
same executables are run, but the first argument is now
<literal>post</literal>. All executables in this directory are
Expand All @@ -100,6 +105,7 @@
<filename>systemd-suspend.service</filename>,
<filename>systemd-hibernate.service</filename>, and
<filename>systemd-hybrid-sleep.service</filename>
<filename>systemd-suspend-to-hibernate.service</filename>
should never be executed directly. Instead, trigger system sleep
states with a command such as <literal>systemctl suspend</literal>
or similar.</para>
Expand Down Expand Up @@ -128,9 +134,10 @@
<term><option>suspend</option></term>
<term><option>hibernate</option></term>
<term><option>hybrid-sleep</option></term>
<term><option>suspend-to-hibernate</option></term>

<listitem><para>Suspend, hibernate, or put the system to
hybrid sleep.</para>
<listitem><para>Suspend, hibernate, suspend to hibernate, or put the
system to hybrid sleep.</para>
</listitem>
</varlistentry>
</variablelist>
Expand Down
10 changes: 10 additions & 0 deletions man/systemd.special.xml
Expand Up @@ -65,6 +65,7 @@
<filename>halt.target</filename>,
<filename>hibernate.target</filename>,
<filename>hybrid-sleep.target</filename>,
<filename>suspend-to-hibernate.target</filename>,
<filename>initrd-fs.target</filename>,
<filename>initrd-root-device.target</filename>,
<filename>initrd-root-fs.target</filename>,
Expand Down Expand Up @@ -307,6 +308,15 @@
<filename>sleep.target</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>suspend-to-hibernate.target</filename></term>
<listitem>
<para>A special target unit for suspending the system for a period
of time, waking it and putting it into hibernate. This pulls in
<filename>sleep.target</filename>.</para>
</listitem>
</varlistentry>

<varlistentry>
<term><filename>halt.target</filename></term>
<listitem>
Expand Down
5 changes: 3 additions & 2 deletions shell-completion/bash/systemctl.in
Expand Up @@ -205,8 +205,9 @@ _systemctl () {
[JOBS]='cancel'
[ENVS]='set-environment unset-environment import-environment'
[STANDALONE]='daemon-reexec daemon-reload default
emergency exit halt hibernate hybrid-sleep kexec list-jobs
list-sockets list-timers list-units list-unit-files poweroff
emergency exit halt hibernate hybrid-sleep
suspend-to-hibernate kexec list-jobs list-sockets
list-timers list-units list-unit-files poweroff
reboot rescue show-environment suspend get-default
is-system-running preset-all'
[FILE]='link switch-root'
Expand Down
1 change: 1 addition & 0 deletions shell-completion/zsh/_systemctl.in
Expand Up @@ -18,6 +18,7 @@
"force-reload:Reload one or more units if possible, otherwise restart if active"
"hibernate:Hibernate the system"
"hybrid-sleep:Hibernate and suspend the system"
"suspend-to-hibernate:Suspend the system for a period of time, and then hibernate it"
"try-reload-or-restart:Reload one or more units if possible, otherwise restart if active"
"isolate:Start one unit and stop all others"
"kill:Send signal to processes of a unit"
Expand Down
1 change: 1 addition & 0 deletions src/basic/special.h
Expand Up @@ -37,6 +37,7 @@
#define SPECIAL_SUSPEND_TARGET "suspend.target"
#define SPECIAL_HIBERNATE_TARGET "hibernate.target"
#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target"
#define SPECIAL_SUSPEND_TO_HIBERNATE_TARGET "suspend-to-hibernate.target"

/* Special boot targets */
#define SPECIAL_RESCUE_TARGET "rescue.target"
Expand Down
13 changes: 10 additions & 3 deletions src/login/logind-action.c
Expand Up @@ -47,7 +47,8 @@ int manager_handle_action(
[HANDLE_KEXEC] = "Rebooting via kexec...",
[HANDLE_SUSPEND] = "Suspending...",
[HANDLE_HIBERNATE] = "Hibernating...",
[HANDLE_HYBRID_SLEEP] = "Hibernating and suspending..."
[HANDLE_HYBRID_SLEEP] = "Hibernating and suspending...",
[HANDLE_SUSPEND_TO_HIBERNATE] = "Suspending to hibernate...",
};

static const char * const target_table[_HANDLE_ACTION_MAX] = {
Expand All @@ -57,7 +58,8 @@ int manager_handle_action(
[HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET,
[HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET,
[HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET,
[HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET
[HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET,
[HANDLE_SUSPEND_TO_HIBERNATE] = SPECIAL_SUSPEND_TO_HIBERNATE_TARGET,
};

_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
Expand Down Expand Up @@ -110,6 +112,8 @@ int manager_handle_action(
supported = can_sleep("hibernate") > 0;
else if (handle == HANDLE_HYBRID_SLEEP)
supported = can_sleep("hybrid-sleep") > 0;
else if (handle == HANDLE_SUSPEND_TO_HIBERNATE)
supported = can_sleep("suspend-to-hibernate") > 0;
else if (handle == HANDLE_KEXEC)
supported = access(KEXEC, X_OK) >= 0;
else
Expand All @@ -125,7 +129,9 @@ int manager_handle_action(
return -EALREADY;
}

inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;
inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,
HANDLE_SUSPEND_TO_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN;

/* If the actual operation is inhibited, warn and fail */
if (!ignore_inhibited &&
Expand Down Expand Up @@ -172,6 +178,7 @@ static const char* const handle_action_table[_HANDLE_ACTION_MAX] = {
[HANDLE_SUSPEND] = "suspend",
[HANDLE_HIBERNATE] = "hibernate",
[HANDLE_HYBRID_SLEEP] = "hybrid-sleep",
[HANDLE_SUSPEND_TO_HIBERNATE] = "suspend-to-hibernate",
[HANDLE_LOCK] = "lock"
};

Expand Down
1 change: 1 addition & 0 deletions src/login/logind-action.h
Expand Up @@ -29,6 +29,7 @@ typedef enum HandleAction {
HANDLE_SUSPEND,
HANDLE_HIBERNATE,
HANDLE_HYBRID_SLEEP,
HANDLE_SUSPEND_TO_HIBERNATE,
HANDLE_LOCK,
_HANDLE_ACTION_MAX,
_HANDLE_ACTION_INVALID = -1
Expand Down
29 changes: 29 additions & 0 deletions src/login/logind-dbus.c
Expand Up @@ -1933,6 +1933,20 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e
error);
}

static int method_suspend_to_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;

return method_do_shutdown_or_sleep(
m, message,
SPECIAL_SUSPEND_TO_HIBERNATE_TARGET,
INHIBIT_SLEEP,
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"hybrid-sleep",
error);
}

static int nologin_timeout_handler(
sd_event_source *s,
uint64_t usec,
Expand Down Expand Up @@ -2386,6 +2400,19 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b
error);
}

static int method_can_suspend_to_hibernate(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;

return method_can_shutdown_or_sleep(
m, message,
INHIBIT_SLEEP,
"org.freedesktop.login1.hibernate",
"org.freedesktop.login1.hibernate-multiple-sessions",
"org.freedesktop.login1.hibernate-ignore-inhibit",
"suspend-to-hibernate",
error);
}

static int property_get_reboot_to_firmware_setup(
sd_bus *bus,
const char *path,
Expand Down Expand Up @@ -2706,12 +2733,14 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SuspendToHibernate", "b", NULL, method_suspend_to_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CanSuspendToHibernate", NULL, "s", method_can_suspend_to_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
Expand Down
8 changes: 8 additions & 0 deletions src/login/org.freedesktop.login1.conf
Expand Up @@ -150,6 +150,10 @@
send_interface="org.freedesktop.login1.Manager"
send_member="HybridSleep"/>

<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="SuspendToHibernate"/>

<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="CanPowerOff"/>
Expand All @@ -174,6 +178,10 @@
send_interface="org.freedesktop.login1.Manager"
send_member="CanHybridSleep"/>

<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="CanSuspendToHibernate"/>

<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.Manager"
send_member="ScheduleShutdown"/>
Expand Down

0 comments on commit c58493c

Please sign in to comment.