Where did earlysuspend go?

Over the last few months I've given some talks on the status of the Android Upstreaming work with the mainline kernel. As part of this status, I also cover a quick background on the various components of the android kernel patches.

The Android kernel patch set isn't a static thing, as the developers at Google continue to revise and rework their patchset, working with the community to integrate with upstream changes as well as adding new features that they've created.

In some cases, the Android developers have reworked their systems so that some features they originally needed were no longer necessary. I had a simple slide describing some of the Android kernel features that have been deprecated and removed from recent Android kernels (see slide 18 here: http://lca-13.zerista.com/files_user/attachments/9198/android-kernel-status.pdf):
    o pmem
    o earlysuspend
    o apanic
    o yaffs2

One of these items however, has caused much surprise and confusion: The removal of earlysuspend.

This isn't something that has happened recently, in fact, the android developers made public what would become their android-3.4 kernel branch over a year ago!  In fact, one Android developer warned earlysuspend would be going away back in early 2011 (http://permalink.gmane.org/gmane.linux.kernel/1117160)

But despite this, only a few Android devices have been released or upgraded to be based on the 3.4 based kernel. So as developers, who have enabled many drivers with earlysuspend  and who are familiar with the android patchset prior to 3.4 , start working with the newer kernels, they've found themselves at a loss to figure out what the removal of earlysuspend means to them.

So first some background: What was earlysuspend?

With normal suspend and resume, the system can be either on with all devices running, or the system is suspended, where devices are shutdown (hopefully saving power). Earlysuspend provided a bit more of a gradient of states between on and suspended.  The device may be still running, say playing some music, but the screen is off.  In this case, it may make sense to power-down parts of the device that aren't going to be in-use if the screen is off: like touch sensors, or gpus.  Earlysuspend basically provided a number of "levels of suspend" from 0 to ~200 (though there wasn't really a fixed max value other then maxint), marking notable "mile posts" along the way: BLANK_SCREEN (50), STOP_DRAWING (100), and DISABLE_FB(150).

There were also hooks, so the transition between these states could be monitored by things like surfaceflinger, which at the time had to react to the state of the screen and manage shutdown animations that play before the screen is turned off.

On the driver side,  drivers would then register earlysuspend and late resume hooks giving a specific level value.   And the driver suspend hooks will be called in that order as the device moves along the gradient between on and full suspend.  This allowed for devices to save power, suspending unnecessary devices, while still keeping important functionality, like audio playing and network connections alive.

But now, with Android systems using kernel version 3.4+, earlysuspend has been removed. And without much fanfare.


Why?

Earlysuspend was dropped as part of a large power-management rewrite, and you can read the full commit message for this change here:
https://android.googlesource.com/platform/frameworks/base/+/9630704ed3b265f008a8f64ec60a33cf9dcd3345

With this rewrite, the management of the system state has been consolidated into the PowerManagerService. Where as before, the power to the screen, the brightness, the animations, and if the machine could suspend were controlled by separate actions. Some were done as part of surfaceflinger, others managed by the PowerManagerService, and managing these separate actions required complicated coordination. With the rewrite, those actions are requested of the PowerManagerService, which handles all the state changes in a unified way via an internal state machine.

This consolidation of control allowed surfaceflinger to hand off its syncing with earlysuspend to the PowerManagerService, as seen in the following commit:
https://android.googlesource.com/platform/frameworks/native/+/8e533069e5721e55cb9768e140e16546c3a4a8b6%5E!/

Additionally, in the Android 3.4 (and upstream Linux 3.5) kernels, the autosleep feature has been added. This feature tries to repeatedly trigger suspend, whenever there are no wakelocks (wakelocks are requests for the system not to suspend) held. So by reworking the userland's management of the power-states, it allowed the system to then be able to use autosleep instead of the earlysuspend interface on systems that support it (while still preserving support for older earlysuspend enabled kernels at the same time).


What does all this mean for developers?

Ok, so Android's power-management code has been rewritten and improved, but old drivers that have earlysuspend hooks may no longer build against 3.4+ kernels. How do we update these drivers?

The quickest solution is to simply use the standard suspend/resume hooks for your driver. In fact, many drivers have done this for compatibility between upstream Linux kernels and Android patched kernels.  However, this won't help save power when the device is on but your driver isn't in use.  Since this is why you likely were using early_suspend in the first place, how do we recoup this extra power-savings?

The answer is two parts:

The first part is to utilize the upstream kernel's runtime_pm infrastructure. This infrastructure allows drivers to suspend their hardware whenever they're not being used. Its actually a little more flexible then earlysuspend, as earlysuspend provided only a linear order. So in a contrived example: if a mp3 decoder device's suspend hook was late in the earlysuspend order (after the framebuffer was off), it couldn't be suspended when mp3s weren't playing but the screen was on.  The runtime_pm infrastructure allows devices to be managed more independently where possible.

You can read more specific documentation on the kernel's runtime power-management here:
    http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/power/devices.txt
and here:
    http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/Documentation/power/runtime_pm.txt

If you're looking for some specific examples of runtime_suspend being used with Android, checkout some of these drivers from the exynos AOSP tree (used for the Nexus 10 device):
https://android.googlesource.com/kernel/exynos.git/+/android-exynos-manta-3.4-jb-mr1.1/drivers/media/video/exynos/fimc-is-mc/fimc-is-core.c
https://android.googlesource.com/kernel/exynos.git/+/android-exynos-manta-3.4-jb-mr1.1/drivers/media/video/exynos/gsc/gsc-core.c
https://android.googlesource.com/kernel/exynos.git/+/android-exynos-manta-3.4-jb-mr1.1/drivers/media/video/exynos/jpeg/jpeg_dev.c
https://android.googlesource.com/kernel/exynos.git/+/android-exynos-manta-3.4-jb-mr1.1/drivers/media/video/exynos/tv/hdmi_drv.c

That said, runtme_pm on its own isn't always completely sufficient to replace earlysuspend.  While runtime_pm can be used to suspend devices that are not in use, determining this "in-use" state is not always possible at the driver level.  In these cases the power_module of the userland HAL library can be used to explicitly manage devices from userland via their sysfs interfaces.

Thus, the second part is the setInteractive hook in the HAL power module. This hook allows actions to be taken from userland whenever the device switches from interactive to non-interactive mode (or stated more simply, if the screen is on or off).  Usually this hook is used to ramp up or down the cpu frequency, but it can also be used to turn on or off specific hardware on the device. An example use of this can be found here:
    https://android.googlesource.com/device/samsung/manta/+/master/power/power_manta.c


Many thanks to +Dave Hansen, +Karim Yaghmour, +Kevin Hilman , +Colin Cross and +Arve Hjønnevåg  for their review and help in creating this!
Shared publiclyView activity