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.
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.
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.
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
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
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/
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
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.
We need to enable the service and then reboot the system:
$ systemctl enable suspend-to-hibernate $ systemctl is-enabled suspend-to-hibernate enabled
I'm sure you can just restart the whole of systemd some how. But, I just rebooted the whole system.
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
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.
[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 |