diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2015-11-05 03:51:19 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-11-06 17:59:41 -0500 |
commit | 87b4115db0239865bc812f61704bb1f43e2439b6 (patch) | |
tree | 957cb0855dddaf03ab63e7ce0cf8bba24018bd9d | |
parent | 1794ec1f9585501e4ed4390f5a5d396fd28c63ce (diff) |
PM / OPP: Protect updates to list_dev with mutex
dev_opp_list_lock is used everywhere to protect device and OPP lists,
but dev_pm_opp_set_sharing_cpus() is missed somehow. And instead we used
rcu-lock, which wouldn't help here as we are adding a new list_dev.
This also fixes a problem where we have called kzalloc(..., GFP_KERNEL)
from within rcu-lock, which isn't allowed as kzalloc can sleep when
called with GFP_KERNEL.
With CONFIG_DEBUG_ATOMIC_SLEEP set, we get following lockdep-splat:
include/linux/rcupdate.h:578 Illegal context switch in RCU read-side critical section!
other info that might help us debug this:
rcu_scheduler_active = 1, debug_locks = 0
5 locks held by swapper/0/1:
#0: (&dev->mutex){......}, at: [<c02f68f4>] __driver_attach+0x48/0x98
#1: (&dev->mutex){......}, at: [<c02f6904>] __driver_attach+0x58/0x98
#2: (cpu_hotplug.lock){++++++}, at: [<c00249d0>] get_online_cpus+0x40/0xb0
#3: (subsys mutex#5){+.+.+.}, at: [<c02f4f8c>] subsys_interface_register+0x44/0xdc
#4: (rcu_read_lock){......}, at: [<c0305c80>] dev_pm_opp_set_sharing_cpus+0x0/0x1e4
stack backtrace:
CPU: 1 PID: 1 Comm: swapper/0 Tainted: G W 4.3.0-rc7-00047-g81f5932958a8 #59
Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[<c0016874>] (unwind_backtrace) from [<c001355c>] (show_stack+0x10/0x14)
[<c001355c>] (show_stack) from [<c022553c>] (dump_stack+0x94/0xbc)
[<c022553c>] (dump_stack) from [<c004904c>] (___might_sleep+0x24c/0x298)
[<c004904c>] (___might_sleep) from [<c00f07e4>] (kmem_cache_alloc+0xe8/0x164)
[<c00f07e4>] (kmem_cache_alloc) from [<c0305354>] (_add_list_dev+0x30/0x58)
[<c0305354>] (_add_list_dev) from [<c0305d50>] (dev_pm_opp_set_sharing_cpus+0xd0/0x1e4)
[<c0305d50>] (dev_pm_opp_set_sharing_cpus) from [<c040eda4>] (cpufreq_init+0x4cc/0x62c)
[<c040eda4>] (cpufreq_init) from [<c040a964>] (cpufreq_online+0xbc/0x73c)
[<c040a964>] (cpufreq_online) from [<c02f4fe0>] (subsys_interface_register+0x98/0xdc)
[<c02f4fe0>] (subsys_interface_register) from [<c040a640>] (cpufreq_register_driver+0x110/0x17c)
[<c040a640>] (cpufreq_register_driver) from [<c040ef64>] (dt_cpufreq_probe+0x60/0x8c)
[<c040ef64>] (dt_cpufreq_probe) from [<c02f8084>] (platform_drv_probe+0x44/0xa4)
[<c02f8084>] (platform_drv_probe) from [<c02f67c0>] (driver_probe_device+0x208/0x2f4)
[<c02f67c0>] (driver_probe_device) from [<c02f6940>] (__driver_attach+0x94/0x98)
[<c02f6940>] (__driver_attach) from [<c02f4c1c>] (bus_for_each_dev+0x68/0x9c)
Reported-by: Michael Turquette <mturquette@baylibre.com>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Cc: 4.3 <stable@vger.kernel.org> # 4.3
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/base/power/opp/core.c | 2 | ||||
-rw-r--r-- | drivers/base/power/opp/cpu.c | 8 | ||||
-rw-r--r-- | drivers/base/power/opp/opp.h | 3 |
3 files changed, 8 insertions, 5 deletions
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c index ccf2c91aedff..252706d6f60b 100644 --- a/drivers/base/power/opp/core.c +++ b/drivers/base/power/opp/core.c | |||
@@ -29,7 +29,7 @@ | |||
29 | */ | 29 | */ |
30 | static LIST_HEAD(dev_opp_list); | 30 | static LIST_HEAD(dev_opp_list); |
31 | /* Lock to allow exclusive modification to the device and opp lists */ | 31 | /* Lock to allow exclusive modification to the device and opp lists */ |
32 | static DEFINE_MUTEX(dev_opp_list_lock); | 32 | DEFINE_MUTEX(dev_opp_list_lock); |
33 | 33 | ||
34 | #define opp_rcu_lockdep_assert() \ | 34 | #define opp_rcu_lockdep_assert() \ |
35 | do { \ | 35 | do { \ |
diff --git a/drivers/base/power/opp/cpu.c b/drivers/base/power/opp/cpu.c index 2139c9d4c447..7b445e88a0d5 100644 --- a/drivers/base/power/opp/cpu.c +++ b/drivers/base/power/opp/cpu.c | |||
@@ -127,12 +127,12 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | |||
127 | struct device *dev; | 127 | struct device *dev; |
128 | int cpu, ret = 0; | 128 | int cpu, ret = 0; |
129 | 129 | ||
130 | rcu_read_lock(); | 130 | mutex_lock(&dev_opp_list_lock); |
131 | 131 | ||
132 | dev_opp = _find_device_opp(cpu_dev); | 132 | dev_opp = _find_device_opp(cpu_dev); |
133 | if (IS_ERR(dev_opp)) { | 133 | if (IS_ERR(dev_opp)) { |
134 | ret = -EINVAL; | 134 | ret = -EINVAL; |
135 | goto out_rcu_read_unlock; | 135 | goto unlock; |
136 | } | 136 | } |
137 | 137 | ||
138 | for_each_cpu(cpu, cpumask) { | 138 | for_each_cpu(cpu, cpumask) { |
@@ -153,8 +153,8 @@ int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, cpumask_var_t cpumask) | |||
153 | continue; | 153 | continue; |
154 | } | 154 | } |
155 | } | 155 | } |
156 | out_rcu_read_unlock: | 156 | unlock: |
157 | rcu_read_unlock(); | 157 | mutex_unlock(&dev_opp_list_lock); |
158 | 158 | ||
159 | return ret; | 159 | return ret; |
160 | } | 160 | } |
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h index dcb38f78dae4..7366b2aa8997 100644 --- a/drivers/base/power/opp/opp.h +++ b/drivers/base/power/opp/opp.h | |||
@@ -21,6 +21,9 @@ | |||
21 | #include <linux/rculist.h> | 21 | #include <linux/rculist.h> |
22 | #include <linux/rcupdate.h> | 22 | #include <linux/rcupdate.h> |
23 | 23 | ||
24 | /* Lock to allow exclusive modification to the device and opp lists */ | ||
25 | extern struct mutex dev_opp_list_lock; | ||
26 | |||
24 | /* | 27 | /* |
25 | * Internal data structure organization with the OPP layer library is as | 28 | * Internal data structure organization with the OPP layer library is as |
26 | * follows: | 29 | * follows: |