2024-06-29

OpenBSD Power Management

 

OpenBSD's power management features are powerful and plenty. My current setup floats the battery at 80% charged, to reduce battery wear during the work week, and it doesn't suspend when I close the lid as long as it's plugged in. On battery power, it adjusts the CPU speed under load to optimize battery life without sacrificing performance when I need it, and it will automatically suspend at 5% battery to save me from the system powering off unexpectedly. When I plan to head out, I can use a quick alias to allow the battery to charge all the way, which takes about 20 minutes from 80%. We'll go through how I have it set up.

Long-time readers of HiR will not be surprised I'm running OpenBSD as my primary general-purpose operating system on my ThinkPad X1 Carbon. Over on Instagram, people do a double-take. One of the surprises was the fact that in a NeoFetch screenshot, it was noted that my CPU was 400 MHz because I was on battery power without anything heavy running. Power management on OpenBSD also blew some minds.

Out of the box, power management is disabled on OpenBSD. It's one of the few things that do not "just work" by default. The vast majority of OpenBSD systems do not need power management features, but it's a must for laptops.

Understanding apmd

apmd is the Advanced Power Management daemon. If you just set apmd to start with no options, on a well-supported laptop like the 8th Generation Lenovo ThinkPad X1 Carbon I'm using, your laptop will probably suspend when you close the lid or use "zzz" on the command line. And it will wake up when you open the lid or mess with the keyboard. apmd will also enable automatic performance adjustment mode -- clocking down the CPU when there's not much load.  That's a pretty good start. There will likely be no warning when your battery is close to dying, and there won't be anything there to stop it from just turning off abruptly when it hits 0%. That's not optimal. Also, when you close the laptop lid and it's plugged in, you might want the system to remain active. You can do that by tweaking machdep.lidaction with sysctl, but there's a much better way.

Looking at the manual page for apmd, we have a lot of useful options.

We can start apmd in high-performance mode (-H) to get the most processing power out of the system, low-performance mode (-L) to extend battery life at the expense of CPU speed, or force automatic performance adjustment mode (-A) which happens to be the default.

The -a option (lowercase) will block incoming BIOS suspend requests, such as those coming from closing the lid, if the system is plugged in. You can still manually suspend through your window manager or the command line zzz utility.

The -z [percent] option will automatically suspend the system if it is not plugged in and the battery is at or below the threshold percentage.

Enable and configure apmd. I assume that you've configured doas. I've covered this on several pages, like my OpenBSD webserver article. I explicitly set automatic performance mode (-A), blocking suspend when plugged in (-a), and set it to automatically suspend at 5% battery (-z 5). Feel free to change this however you please.

doas rcctl enable apmd
doas rcctl set apmd flags -A -a -z 5

The apm command line utility

Simply running the apm utility will provide battery and charge status

apm
Battery state: high, 79% remaining, 153 minutes life estimate
AC adapter state: not connected
Performance adjustment mode: auto (400 MHz)


There are a number of display flags that you can pass to APM to get specific details, for instance, apm -m will display the number of minutes of estimated battery life (or time to achieve a full charge). See the man page for apm for all the details. This is useful if you are making scripts to determine power management status such as for tmux/powerline or custom status bar scripts.

You can also adjust the performance mode on-the-fly, using apm -H, apm -L or apm -A to enable high performance, low-performance or automatic performance modes respectively, without restarting apmd.

sysctl, power management and sensors

This will dump out everything from the hw.sensors tree in sysctl:
sysctl hw.sensors

From here, we can see fan speeds, temperatures, the number of battery charge cycles and even things like the battery's factory design capacity and last fully-charged capacity in Watt-hours. 



By dividing last full capacity by the design capacity, you can see how far below the rated capacity your battery has deteriorated, in a way a measure of battery health. For example, my battery's design capacity is 51 Wh, but my last full charge was 39.76 Wh. 39.76 / 51 is about 0.78, so my "full" capacity is about 78% of what it was when new. That's not bad for a 4-year-old laptop on the original battery with about 500 discharge cycles.

Extending battery health with charging optimization

Many devices are now limiting the battery charge to about 80% when the system spends a lot of time plugged in. The MacBook my employer issued to me does this using some kind of magic algorithm that determines if it hasn't been running on battery power much lately. Leaving your battery slightly discharged like this actually extends the life of the battery substantially if you use it while plugged in most of the time.

OpenBSD can do the same thing, to an extent. The maximum charge level can be set with sysctl, so you can place the following line in /etc/sysctl.conf so that it's set immediately when booting up:

hw.battery.chargestop=80

Then, manually set it with sysctl, or reboot:

doas sysctl hw.battery.chargestop=80

If your battery is fully charged, it won't do anything, but the next time you run the battery down below 80% and plug it back in, it will stop charging the battery once it hits 80%. As far as I know, the battery will still charge to 100% if you turn the system off, though.

You can set or adjust the maximum charge level from the command line as well. For instance, if you know you're going to be on the go later today and want to actually charge the battery to 100%, running this command will take care of things:

doas sysctl hw.battery.chargestop=100

There are also additional hw.battery.chargemode options and an hw.battery.chargestart variable, for advanced use cases that I haven't needed. You can reference the hw.battery section of the detailed manual page for the sysctl API to read more about these settings.

Finally, I've been using XFCE lately, and the package "xfce4-battery" does a decent job, allowing me to place a battery widget in any of the XFCE panels. There may be additional widgets or plugins for your GUI of choice.