Merge pull request #32705 from YHNdnzj/hibernate-error

hibernate-util: differentiate some errors from the generic ENOSPC; systemctl: adjust the fallback behavior for sleep operations
This commit is contained in:
Luca Boccassi 2024-05-08 14:19:07 +02:00 committed by GitHub
commit 667fe27e5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 70 additions and 32 deletions

View File

@ -1843,6 +1843,10 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<para>Suspend the system. This will trigger activation of the special target unit
<filename>suspend.target</filename>. This command is asynchronous, and will return after the suspend
operation is successfully enqueued. It will not wait for the suspend/resume cycle to complete.</para>
<para>If <option>--force</option> is specified, and <command>systemd-logind</command> returned
error for the operation, the error will be ignored and the operation will be tried again directly
through starting the target unit.</para>
</listitem>
</varlistentry>
@ -1853,6 +1857,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<para>Hibernate the system. This will trigger activation of the special target unit
<filename>hibernate.target</filename>. This command is asynchronous, and will return after the hibernation
operation is successfully enqueued. It will not wait for the hibernate/thaw cycle to complete.</para>
<para>This command honors <option>--force</option> in the same way as <command>suspend</command>.</para>
</listitem>
</varlistentry>
@ -1864,6 +1870,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<filename>hybrid-sleep.target</filename>. This command is asynchronous, and will return after the hybrid
sleep operation is successfully enqueued. It will not wait for the sleep/wake-up cycle to complete.</para>
<para>This command honors <option>--force</option> in the same way as <command>suspend</command>.</para>
<xi:include href="version-info.xml" xpointer="v196"/>
</listitem>
</varlistentry>
@ -1872,11 +1880,14 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<term><command>suspend-then-hibernate</command></term>
<listitem>
<para>Suspend the system and hibernate it after the delay specified in <filename>systemd-sleep.conf</filename>.
This will trigger activation of the special target unit <filename>suspend-then-hibernate.target</filename>.
This command is asynchronous, and will return after the hybrid sleep operation is successfully enqueued.
<para>Suspend the system and hibernate it when the battery is low, or when the delay specified
in <filename>systemd-sleep.conf</filename> elapsed. This will trigger activation of the special
target unit <filename>suspend-then-hibernate.target</filename>. This command is asynchronous,
and will return after the hybrid sleep operation is successfully enqueued.
It will not wait for the sleep/wake-up or hibernate/thaw cycle to complete.</para>
<para>This command honors <option>--force</option> in the same way as <command>suspend</command>.</para>
<xi:include href="version-info.xml" xpointer="v240"/>
</listitem>
</varlistentry>
@ -2531,25 +2542,27 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<term><option>--force</option></term>
<listitem>
<para>When used with <command>enable</command>, overwrite
any existing conflicting symlinks.</para>
<para>When used with <command>enable</command>, overwrite any existing conflicting symlinks.</para>
<para>When used with <command>edit</command>, create all of the
specified units which do not already exist.</para>
<para>When used with <command>edit</command>, create all of the specified units which do not already exist.</para>
<para>When used with <command>halt</command>, <command>poweroff</command>, <command>reboot</command> or
<command>kexec</command>, execute the selected operation without shutting down all units. However, all
processes will be killed forcibly and all file systems are unmounted or remounted read-only. This is hence a
drastic but relatively safe option to request an immediate reboot. If <option>--force</option> is specified
twice for these operations (with the exception of <command>kexec</command>), they will be executed
immediately, without terminating any processes or unmounting any file systems.</para>
<para>When used with <command>suspend</command>, <command>hibernate</command>, <command>hybrid-sleep</command>,
or <command>suspend-then-hibernate</command>, the error returned by <command>systemd-logind</command>
will be ignored, and the operation will be performed directly through starting the corresponding units.
</para>
<para>When used with <command>halt</command>, <command>poweroff</command>, <command>reboot</command>,
or <command>kexec</command>, execute the selected operation without shutting down all units. However,
all processes will be killed forcibly and all file systems are unmounted or remounted read-only.
This is hence a drastic but relatively safe option to request an immediate reboot. If <option>--force</option>
is specified twice for these operations (with the exception of <command>kexec</command>), they will
be executed immediately, without terminating any processes or unmounting any file systems.</para>
<warning>
<para>Specifying
<option>--force</option> twice with any of these operations might result in data loss. Note that when
<option>--force</option> is specified twice the selected operation is executed by
<command>systemctl</command> itself, and the system manager is not contacted. This means the command should
succeed even when the system manager has crashed.</para>
<para>Specifying <option>--force</option> twice with any of these operations might result in data loss.
Note that when <option>--force</option> is specified twice the selected operation is executed by
<command>systemctl</command> itself, and the system manager is not contacted. This means the command
should succeed even when the system manager has crashed.</para>
</warning>
</listitem>
</varlistentry>

View File

@ -2156,6 +2156,14 @@ static int method_do_shutdown_or_sleep(
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not running on EFI and resume= is not set, or noresume is set. No available method to resume from hibernation");
case SLEEP_RESUME_DEVICE_MISSING:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Specified resume device is missing or is not an active swap device");
case SLEEP_RESUME_MISCONFIGURED:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Invalid resume config: resume= is not populated yet resume_offset= is");
case SLEEP_NOT_ENOUGH_SWAP_SPACE:
return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED,
"Not enough suitable swap space for hibernation available on compatible block devices and file systems");

View File

@ -159,7 +159,7 @@ static int read_resume_config(dev_t *ret_devno, uint64_t *ret_offset) {
}
if (devno == 0 && offset > 0 && offset != UINT64_MAX)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM),
"Found populated /sys/power/resume_offset (%" PRIu64 ") but /sys/power/resume is not set, refusing.",
offset);
@ -394,7 +394,7 @@ int find_suitable_hibernation_device_full(HibernationDevice *ret_device, uint64_
if (!entry) {
/* No need to check n_swaps == 0, since it's rejected early */
assert(resume_config_devno > 0);
return log_debug_errno(SYNTHETIC_ERRNO(ENOSPC), "Cannot find swap entry corresponding to /sys/power/resume.");
return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find swap entry corresponding to /sys/power/resume.");
}
if (ret_device) {
@ -452,11 +452,11 @@ int hibernation_is_safe(void) {
bypass_space_check = getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0;
r = find_suitable_hibernation_device_full(NULL, &size, &used);
if (r == -ENOSPC && bypass_space_check)
/* If we don't have any available swap space at all, and SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK
* is set, skip all remaining checks since we can't do that properly anyway. It is quite
* possible that the user is using a setup similar to #30083. When we actually perform
* hibernation in sleep.c we'll check everything again. */
if (IN_SET(r, -ENOSPC, -ESTALE) && bypass_space_check)
/* If we don't have any available swap space at all, or the specified resume device is missing,
* and $SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK is set, skip all remaining checks since
* we can't do that properly anyway. It is quite possible that the user is using a setup
* similar to #30083. When we actually perform hibernation in sleep.c we'll check everything again. */
return 0;
if (r < 0)
return r;

View File

@ -368,16 +368,28 @@ static int sleep_supported_internal(
}
r = hibernation_is_safe();
if (r == -ENOTRECOVERABLE) {
switch (r) {
case -ENOTRECOVERABLE:
*ret_support = SLEEP_RESUME_NOT_SUPPORTED;
return false;
}
if (r == -ENOSPC) {
case -ESTALE:
*ret_support = SLEEP_RESUME_DEVICE_MISSING;
return false;
case -ENOMEDIUM:
*ret_support = SLEEP_RESUME_MISCONFIGURED;
return false;
case -ENOSPC:
*ret_support = SLEEP_NOT_ENOUGH_SWAP_SPACE;
return false;
}
default:
if (r < 0)
return r;
}
} else
assert(!sleep_config->modes[operation]);

View File

@ -59,6 +59,8 @@ typedef enum SleepSupport {
SLEEP_NOT_CONFIGURED, /* SleepConfig.states is not configured */
SLEEP_STATE_OR_MODE_NOT_SUPPORTED, /* SleepConfig.states/modes are not supported by kernel */
SLEEP_RESUME_NOT_SUPPORTED,
SLEEP_RESUME_DEVICE_MISSING, /* resume= is specified, but the device cannot be found in /proc/swaps */
SLEEP_RESUME_MISCONFIGURED, /* resume= is not set yet resume_offset= is configured */
SLEEP_NOT_ENOUGH_SWAP_SPACE,
SLEEP_ALARM_NOT_SUPPORTED, /* CLOCK_BOOTTIME_ALARM is unsupported by kernel (only used by s2h) */
} SleepSupport;

View File

@ -223,8 +223,11 @@ int verb_start_special(int argc, char *argv[], void *userdata) {
case ACTION_HYBRID_SLEEP:
case ACTION_SUSPEND_THEN_HIBERNATE:
/* For sleep operations, do not automatically fall back to low-level operation for
* errors other than logind not available. There's a high chance that logind did
* some extra sanity check and that didn't pass. */
r = logind_reboot(a);
if (r >= 0 || IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS))
if (r >= 0 || (r != -ENOSYS && arg_force == 0))
return r;
arg_no_block = true;