linux/drivers/cpufreq
Jane Li 6f1e4efd88 cpufreq: Fix timer/workqueue corruption by protecting reading governor_enabled
When a CPU is hot removed we'll cancel all the delayed work items via
gov_cancel_work(). Sometimes the delayed work function determines that
it should adjust the delay for all other CPUs that the policy is
managing. If this scenario occurs, the canceling CPU will cancel its own
work but queue up the other CPUs works to run.

Commit 3617f2 (cpufreq: Fix timer/workqueue corruption due to double
queueing) has tried to fix this, but reading governor_enabled is not
protected by cpufreq_governor_lock. Even though od_dbs_timer() checks
governor_enabled before gov_queue_work(), this scenario may occur. For
example:

 CPU0                                        CPU1
 ----                                        ----
 cpu_down()
  ...                                        <work runs>
  __cpufreq_remove_dev()                     od_dbs_timer()
   __cpufreq_governor()                       policy->governor_enabled
    policy->governor_enabled = false;
    cpufreq_governor_dbs()
     case CPUFREQ_GOV_STOP:
      gov_cancel_work(dbs_data, policy);
       cpu0 work is canceled
        timer is canceled
        cpu1 work is canceled
        <waits for cpu1>
                                              gov_queue_work(*, *, true);
                                               cpu0 work queued
                                               cpu1 work queued
                                               cpu2 work queued
                                               ...
        cpu1 work is canceled
        cpu2 work is canceled
        ...

At the end of the GOV_STOP case cpu0 still has a work queued to
run although the code is expecting all of the works to be
canceled. __cpufreq_remove_dev() will then proceed to
re-initialize all the other CPUs works except for the CPU that is
going down. The CPUFREQ_GOV_START case in cpufreq_governor_dbs()
will trample over the queued work and debugobjects will spit out
a warning:

WARNING: at lib/debugobjects.c:260 debug_print_object+0x94/0xbc()
ODEBUG: init active (active state 0) object type: timer_list hint: delayed_work_timer_fn+0x0/0x14
Modules linked in:
CPU: 1 PID: 1205 Comm: sh Tainted: G        W    3.10.0 #200
[<c01144f0>] (unwind_backtrace+0x0/0xf8) from [<c0111d98>] (show_stack+0x10/0x14)
[<c0111d98>] (show_stack+0x10/0x14) from [<c01272cc>] (warn_slowpath_common+0x4c/0x68)
[<c01272cc>] (warn_slowpath_common+0x4c/0x68) from [<c012737c>] (warn_slowpath_fmt+0x30/0x40)
[<c012737c>] (warn_slowpath_fmt+0x30/0x40) from [<c034c640>] (debug_print_object+0x94/0xbc)
[<c034c640>] (debug_print_object+0x94/0xbc) from [<c034c7f8>] (__debug_object_init+0xc8/0x3c0)
[<c034c7f8>] (__debug_object_init+0xc8/0x3c0) from [<c01360e0>] (init_timer_key+0x20/0x104)
[<c01360e0>] (init_timer_key+0x20/0x104) from [<c04872ac>] (cpufreq_governor_dbs+0x1dc/0x68c)
[<c04872ac>] (cpufreq_governor_dbs+0x1dc/0x68c) from [<c04833a8>] (__cpufreq_governor+0x80/0x1b0)
[<c04833a8>] (__cpufreq_governor+0x80/0x1b0) from [<c0483704>] (__cpufreq_remove_dev.isra.12+0x22c/0x380)
[<c0483704>] (__cpufreq_remove_dev.isra.12+0x22c/0x380) from [<c0692f38>] (cpufreq_cpu_callback+0x48/0x5c)
[<c0692f38>] (cpufreq_cpu_callback+0x48/0x5c) from [<c014fb40>] (notifier_call_chain+0x44/0x84)
[<c014fb40>] (notifier_call_chain+0x44/0x84) from [<c012ae44>] (__cpu_notify+0x2c/0x48)
[<c012ae44>] (__cpu_notify+0x2c/0x48) from [<c068dd40>] (_cpu_down+0x80/0x258)
[<c068dd40>] (_cpu_down+0x80/0x258) from [<c068df40>] (cpu_down+0x28/0x3c)
[<c068df40>] (cpu_down+0x28/0x3c) from [<c068e4c0>] (store_online+0x30/0x74)
[<c068e4c0>] (store_online+0x30/0x74) from [<c03a7308>] (dev_attr_store+0x18/0x24)
[<c03a7308>] (dev_attr_store+0x18/0x24) from [<c0256fe0>] (sysfs_write_file+0x100/0x180)
[<c0256fe0>] (sysfs_write_file+0x100/0x180) from [<c01fec9c>] (vfs_write+0xbc/0x184)
[<c01fec9c>] (vfs_write+0xbc/0x184) from [<c01ff034>] (SyS_write+0x40/0x68)
[<c01ff034>] (SyS_write+0x40/0x68) from [<c010e200>] (ret_fast_syscall+0x0/0x48)

In gov_queue_work(), lock cpufreq_governor_lock before gov_queue_work,
and unlock it after __gov_queue_work(). In this way, governor_enabled
is guaranteed not changed in gov_queue_work().

Signed-off-by: Jane Li <jiel@marvell.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2014-01-06 01:22:02 +01:00
..
Kconfig cpufreq: Select PM_OPP rather than depending on it 2013-12-22 22:03:49 +01:00
Kconfig.arm cpufreq: arm-big-little: Make driver dependent on CONFIG_BIG_LITTLE 2014-01-06 01:17:48 +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
Makefile cpufreq: arm_big_little: add vexpress SPC interface driver 2013-10-30 00:48:26 +01:00
acpi-cpufreq.c Merge branch 'pm-cpufreq' 2013-11-07 19:26:02 +01:00
amd_freq_sensitivity.c
arm_big_little.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
arm_big_little.h cpufreq: arm_big_little: add in-kernel switching (IKS) support 2013-10-31 00:10:53 +01:00
arm_big_little_dt.c PM / OPP: rename header to linux/pm_opp.h 2013-10-25 22:33:23 +02:00
at32ap-cpufreq.c cpufreq_ at32ap-cpufreq.c: Fix section mismatch 2013-12-10 08:46:38 +01:00
blackfin-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
cpufreq-cpu0.c cpufreq: cpufreq-cpu0: clk_round_rate() can return a zero upon error 2013-12-06 23:19:19 +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: Fix timer/workqueue corruption by protecting reading governor_enabled 2014-01-06 01:22:02 +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: Fix wrong time unit conversion 2013-09-10 02:49:46 +02:00
cpufreq_userspace.c cpufreq / governor: Remove fossil comment 2013-10-17 23:00:20 +02:00
cris-artpec3-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
cris-etraxfs-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
davinci-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
dbx500-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
e_powersaver.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
elanfreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
exynos-cpufreq.c Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial 2013-11-15 16:47:22 -08:00
exynos-cpufreq.h cpufreq: fix EXYNOS drivers selection 2013-08-12 12:00:21 +05:30
exynos4x12-cpufreq.c cpufreq: exynos: Remove unwanted EXPORT_SYMBOL 2013-11-20 23:55:38 +01:00
exynos4210-cpufreq.c cpufreq: exynos: Remove unwanted EXPORT_SYMBOL 2013-11-20 23:55:38 +01:00
exynos5250-cpufreq.c cpufreq: exynos: Remove unwanted EXPORT_SYMBOL 2013-11-20 23:55:38 +01:00
exynos5440-cpufreq.c cpufreq: distinguish drivers that do asynchronous notifications 2013-10-31 00:11:08 +01:00
freq_table.c cpufreq: define generic .attr, .exit() and .verify() routines 2013-10-16 00:50:23 +02: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: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
imx6q-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
integrator-cpufreq.c ACPI and power management updates for 3.13-rc1 2013-11-14 13:41:48 +09:00
intel_pstate.c Merge back earlier 'pm-cpufreq' material. 2014-01-05 15:32:51 +01:00
kirkwood-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
longhaul.c cpufreq: Implement light weight ->target_index() routine 2013-10-25 22:42:24 +02:00
longhaul.h
longrun.c cpufreq: add new routine cpufreq_verify_within_cpu_limits() 2013-10-16 00:50:23 +02:00
loongson2_cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +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: OMAP: Fix compilation error 'r & ret undeclared' 2013-11-14 00:52:48 +01:00
p4-clockmod.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
pasemi-cpufreq.c ACPI and power management updates for 3.13-rc1 2013-11-14 13:41:48 +09:00
pcc-cpufreq.c Merge branch 'pm-cpufreq' 2013-10-28 01:29:34 +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: Implement light weight ->target_index() routine 2013-10-25 22:42:24 +02:00
powernow-k7.c cpufreq: Implement light weight ->target_index() routine 2013-10-25 22:42:24 +02:00
powernow-k7.h
powernow-k8.c cpufreq: distinguish drivers that do asynchronous notifications 2013-10-31 00:11:08 +01:00
powernow-k8.h
ppc-corenet-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
ppc_cbe_cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
ppc_cbe_cpufreq.h
ppc_cbe_cpufreq_pervasive.c
ppc_cbe_cpufreq_pmi.c
pxa2xx-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
pxa3xx-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
s3c24xx-cpufreq-debugfs.c
s3c24xx-cpufreq.c cpufreq: s3c24xx: Staticize local variable 2014-01-06 01:18:33 +01:00
s3c64xx-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
s3c2410-cpufreq.c
s3c2412-cpufreq.c
s3c2416-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
s3c2440-cpufreq.c cpufreq: s3c2440: Staticize local variables 2014-01-06 01:18:33 +01:00
s5pv210-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
sa1100-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
sa1110-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
sc520_freq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
sh-cpufreq.c cpufreq: sh: don't initialize part of policy set by core 2013-10-16 00:50:32 +02:00
sparc-us2e-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
sparc-us3-cpufreq.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
spear-cpufreq.c cpufreq: SPEAr: clk_round_rate() can return a zero upon error 2013-12-06 23:19:19 +01:00
speedstep-centrino.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
speedstep-ich.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
speedstep-lib.c
speedstep-lib.h
speedstep-smi.c cpufreq: move freq change notifications to cpufreq core 2013-10-31 00:11:08 +01:00
tegra-cpufreq.c cpufreq: tegra: don't error target() when suspended 2013-11-20 23:28:32 +01:00
unicore2-cpufreq.c cpufreq: unicore2: don't initialize part of policy set by core 2013-10-16 00:50:32 +02:00
vexpress-spc-cpufreq.c cpufreq: arm_big_little: add vexpress SPC interface driver 2013-10-30 00:48:26 +01:00