0

My laptop doesn't send ACPI discharge events reliably, so I created a Systemd timer and service to periodically poll the battery level and decide if the computer should hibernate. However, some random amount of time after boot (usually within an hour or so), Systemd starts using about 90% CPU, and continues to do so until I systemctl stop the timer. Specifically the processes are (by CPU usage)

~90%: /usr/lib/systemd/systemd --switched-root --system --deserialize 32
~80%: /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
~20%: /usr/lib/systemd/systemd-logind

These all go back to near zero when I stop the timer. The relevant files are below. I'm running Arch Linux, Systemd version 235.8-1. It's worth noting that this problem happens even when connected to wall power, when hibernate-if-low-battery shouldn't even be running.

auto-hibernate.timer

[Unit]
Description=Check battery level periodically and hibernate when low

[Timer]
OnBootSec=30s
OnUnitActiveSec=30s

[Install]
WantedBy=timers.target

auto-hibernate.service

[Unit]
Description=Check battery level and hibernate if low
ConditionACPower=false

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hibernate-if-low-battery

hibernate-if-low-battery

#!/usr/bin/env bash

# Configuration.
BATTERY_PATH=/sys/class/power_supply/BAT0
CRITICAL_BATTERY_PERCENTAGE=5

# Calculate (the floor of) the battery percentage.
current_battery_level=$(< ${BATTERY_PATH}/energy_now)
max_battery_level=$(< ${BATTERY_PATH}/energy_full)
current_battery_percentage=$(((current_battery_level * 100)/max_battery_level))

if ((current_battery_percentage <= critical_battery_percentage)); then
    logger 'Hibernating due to low battery.'
    systemctl hibernate
fi

1 Answer 1

0

This appears to be a systemd bug caused by using a timer to trigger a service that uses ConditionACPower; see https://github.com/systemd/systemd/issues/5969. Moving the check for AC power into the hibernate-if-low-battery script should fix the problem. Specifically, this works:

auto-hibernate.service

[Unit]
Description=Check battery level and hibernate if low

[Service]
Type=oneshot
ExecStart=/usr/local/bin/hibernate-if-low-battery

hibernate-if-low-battery

#!/usr/bin/env bash

# Configuration.
BATTERY_PATH=/sys/class/power_supply/BAT0
AC_PATH=/sys/class/power_supply/AC
CRITICAL_BATTERY_PERCENTAGE=5

# Get AC power status.
ac_power_active=$(< "${AC_PATH}/online")

# Calculate (the floor of) the battery percentage.
current_battery_level=$(< "${BATTERY_PATH}/energy_now")
max_battery_level=$(< "${BATTERY_PATH}/energy_full")
current_battery_percentage=$(((current_battery_level * 100)/max_battery_level))
battery_level_critical=$((current_battery_percentage <= critical_battery_percentage))

if (( ! ac_power_active && battery_level_critical )); then
    logger 'Hibernating due to low battery.'
    systemctl hibernate
fi

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .