Thursday, 26 July 2012

Short changelog this time, the main improvement is IDLE2 v0.210 which implements the DEEP-IDLE TOP ON state for use when bluetooth or GPS is active. This should hopefully bring some power savings in those situations, but your mileage may vary.

I also implemented a load of micro-optimisations of variables and functions in the idle2 code to hopefully speed up the hot paths even more and make them cheaper.

Changelog:

Interactive governor boost enabled and set as default

IDLE2 v0.210

Version provided for i9023 users which contains the patch from codeworkx which should fix the CRT off animation

Download:

Update: Please use –24 instead, –23 has been removed due to a bug which is fixed in –24 which is available here.

I will write a blog post shortly detailing the bug and changes in –24.

So, I have spent a lot of time recently thinking about how best to implement IDLE2.

I decided to do something which hadn’t been done before, and implement both IDLE states so they are available concurrently without having to ‘switch’ between them, as was being done previously.

I implemented 2 idle states, IDLE (which was already implemented) and IDLE2, which is better described as DEEP IDLE TOP OFF, referred to as IDLE2 mode from hereon in.

I spent a lot of time making the hot paths as cheap as possible, wherever possible using variables for storage and checking them instead of polling (expensive) functions. This has paid off with a doubling of the number of times the CPU enters IDLE2 mode.

I also implemented PM notifiers to disable IDLE2 mode when suspend is active instead of hooking into the suspend sequence. However, due to the implementation, IDLE2 mode should never be enabled when the device is suspending, so it is just a failsafe.

Bluetooth caused me a massive headache, and although I have managed to partially solve the issue, I’m not entirely happy with the fix. Due to the way bluetooth works in ICS/JB, it is permanently enabled. This causes an issue, as IDLE2 mode is incompatible with bluetooth, enabling IDLE2 mode whilst the bluetooth is actively working breaks the link. The fix for this is a bit of a fudge. I added a hook to herring-rfkill to call a function when the GPIO_BT_HOST_WAKE goes high. This sets the external_active variable to true in cpuidle.c, which tells cpuidle to ignore the IDLE2 mode. When the gpio goes low again, a function is called which sets external_active to false, but it queues it for 60 seconds time. If another interrupt occurs, the queued inactive work will be cancelled and so on. This should work for the majority of bluetooth use cases. If the devices don’t ping each other every minute, I would be surprised. But, if this doesn’t work for you, IDLE2 mode can be disabled with a switch.

I implemented a hook into the audio subsystem so IDLE2 mode will not be activated unless audio playback has been active for 30 seconds. This choice was made to stop spurious activations of IDLE2 mode when there are system notifications, for example.

IDLE2 mode will now be activated under the following circumstances:

The disabled switch is not set

Audio playback has been active for 30 seconds

USB cable is disconnected

Bluetooth is inactive (see above)

Earlysuspend is active (screen is turned off)

Various clocks are gated and various subsystems are power gated

No interrupts pending

Added code to disable cpufreq and clamp the CPU at 800MHz when the conditions are right for IDLE2 mode to be entered. This appears to save significantly more power.

Codewise, many of the IDLE2 specific functions have been moved into idle2.h, to keep them separate from the standard cpufreq code. The asm has also been further cleaned up, with calls to the generic flush_cache_all() and cpu_do_idle() functions instead of using asm calls. The remaining asm is involved in saving and restoring registers for IDLE2 mode. Exit latency has been set to 400, which is what is stated in the S5PC100 TRM. Residency time has been reduced to 2000, as even going into IDLE2 for short periods is more efficient than using regular idle or not idling at all.

IDLE2 mode now has a disable switch, which can be activated with the following command:

echo 1 > /sys/module/cpuidle/parameters/idle2_disabled

As the IDLE2 state is now implemented concurrently with IDLE, info can be obtained from the standard cpuidle interfaces.

For normal IDLE these can be found here:

/sys/devices/system/cpu/cpu0/cpuidle/state0

And IDLE2 can be found here:

/sys/devices/system/cpu/cpu0/cpuidle/state1

I think that is all the improvements.

Patches v0.200 and v0.201 are available on my github which will need to be applied on top of all the rest of the IDLE2 patches.

As some of you will know, I have been working on a port of Samsungs IDLE2 / LPAUDIO feature from their 2.6.32 P1000 kernel to the 3.0.x i902x kernel.

This implements the same thing as deep idle attempts to do, but it has several benefits over the original deep-idle patch, as will be explained below.

I’m pleased to say that I am going to release the source and a kernel with it included today.

This release comes after a large amount of debugging of lockups caused by interactions with the coresight tracing driver and general other breakage, including some asm issues. I did get to a point where I couldn’t get it stable at all, I wasn’t getting any useful output from last_kmsg and I was almost about to give up, but thanks to a decent last_kmsg trace or 2 and gdb <3 I was able to find the problem, fix the issue and get it stable. :) Myself and several others have been running it for about 2 days now, so altogether we have probably amassed about 500 hours of testing, with no issues reported so far.

Differences / Advantages compared to deep-idle:

Enabled by default and automatically invoked whenever the conditions are right.

When the screen is on, the idle mode is set to NORMAL, which skips all the idle2 code and works as normal, so no change there.

When the screen is turned off (earlysuspend), the mode is switched to IDLE2 which is used when all the following conditions are met:

PowerManagerService is holding a wakelock

USB is not plugged in

GPU is not running

MMC & NAND aren’t in use

DMA is clock gated

Audio DMA is not in use

No RTC interrupts pending

It also checks the Vector Interrupt Controller status immediately before saving register state / calling WFI and bails if an interrupt is active

Cleaner code paths, optimised for the most likely scenario to save some cycles on branching. Many critical functions inlined to make their execution quicker and cheaper. Many int type functions modified to use the cheapest type possible, normally bool or void.

No expensive stat collecting sysfs interface chewing up cpu cycles. I know people will moan that this stops you seeing what it is doing, but, does it really matter if it is working? Just accept that it *is* working. If you really want to check what it is doing, recompile the kernel with the debugging messages enabled and it will spam dmesg with everything that idle2 is doing. :)

ASM modified to use existing generic platform cache flush and idle routines rather than duplicate them. Certainly more could be done here, and that is something for a future version, but I’ve made a good start at it.

It doesn’t require the CPU to be forced to an artificial frequency for stability. The CPU scales as normal and ranges 100 <> 1000 whilst idle2 is active.

DEEP-IDLE TOP OFF mode is default. TOP-ON is not used.

There is no requirement for the config_bluetooth_adapter_quick_switch disabling patch as it works perfectly without it.

Due to it being hooked into a wakelock, it cannot be invoked at the same time as suspend is called, so it has no way of racing with the suspend code.

It seems to be completely stable (so far anyway).

I think that is about it for the advantages, but as you can see, it is a worthwhile improvement.

To do:

More code cleanups.

Try to make some code paths cheaper.

Try to use generic platform asm or unify with platform asm.

Patches and improvement suggestions are welcome. This is one of the most complex things I have released, so there will be improvements to be made and suggestions and patches are more than welcome, specially for any of the todo items.

Wilful kanging, not clearly giving proper credits and not contributing back is extremely unwelcome. Open source is not about copying other peoples work, a principle which some developers don’t get, The GPL was never designed for the purpose of copying code verbatim and riding on other peoples work, it was designed to allow freedom of code, a continual cycle of improvement and peer review, so consider that please.