systemd / systemd

The systemd System and Service Manager
https://systemd.io
GNU General Public License v2.0
13.04k stars 3.73k forks source link

Allow setting battery drain threshold for sleep-then-hibernate #32420

Open 3flex opened 4 months ago

3flex commented 4 months ago

Component

No response

Is your feature request related to a problem? Please describe

Device with battery using suspend-then-hibernate should stay asleep without polling battery state. Waking the system creates unnecessary power drain when better options exist.

Switching to hibernate should also not depend on either timers, or remaining battery percentage dropping to 5%. Though waiting until 5% remaining avoids data loss it does not allow the machine to run for very long when it's started up again if the user is not near a power source and may be insufficient to even hibernate if it's an older device with an older battery.

Describe the solution you'd like

Device with battery and ACPI _BTP (battery trip point) support should allow suspend-then-hibernate to switch to hibernate only when battery drain exceeds a specified threshold (default 5%) or drops below a threshold where the use time remaining is insufficient.

Modern Standby on Windows requires this behaviour and does not user any timers or polling to determine when to switch to hibernate mode. They specifically list the benefit of "Eliminate resuming to a dead battery" which the systemd implementation does not (sure there should be 5% left but that's near enough to dead).

There are two ways to determine when to switch from suspend to hibernate:

  1. Battery drain exceeds set threshold
  2. Standby reserve time. This is the amount of time that's guaranteed to be available to the user after resuming. This avoids the issue where having 5% battery on a device with an old battery might not give a reasonable amount of time when starting (it might only have enough power to boot then stay up for a couple of minutes). This default is 1200 seconds/20 minutes. The algorithm for this is not published but I suspect it would be based on "Battery present rate" which is Windows-specific requirement to report to provide in _BST method and indicates current rate of drain in mW and "Battery remaining capacity" which again is not in ACPI spec but "Provides the remaining battery capacity in milliwatt-hours." That allows calculation of the time remaining, but should also factor in the power required to hibernate, which is available in _BIX as "Design Capacity of Low" (part of ACPI spec).

These are all required on modern Windows machines, but it would still require a fall back to the timer method in case required ACPI methods & data are not available.

The relevant value can be calculated, then _BTP will be called to set the battery trip point, which will send notify event through ACPI so the system can take action and hibernate.

Using this kind of implementation means there can be sane defaults that are not time-based and will:

  1. Not drain the battery too much while in suspend mode/S0ix
  2. In a low battery state will still provide enough power to use the laptop for a reasonable amount of time to save any work in progress before shutting down or hibernating.

Describe alternatives you've considered

Using the existing methods which have issues with polling or setting arbitrary values that are system-specific instead of using the data & features available in firmware to provide the best possible user experience.

The systemd version you checked that didn't have the feature you are asking for

No response

poettering commented 4 months ago

I am not sure what you are requesting? we do use _BTP already. What else do you want?

3flex commented 4 months ago

The system will switch to hibernate if either:

  1. Battery level drops to 5% or less
  2. After timer threshold is reached

I would like:

  1. In a high battery level scenario: Not wait until the battery drains to critical level before laptop switches to hibernate. Current behaviour avoids data loss but can cause large battery drain while sleeping which is unfortunately quite common with s0ix on Linux. Windows waits until the battery has drained 5% (configurable) so you can start with 100% battery, sleep, then it will hibernate with 95% battery left so there's plenty of productive use left. systemd lets the battery almost drain to 0 before sleeping. It saves data but that's all - and if booting from hibernate fails due to limited battery then data loss can still occur if it dies before the next graceful shutdown.
  2. In a low battery level scenario: Hibernate while there's enough battery for some level of productive use left. On Windows it ensures system hibernates with enough battery for at least 20 minutes (configurable) productive time left allowing for properly saving work or reaching a power outlet.

_BTP is a way to implement this as it allows setting the alarm threshold - I had a quick look at source, and systemd doesn't seem to set the alarm at all.

YHNdnzj commented 4 months ago

1) is a duplicate of https://github.com/systemd/systemd/issues/27839

3flex commented 4 months ago

Thanks, I didn't see that - it's similar but not the same, as that issue asks that a static threshold of capacity remaining is set e.g. always hibernate if capacity remaining reaches or is below 50%, while I'm asking for configurable battery drain amount to trigger hibernate when there's a lot of battery remaining, while also ensuring there's at least enough battery left for a certain amount of usage time.