linux/drivers/cpufreq
Srivatsa S. Bhat 12478cf0c5 cpufreq: Make sure frequency transitions are serialized
Whenever we change the frequency of a CPU, we call the PRECHANGE and POSTCHANGE
notifiers. They must be serialized, i.e. PRECHANGE and POSTCHANGE notifiers
should strictly alternate, thereby preventing two different sets of PRECHANGE or
POSTCHANGE notifiers from interleaving arbitrarily.

The following examples illustrate why this is important:

Scenario 1:
-----------
A thread reading the value of cpuinfo_cur_freq, will call
__cpufreq_cpu_get()->cpufreq_out_of_sync()->cpufreq_notify_transition()

The ondemand governor can decide to change the frequency of the CPU at the same
time and hence it can end up sending the notifications via ->target().

If the notifiers are not serialized, the following sequence can occur:
- PRECHANGE Notification for freq A (from cpuinfo_cur_freq)
- PRECHANGE Notification for freq B (from target())
- Freq changed by target() to B
- POSTCHANGE Notification for freq B
- POSTCHANGE Notification for freq A

We can see from the above that the last POSTCHANGE Notification happens for freq
A but the hardware is set to run at freq B.

Where would we break then?: adjust_jiffies() in cpufreq.c & cpufreq_callback()
in arch/arm/kernel/smp.c (which also adjusts the jiffies). All the
loops_per_jiffy calculations will get messed up.

Scenario 2:
-----------
The governor calls __cpufreq_driver_target() to change the frequency. At the
same time, if we change scaling_{min|max}_freq from sysfs, it will end up
calling the governor's CPUFREQ_GOV_LIMITS notification, which will also call
__cpufreq_driver_target(). And hence we end up issuing concurrent calls to
->target().

Typically, platforms have the following logic in their ->target() routines:
(Eg: cpufreq-cpu0, omap, exynos, etc)

A. If new freq is more than old: Increase voltage
B. Change freq
C. If new freq is less than old: decrease voltage

Now, if the two concurrent calls to ->target() are X and Y, where X is trying to
increase the freq and Y is trying to decrease it, we get the following race
condition:

X.A: voltage gets increased for larger freq
Y.A: nothing happens
Y.B: freq gets decreased
Y.C: voltage gets decreased
X.B: freq gets increased
X.C: nothing happens

Thus we can end up setting a freq which is not supported by the voltage we have
set. That will probably make the clock to the CPU unstable and the system might
not work properly anymore.

This patch introduces a set of synchronization primitives to serialize frequency
transitions, which are to be used as shown below:

cpufreq_freq_transition_begin();

//Perform the frequency change

cpufreq_freq_transition_end();

The _begin() call sends the PRECHANGE notification whereas the _end() call sends
the POSTCHANGE notification. Also, all the necessary synchronization is handled
within these calls. In particular, even drivers which set the ASYNC_NOTIFICATION
flag can also use these APIs for performing frequency transitions (ie., you can
call _begin() from one task, and call the corresponding _end() from a different
task).

The actual synchronization underneath is not that complicated:

The key challenge is to allow drivers to begin the transition from one thread
and end it in a completely different thread (this is to enable drivers that do
asynchronous POSTCHANGE notification from bottom-halves, to also use the same
interface).

To achieve this, a 'transition_ongoing' flag, a 'transition_lock' spinlock and a
wait-queue are added per-policy. The flag and the wait-queue are used in
conjunction to create an "uninterrupted flow" from _begin() to _end(). The
spinlock is used to ensure that only one such "flow" is in flight at any given
time. Put together, this provides us all the necessary synchronization.

Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2014-03-26 16:41:40 +01:00
..
acpi-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
amd_freq_sensitivity.c cpufreq: AMD "frequency sensitivity feedback" powersave bias for ondemand governor 2013-04-10 13:19:26 +02:00
arm_big_little_dt.c PM / OPP: rename header to linux/pm_opp.h 2013-10-25 22:33:23 +02:00
arm_big_little.c cpufreq: arm_big_little: set 'physical_cluster' for each CPU 2014-03-19 02:18:39 +01:00
arm_big_little.h cpufreq: arm_big_little: add in-kernel switching (IKS) support 2013-10-31 00:10:53 +01:00
at32ap-cpufreq.c cpufreq: introduce cpufreq_generic_get() routine 2014-01-17 02:00:44 +01:00
blackfin-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
cpufreq_conservative.c cpufreq: conservative: set requested_freq to policy max when it is over policy max 2013-11-12 23:18:20 +01:00
cpufreq_governor.c cpufreq: Fix timer/workqueue corruption by protecting reading governor_enabled 2014-01-06 01:22:02 +01:00
cpufreq_governor.h cpufreq: Fix timer/workqueue corruption by protecting reading governor_enabled 2014-01-06 01:22:02 +01:00
cpufreq_ondemand.c cpufreq: ondemand: Remove redundant return statement 2013-11-01 00:44:34 +01:00
cpufreq_performance.c cpufreq: Clean up header files included in the core 2013-08-07 23:34:09 +02:00
cpufreq_powersave.c cpufreq: Clean up header files included in the core 2013-08-07 23:34:09 +02:00
cpufreq_stats.c cpufreq: stats: Refactor common code into __cpufreq_stats_create_table() 2014-03-02 00:55:50 +01:00
cpufreq_userspace.c cpufreq / governor: Remove fossil comment 2013-10-17 23:00:20 +02:00
cpufreq-cpu0.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
cpufreq-nforce2.c cpufreq: nforce2: don't initialize part of policy set by core 2013-10-16 00:50:29 +02:00
cpufreq.c cpufreq: Make sure frequency transitions are serialized 2014-03-26 16:41:40 +01:00
cris-artpec3-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
cris-etraxfs-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
davinci-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
dbx500-cpufreq.c cpufreq: introduce cpufreq_generic_get() routine 2014-01-17 02:00:44 +01:00
e_powersaver.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
elanfreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
exynos4x12-cpufreq.c ACPI and power management updates for 3.14-rc1 2014-01-24 15:51:02 -08:00
exynos4210-cpufreq.c Samsung cleanup 2nd for v3.14 2013-12-28 15:12:07 -08:00
exynos5250-cpufreq.c ACPI and power management updates for 3.14-rc1 2014-01-24 15:51:02 -08:00
exynos5440-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
exynos-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
exynos-cpufreq.h cpufreq: exynos: move definitions for exynos-cpufreq into drivers/cpufreq/ 2013-12-19 05:21:18 +09:00
freq_table.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
gx-suspmod.c cpufreq: gx: don't initialize part of policy set by core 2013-10-16 00:50:30 +02:00
highbank-cpufreq.c cpufreq: highbank-cpufreq: Enable Midway/ECX-2000 2013-10-17 00:53:08 +02:00
ia64-acpi-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
imx6q-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
integrator-cpufreq.c cpufreq: Mark ARM drivers with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag 2014-01-06 14:17:25 +01:00
intel_pstate.c intel_pstate: Use del_timer_sync in intel_pstate_cpu_stop 2014-03-26 16:39:53 +01:00
Kconfig cpufreq: enable ARM drivers on arm64 2014-03-01 00:55:40 +01:00
Kconfig.arm cpufreq: arm_big_little: make vexpress driver depend on bL core driver 2014-03-19 02:15:23 +01:00
Kconfig.powerpc cpufreq: remove CONFIG_CPU_FREQ_TABLE 2013-10-16 00:50:33 +02:00
Kconfig.x86 Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial 2013-11-15 16:47:22 -08:00
kirkwood-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
longhaul.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
longhaul.h cpufreq: delete __cpuinit usage from all cpufreq files 2013-07-14 19:36:57 -04:00
longrun.c cpufreq: add new routine cpufreq_verify_within_cpu_limits() 2013-10-16 00:50:23 +02:00
loongson2_cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
Makefile cpufreq: arm_big_little: add vexpress SPC interface driver 2013-10-30 00:48:26 +01:00
maple-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
omap-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
p4-clockmod.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
pasemi-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
pcc-cpufreq.c cpufreq: send new set of notification for transition failures 2014-01-06 01:43:44 +01:00
pmac32-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
pmac64-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
powernow-k6.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
powernow-k7.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
powernow-k7.h
powernow-k8.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
powernow-k8.h cpufreq: Remove support for hardware P-state chips from powernow-k8 2012-09-09 22:05:30 +02:00
ppc_cbe_cpufreq_pervasive.c cpufreq: powerpc/platforms/cell: move cpufreq driver to drivers/cpufreq 2013-04-10 13:19:26 +02:00
ppc_cbe_cpufreq_pmi.c cpufreq: powerpc/platforms/cell: move cpufreq driver to drivers/cpufreq 2013-04-10 13:19:26 +02:00
ppc_cbe_cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
ppc_cbe_cpufreq.h cpufreq: powerpc/platforms/cell: move cpufreq driver to drivers/cpufreq 2013-04-10 13:19:26 +02:00
ppc-corenet-cpufreq.c cpufreq: powerpc: add cpufreq transition latency for FSL e500mc SoCs 2014-03-20 03:37:17 +01:00
pxa2xx-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
pxa3xx-cpufreq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
s3c24xx-cpufreq-debugfs.c cpufreq: s3c24xx: move cpufreq driver to drivers/cpufreq 2013-05-20 23:04:28 +09:00
s3c24xx-cpufreq.c cpufreq: introduce cpufreq_generic_get() routine 2014-01-17 02:00:44 +01:00
s3c64xx-cpufreq.c cpufreq: introduce cpufreq_generic_get() routine 2014-01-17 02:00:44 +01:00
s3c2410-cpufreq.c cpufreq: s3c24xx: move cpufreq driver to drivers/cpufreq 2013-05-20 23:04:28 +09:00
s3c2412-cpufreq.c cpufreq: s3c24xx: move cpufreq driver to drivers/cpufreq 2013-05-20 23:04:28 +09:00
s3c2416-cpufreq.c cpufreq: Mark ARM drivers with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag 2014-01-06 14:17:25 +01:00
s3c2440-cpufreq.c cpufreq: s3c2440: Staticize local variables 2014-01-06 01:18:33 +01:00
s5pv210-cpufreq.c cpufreq: s5pv210: Use cpufreq_generic_suspend() 2014-03-06 15:04:12 +01:00
sa1100-cpufreq.c cpufreq: Mark ARM drivers with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag 2014-01-06 14:17:25 +01:00
sa1110-cpufreq.c cpufreq: Mark ARM drivers with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag 2014-01-06 14:17:25 +01:00
sc520_freq.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
sh-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
sparc-us2e-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
sparc-us3-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
spear-cpufreq.c cpufreq: SPEAr: Instantiate as platform_driver 2014-03-12 01:06:01 +01:00
speedstep-centrino.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
speedstep-ich.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
speedstep-lib.c cpufreq: Add support for x86 cpuinfo auto loading v4 2012-01-26 16:49:06 -08:00
speedstep-lib.h
speedstep-smi.c cpufreq: Remove cpufreq_generic_exit() 2014-03-12 01:06:00 +01:00
tegra-cpufreq.c cpufreq: add 'freq_table' in struct cpufreq_policy 2014-03-12 01:06:00 +01:00
unicore2-cpufreq.c cpufreq: introduce cpufreq_generic_get() routine 2014-01-17 02:00:44 +01:00
vexpress-spc-cpufreq.c cpufreq: arm_big_little: add vexpress SPC interface driver 2013-10-30 00:48:26 +01:00