Increasing Battery Life on an Arch Linux Laptop (ThinkPad T14s)

This week I received the latest 14in laptop from Lenovo, the T14s (AMD gen1). Below are some of the tweaks / setup I’ve done on my machine (and generally all my laptops) which typically increase the battery life (with no noticeable reduction in performance).

The specs are as follows (Lenovo Thinkpad T14s):

  • CPU – 8 Core, 16 Thread – AMD Ryzen 7 Pro 4750u
  • GPU – AMD Renoir
  • DISK – 2Tb – M.2 NVME SSD – 970 EVO Plus
  • RAM – 32 Gb
  • DISPLAY – 1080p low power (ISP)
  • KEYBOARD – backlit English

As all my machines run on Arch Linux (with Gnome as the GUI), I went ahead and installed the latest build. It worked out of the box, no issues, was up in about an hour.

However, it does requires quite a few optimizations to get power consumption down on a laptop. The goal is always to maximize battery life, while enabling all the functions you’d normally do on a machine.

BIOS

On any new laptop, it’s often possible to disable features you don’t need in the BIOS. For instance, any 3rd party software and unused components (such as fingerprint scanner), Bluetooth, etc. can switched off. Often, this will power down those components and save battery.

In this case, I turned off the following components (it’s possible to turn them back on, if needed):

  • Ethernet LAN
  • USB Port
  • Memory Card Slot
  • Smart Card Slot
  • Fingerprint Reader

There is also some 3rd party software which apparently you can also uninstall:

That’s it! It’s possible to later turn on components if necessary. This should result in some minor reductions in power draw.

TLP or PowerTop

On the software side, the first step is to install PowerTop. This lets you manage and monitor almost everything you’ll need related to power consumption: turning off components, increasing system wakes, etc.

This can get rather complex, but for brevity: start with calibrating PowerTop. I should note, this will run several measurements, changing screen brightness, fan speed, etc. and will take some time (few minutes), so be prepared:

sudo powertop --calibrate

Launch powertop with sudo:

sudo powertop

Tab over to “Tunables” and turn on / off various recommendations. This can often result in 5-25% gains in battery life. To set all recommendations to “good”, utilize auto-tune:

sudo powertop --auto-tune

This will automatically run all the possible recommendations.

Finally, if you’d like these changes to be persistent (aka not run the command every time you startup the laptop), it’s recommended you create a service:

/etc/systemd/system/powertop.service

Then enable the service:

sudo systemctl enable powertop.service

This will automatically tune your computer to improve power utilization on boot (although it’ll slow the boot process 1-3 seconds).

It is also important to note that there is TLP, which also offers a similar service to PowerTop. There are some noticeable differences with TLP. TLP for instance, doesn’t shutdown all connected devices (i.e. PCI devices).

I personally chose TLP, but many other swear by PowerTop.

TLP requires a far more minimal setup:

sudo systemctl enable tlp.service

Suspend vs Hibernate

The next improvement that can dramatically improve the time between charges is modifying the suspend behavior.

Don’t know what suspend is?

Suspend to RAM – Keep machine state in RAM. This requires keeping the RAM powered, but power is cut to other components of the system (keyboard, monitor, etc.)

Essentially, when a laptop lid opens, it instantly boots, often before the laptop is fully ajar. This lets you get “right to work”, but that requires the RAM to be constantly powered.

There is an alternative method, often called Suspend to Disk or Hibernation

Hibernation (Suspend to Disk) – Saves the machine state into the swap space on disk and completely powers off the machine. When the lid is opened / machine is powered on, the machine state is loaded into RAM.

The hibernation method can save power for very long periods, as it completely powers down the machine. The drawback is that (at least on my machine) it can take 10-30 seconds to boot into the prior state.

In addition, a swap file or partition is required. Note, it is recommend that the swap space is 2-3x than the available RAM.

The beauty, is it’s possible to marry the two methods called: suspend-then-hibernate. It’s possible to suspend for a duration of time (say one hour), then if the laptop isn’t brought out of suspend it will hibernate.

Suspend then Hibernate – Keep the machine state in RAM for a duration of time, then transition to Hibernation on disk.

I should note, it’s also possible to do both suspend and hibernate at the same time, in the case of power loss.

Suspend and Hibernate – The machine state is copied to the swap space and continues to be stored in RAM. The RAM will continue to draw power, but if power is lost the machine state can be recovered.

Closing the Lid

The most effective power saving method (as discussed above) is to hibernate, every time.

However, it can be rather annoying to close a laptop, realize you need something, then reopen it and waiting 30 seconds for it to boot.

Is a 30 second delay in booting your laptop worth the power savings?

Honestly, most people should say it’s a worthy trade. Thirty seconds is a negligible amount of time, given we often open our laptops for hours.

That being said, there’s something primal about being able to open a laptop and being “ready-to-rock” in less than a few seconds. With that in mind, I used the Suspend-then-Hibernate configuration. The suspend-then-hibernate behavior will let the computer quickly boot for some specified amount of time (say 2 hours), after which it will enter the hibernation state.

Hibernate Configuration

First, to configure the hibernate behavior, simply edit the following files.

To get hibernate configured, inform GRUB where and how to swap. I personally recommend a swap partition over a swap file as the swap file appears to cause issues in some machines[1].

To accomplish this, find the swap partition, and add the UUID to the GRUB_CMDLINE_LINUX_DEFAULT.

$ sudo lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme0n1     259:0    0  1.8T  0 disk 
├─nvme0n1p1 259:1    0    1G  0 part  
├─nvme0n1p2 259:2    0   64G  0 part [SWAP]
└─nvme0n1p3 259:3    0  1.8T  0 part /
$ sudo lsblk -f | grep SWAP
├─nvme0n1p2 swap   1 <UUID>  [SWAP]

Add the resume=UUID=<PARTITION UUID> to the following file:

/etc/default/grub

From there, you can start hibernating by adding the MAJ:MIN values for the swap partition to /sys/power/resume with the following command[2]:

sudo echo 259:2 > /sys/power/resume

It should then be possible to hibernate.

To make the changes to GRUB on boot, execute the following:

sudo grub-mkconfig -o /boot/grub/grub.cfg

At this point, a reboot is recommended.

Suspend-Then-Hibernate Configuration

Once it’s possible to enter & wake from the hibernation state, we can then transition to the suspend-then-hibernate step. For this, we have to configure systemd to handle entering hibernation from the suspend state after a given period.

There are two edits here:

  1. Set a delay in the sleep.conf, which will inform systemd how long to stay in the suspend state before hibernating.
  2. Inform systemd the behavior of the power switch, lid closing, and suspend state.

See the file edits below:

/etc/systemd/sleep.conf
/etc/systemd/logind.conf

The above enables closing the lid or clicking power button to trigger a suspend-then-hibernate state. For the first 45 minutes (in this case), the machine state will be kept in RAM. After 45 minutes (in this case), the machine state will write to disk and power down.

When the lid is next opened, if the machine is in the suspend state (<45 minutes), logging in will take a second or two. If the machine is in the hibernation state, the system will power up, load the machine state into RAM, then let you login (~25-30 seconds).

Quickly Recovering from Hibernation

One quick way to speed up recovering from hibernation is to reduce the time the grub menu is up. To edit this, go to:

/etc/default/grub

And edit the following command, I changed it from GRUB_TIMEOUT=5 to:

GRUB_TIMEOUT=1

This saves an addition 4 seconds on boot, although this does mean you’ll have to act fast if you need to utilize the grub menu. After that, you’ll have to execute the following command for it to take effect:

sudo grub-mkconfig -o /boot/grub/grub.cfg

Beyond speeding up GRUB, you can run the following command to analyze the boot up process via systemd-analyze:

$ sudo systemd-analyze
Startup finished in 5.566s (firmware) + 2.088s (loader) 
+ 1.010s (kernel) + 9.702s (userspace) = 18.368s 
graphical.target reached after 9.614s in userspace

To get a more detailed list use the blame or critical-chain argument:

$ sudo systemd-analyze critical-chain
The time when unit became active or started is printed after the "@" character.
The time the unit took to start is printed after the "+" character.

graphical.target @9.614s
└─multi-user.target @9.613s
  └─autofs.service @9.577s +34ms
    └─remote-fs.target @9.575s
      └─mnt-archive.mount @9.040s +534ms
        └─network-online.target @9.037s
          └─NetworkManager-wait-online.service @920ms +8.116s
            └─NetworkManager.service @880ms +38ms
              └─dbus.service @877ms
                └─basic.target @876ms
                  └─sockets.target @876ms
                    └─dbus.socket @876ms
                      └─sysinit.target @874ms
                        └─[email protected]:amdgpu_bl0.service @2.609s +12ms
                          └─system-systemd\x2dbacklight.slice @893ms
                            └─system.slice @177ms
                              └─-.slice @177ms

In this case, it appears the network manager is the real bottleneck. This is a known issue, and can be addressed by disabling the NetworkManager-wait-onine.service. Although, it should be noted this could cause your system to fail to boot, if the network daemons require internet connection (highly unadvised to use such systems on a laptop).

To disable, it is possible (but risky) execute the following:

sudo systemctl disable --now NetworkManager-wait-online.service

From there, we should see a very quick boot time — if it works. Unfortunately, disabling this service can lead to other systems failing and can cause a failure to boot. Basically, some services will need the network up, if those services don’t wait for the NetworkManager to boot, then the service will fail.

Boot Optimizations

Below are the steps I particularly took to reduce the boot time by around 90%.

Switched to autofs (NFS solution):

Increase battery life:

  • Utilize the TLP, service
  • Alternatively, it’s possible to use Powertop

I also disabled the NetworkManager-wait-online.service (reducing boot by 6 seconds).

sudo systemctl disable --now NetworkManager-wait-online.service

After the above, the updated boot time is down 88%, nine seconds to one second:

$ sudo systemd-analyze critical-chain

graphical.target @1.152s
└─multi-user.target @1.152s
  └─systemd-logind.service @922ms +225ms
    └─basic.target @914ms
      └─sockets.target @913ms
        └─dbus.socket @913ms
          └─sysinit.target @906ms
            └─[email protected]:tpacpi::kbd_backlight.service @1.171s +14ms
              └─system-systemd\x2dbacklight.slice @1.170s
                └─system.slice @185ms
                  └─-.slice @185ms

A few final items which can provide some speedups:

Utilize CPU’s Random Function

Adding random.trust_cpu=on flag on boot, will improve the bootspeed. This flag tells your system to trust the CPU random number generator (vs the software solution). This shaves a second off system boot and likely poses minimal, if any, risk.

Switch initramfs Compression to lz4

Changing the compression on the initramfs to lz4 should provide some speed improvements (according to this presentation by LG). Just uncomment the COMPRESSION=”lz4″ line in the /etc/mkinitcpio.conf file. By default, gzip is used, but lz4 compression is quite a bit faster.

Notable Issues: GDM Failure

One rather frustrating issue has been a GDM (Gnome Display Manager) failure.

GDM hangs on boot/reboot/wake up with a black blinking cursor

The computer will reach all the way to the graphical.target state only to have no login window appear. It’s possible to do:

CTRL+ALT+F2 (move to tty2)
CTRL+ALT+F1 (return to tty1)

After which GDM would start up again. This appears to be an error with the service, in the logs you see the following:

$ journalctl -u gdm

systemd[1]: Starting GNOME Display Manager... 
systemd[1]: Started GNOME Display Manager. 
gdm[543]: Child process -622 was already dead. 
gdm-password][964]: gkr-pam: unable to locate daemon control file  
gdm-password][964]: gkr-pam: stashed password to try later in open session 
gdm-password][964]: pam_unix(gdm-password:session): session opened for user by (uid=0) 
gdm-password][964]: gkr-pam: gnome-keyring-daemon started properly and unlocked keyring

The fix for this is in the Arch wiki, GDM will freeze on systemd unless the service is edited:

$ systemctl edit gdm
[Service]
Type=idle

After which, the system should boot up without hanging.

Conclusion

I hope others found this guide useful, feel free to give any tips in the comment section.

Overall, these changes have just about doubled my computers effectively battery life – roughly 8 hours of decent use. At the same time, battery charge is preserved as the computer goes into hibernation after a short-ish suspend cycle and reboots in a few seconds. Personally, I am pleased with the results.

Related Content

4 thoughts on “Increasing Battery Life on an Arch Linux Laptop (ThinkPad T14s)

  1. I found this article incredibly useful for getting my new thinkpad t14 (sadly only the 6 core variant as I couldn’t see the 8 core when I went to order) running with arch. The only remaining issues I appear to be having is with suspend and resume which I need to do some more research into. 10/10 guide thank you.

    PS. have you had any success with getting the fingerprint sensor running?

Leave a Reply

Your email address will not be published.

 characters available

Time limit is exhausted. Please reload the CAPTCHA.