Hibernation and delayed hibernation for Ubuntu 16.04 LTS

Mac OSX has a fantastic capability that suspends when you shut the lid and then after a few minutes it runs hibernate (S4) so you're no longer using any power from the battery. We can set-up the same capability under Linux, this method works for Ubuntu 16.04 LTS.

The really nice part of this strategy is that we get the speed of instantaneous suspends, but the battery saving of hibernate. I set it so that it enters hibernation 15 minutes after the initial suspension. That way if I'm just moving quickly between rooms I can be back to work immediately, but if I'm travelling for a while the system will hibernate and keep all it's battery!

There's a great post [1] that I've used in the past (14.04 LTS) to set this up which calls it 'hybrid suspend'. Systemd built in some capabilities and calls 'hybrid suspend' a strategy where we both suspend and hibernate at the same time - then if the power is removed suddenly (e.g. a power failure) we can use the hibernated image. To avoid confusion it's better to call the strategy I'm using delayed hibernation.

With a delayed hibernate we're initially putting the system to sleep, waiting 15 minutes, reawakening so that it can hibernate.

As everything has changed to systemd in Xenial we'll do it in two stages. First getting hibernate working, and then setting up a suspend-to-hibernate service systemd.

Setting up hibernate

The first step is to test that hibernate works. Check with this command:

$ sudo pm-hibernate

This will shut the computer down so make sure all work is saved. For an initial test I shut all application and turn-off as many devices as possible. For me that means taking my laptop off the base-station, disconnecting the monitor, removing my USB camera and even shutting off WiFi and Bluetooth to get the smallest possible hardware requirement first:

$ bluetooth off
$ wifi off

If it doesn't work look at /var/log/pm-suspend.log. We're looking for lines like this:

Sat 10 Dec 17:08:12 GMT 2016: Running hooks for hibernate.
Running hook /usr/lib/pm-utils/sleep.d/000kernel-change hibernate hibernate:
/usr/lib/pm-utils/sleep.d/000kernel-change hibernate hibernate: success.

[lots of output with - 'success' at the end of each one]

Initially, this wasn't working for me. The log said everything was working - it was showing success. But, when I started the system back up it would go through the whole boot process and wouldn't resume.

Ultimately, the difficult nature of hibernate is why Ubuntu turned it off by default. So if you're having problems the only thing to do is to look at the Ubuntu Wiki [2], Google and look through bugs which have similarity with your problems.

Common issues I found for 16.04 that were connected to resuming were to do with the device that the system boots. There's some information on the Web about putting the right UUID in /etc/initramfs-tools/conf.d/resume works. This didn't fix it for me personally.

But, on the Ubuntu Wiki there were some notes on how to change /etc/default/grub to directly provide the right switch on boot:

$ sudo vim /etc/default/grub

GRUB_CMDLINE_LINUX_DEFAULT="nosplash resume=/dev/vgUbuntu/swap-ubuntu-ssd-lv""

$ sudo update-grub2

In /var/log/syslog search for:

ACPI: Preparing to enter system sleep state S4

And then for waking up you should see:

ACPI: Waking up from system sleep state S4

Eventually, I got this from /var/log/pm-suspend.log:

Sat 10 Dec 17:08:13 GMT 2016: performing hibernate
Sat 10 Dec 17:08:58 GMT 2016: Awake.
Sat 10 Dec 17:08:58 GMT 2016: Running hooks for thaw
[Lot of output saying 'success']

The Ubuntu Wiki has good information on troubleshooting and some alternative suspend mechanisms to try.

Delayed hibernation

With hibernation working we can move onto setting up the mechanism to do it automatically after a time delay.

The switch to systemd (since the previous 14.04 LTS) means the way that suspend and resume is managed has changed fundamentally. Instead of using pm-utils it's now managed by systemd-sleep. This means that scripts in /etc/pm/ aren't processed any longer. Instead we have to create a new systemd service.

  • Create a service file

    The first step is to create a suspend-to-hibernate.service file in /etc/systemd/system:

    $ sudo touch /etc/systemd/system/suspend-to-hibernate.service
    $ sudo bash -i
    $ vim /etc/systemd/system/suspend-to-hibernate.service
    
  • Copy the service file in

    The service file comes from the Arch Wiki, the only thing I had to change was the location of bash which on Ubuntu is /bin/bash. The SLEEPLENGTH variable is set to 2 hours in the original, it's 15 minutes in mine - it can be any length of time that the date command can process.

[Unit]
Description=Delayed hibernation trigger
Documentation=https://bbs.archlinux.org/viewtopic.php?pid=1420279#p1420279
Documentation=https://wiki.archlinux.org/index.php/Power_management
Before=suspend.target
Conflicts=hibernate.target hybrid-suspend.target
StopWhenUnneeded=true

[Service]
Type=oneshot
RemainAfterExit=yes
Environment="WAKEALARM=/sys/class/rtc/rtc0/wakealarm"
Environment="SLEEPLENGTH=+15minute"
ExecStart=-/bin/bash -c 'echo -n "alarm set for "; date +%%s -d$SLEEPLENGTH | tee $WAKEALARM'
ExecStop=-/bin/bash -c '\
  alarm=$(cat $WAKEALARM); \
  now=$(date +%%s); \
  if [ -z "$alarm" ] || [ "$now" -ge "$alarm" ]; then \
     echo "hibernate triggered"; \
     systemctl hibernate; \
  else \
     echo "normal wakeup"; \
  fi; \
  echo 0 > $WAKEALARM; \
'

[Install]
WantedBy=sleep.target
  • Alter the suspend target service

    The suspend target has to co-operate with our new service. To avoid messing around with the /lib area on Ubuntu which is managed by the packaging system we copy the service across to the /etc/systemd/system area and alter it there.

    $ sudo cp /lib/systemd/system/suspend.target /etc/systemd/system/
    
  • Alter the suspend.target

    We alter the suspend.target file to tell it that it requires the new suspend-to-hibernate.service that we just created. This is done by adding a new Requires line at the bottom, so the final contents of the /etc/systemd/system file:

[Unit]
Description=Suspend
Documentation=man:systemd.special(7)
DefaultDependencies=no
BindsTo=systemd-suspend.service
After=systemd-suspend.service
Requires=suspend-to-hibernate.service
  • Check systemd can see the service

    We have to check that systemd can actually see the new service and enable it:

    $ sudo systemctl list-unit-files | grep suspend
    

It should say that suspend-to-hibernate.service is disabled.

  • Enable the new suspend-to-hibernate service

    We need to enable the service and then reboot the system:

    $ systemctl enable suspend-to-hibernate
    $ systemctl is-enabled suspend-to-hibernate
    enabled
    
  • Reboot the system

    I'm sure you can just restart the whole of systemd some how. But, I just rebooted the whole system.

  • Test by suspending

    When testing it's useful to set the time period for the service to a minute or so! After the trigger hits it will come back from being suspended (sleep) and then it should immediately write-out so that it enters hibernate (s4). After we resume it's useful to search through the logs:

    $ journalctl
    

In the journalctl you should see something like this:

Dec 10 21:27:42 lenovosg systemd[1]: Starting Suspend...
Dec 10 21:27:42 lenovosg bash[6023]: alarm set for 1481405322
Dec 10 21:27:42 lenovosg systemd-sleep[6025]: Failed to connect to non-global ctrl_ifname: (nil)  error: No such file or directory
Dec 10 21:27:42 lenovosg systemd[1]: Started Session c6 of user steve.
Dec 10 21:27:42 lenovosg systemd-logind[1420]: New session c6 of user steve.
Dec 10 21:27:42 lenovosg systemd-sleep[6030]: /lib/systemd/system-sleep/wpasupplicant failed with error code 255.
Dec 10 21:27:42 lenovosg systemd-sleep[6025]: Suspending system...
Dec 10 21:27:42 lenovosg systemd[1]: Started Delayed hibernation trigger.

[... output skipped as it boots back up ...]

Dec 10 21:28:44 lenovosg bash[6179]: hibernate triggered
Dec 10 21:28:44 lenovosg systemd[1]: Stopped Delayed hibernation trigger.

[... more output skipped ...]

Dec 10 21:29:37 lenovosg kernel: ACPI: Preparing to enter system sleep state S4
  • Confirm the services are set-up properly

    If we examine the services we should see that they are running the ones that are defined in /etc/systemd/system:

    $ systemctl status suspend-to-hibernate
    
    suspend-to-hibernate.service - Delayed hibernation trigger
        Loaded: loaded (/etc/systemd/system/suspend-to-hibernate.service; enabled; vendor preset: enabled)
        Active: inactive (dead)
        Docs: https://bbs.archlinux.org/viewtopic.php?pid=1420279#p1420279
        https://wiki.archlinux.org/index.php/Power_management
    
        [... log of when it was called ...]
    
    $ systemctl status suspend.target
    
    suspend.target - Suspend
        Loaded: loaded (/etc/systemd/system/suspend.target; static; vendor preset: enabled)
        Active: inactive (dead)
        Docs: man:systemd.special(7)
    
        [... log of when it was called ...]
    

That's it we're all done!

Please note I'm not an expert on Linux hibernation, so if you're having problems I won't be able to help you - sorry! Try asking at your distributions help locations.

Delayed hibernation resources

[1]Daniel Haler's post on using pm-utils to set up hibernate Use hybrid suspend method by default with pm-utils/Linux (suspend to RAM and disk)
[2]Ubuntu Wiki information is a good overview and has some good troubleshooting information in PowerManagement/Hibernate and I found Understanding Suspend

Posted in tech Saturday 10 December 2016
Tagged with ubuntu linux