aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Shi <alex.shi@linaro.org>2017-07-28 03:09:25 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-07-31 07:09:49 -0400
commit313c8c16ee62b32b8b40c6b00637b401dc19050e (patch)
tree392a1491c74d888594a793bcec95ea7f893167b5
parent16f73eb02d7e1765ccab3d2018e0bd98eb93d973 (diff)
PM / CPU: replace raw_notifier with atomic_notifier
This patch replaces an rwlock and raw notifier by an atomic notifier protected by a spin_lock and RCU. The main reason for this change is due to a 'scheduling while atomic' bug with RT kernels on ARM/ARM64. On ARM/ARM64, the rwlock cpu_pm_notifier_lock in cpu_pm_enter/exit() causes a potential schedule after IRQ disable in the idle call chain: cpu_startup_entry cpu_idle_loop local_irq_disable() cpuidle_idle_call call_cpuidle cpuidle_enter cpuidle_enter_state ->enter :arm_enter_idle_state cpu_pm_enter/exit CPU_PM_CPU_IDLE_ENTER read_lock(&cpu_pm_notifier_lock); <-- sleep in idle __rt_spin_lock(); schedule(); The kernel panic is here: [ 4.609601] BUG: scheduling while atomic: swapper/1/0/0x00000002 [ 4.609608] [<ffff0000086fae70>] arm_enter_idle_state+0x18/0x70 [ 4.609614] Modules linked in: [ 4.609615] [<ffff0000086f9298>] cpuidle_enter_state+0xf0/0x218 [ 4.609620] [<ffff0000086f93f8>] cpuidle_enter+0x18/0x20 [ 4.609626] Preemption disabled at: [ 4.609627] [<ffff0000080fa234>] call_cpuidle+0x24/0x40 [ 4.609635] [<ffff000008882fa4>] schedule_preempt_disabled+0x1c/0x28 [ 4.609639] [<ffff0000080fa49c>] cpu_startup_entry+0x154/0x1f8 [ 4.609645] [<ffff00000808e004>] secondary_start_kernel+0x15c/0x1a0 Daniel Lezcano said this notification is needed on arm/arm64 platforms. Sebastian suggested using atomic_notifier instead of rwlock, which is not only removing the sleeping in idle, but also improving latency. Tony Lindgren found a miss use that rcu_read_lock used after rcu_idle_enter Paul McKenney suggested trying RCU_NONIDLE. Signed-off-by: Alex Shi <alex.shi@linaro.org> Tested-by: Tony Lindgren <tony@atomide.com> Acked-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--kernel/cpu_pm.c50
1 files changed, 13 insertions, 37 deletions
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
index 009cc9a17d95..67b02e138a47 100644
--- a/kernel/cpu_pm.c
+++ b/kernel/cpu_pm.c
@@ -22,15 +22,21 @@
22#include <linux/spinlock.h> 22#include <linux/spinlock.h>
23#include <linux/syscore_ops.h> 23#include <linux/syscore_ops.h>
24 24
25static DEFINE_RWLOCK(cpu_pm_notifier_lock); 25static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
26static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
27 26
28static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls) 27static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
29{ 28{
30 int ret; 29 int ret;
31 30
32 ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL, 31 /*
32 * __atomic_notifier_call_chain has a RCU read critical section, which
33 * could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
34 * RCU know this.
35 */
36 rcu_irq_enter_irqson();
37 ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
33 nr_to_call, nr_calls); 38 nr_to_call, nr_calls);
39 rcu_irq_exit_irqson();
34 40
35 return notifier_to_errno(ret); 41 return notifier_to_errno(ret);
36} 42}
@@ -47,14 +53,7 @@ static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
47 */ 53 */
48int cpu_pm_register_notifier(struct notifier_block *nb) 54int cpu_pm_register_notifier(struct notifier_block *nb)
49{ 55{
50 unsigned long flags; 56 return atomic_notifier_chain_register(&cpu_pm_notifier_chain, nb);
51 int ret;
52
53 write_lock_irqsave(&cpu_pm_notifier_lock, flags);
54 ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
55 write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
56
57 return ret;
58} 57}
59EXPORT_SYMBOL_GPL(cpu_pm_register_notifier); 58EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
60 59
@@ -69,14 +68,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
69 */ 68 */
70int cpu_pm_unregister_notifier(struct notifier_block *nb) 69int cpu_pm_unregister_notifier(struct notifier_block *nb)
71{ 70{
72 unsigned long flags; 71 return atomic_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
73 int ret;
74
75 write_lock_irqsave(&cpu_pm_notifier_lock, flags);
76 ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
77 write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
78
79 return ret;
80} 72}
81EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier); 73EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
82 74
@@ -100,7 +92,6 @@ int cpu_pm_enter(void)
100 int nr_calls; 92 int nr_calls;
101 int ret = 0; 93 int ret = 0;
102 94
103 read_lock(&cpu_pm_notifier_lock);
104 ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls); 95 ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
105 if (ret) 96 if (ret)
106 /* 97 /*
@@ -108,7 +99,6 @@ int cpu_pm_enter(void)
108 * PM entry who are notified earlier to prepare for it. 99 * PM entry who are notified earlier to prepare for it.
109 */ 100 */
110 cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL); 101 cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
111 read_unlock(&cpu_pm_notifier_lock);
112 102
113 return ret; 103 return ret;
114} 104}
@@ -128,13 +118,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_enter);
128 */ 118 */
129int cpu_pm_exit(void) 119int cpu_pm_exit(void)
130{ 120{
131 int ret; 121 return cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
132
133 read_lock(&cpu_pm_notifier_lock);
134 ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
135 read_unlock(&cpu_pm_notifier_lock);
136
137 return ret;
138} 122}
139EXPORT_SYMBOL_GPL(cpu_pm_exit); 123EXPORT_SYMBOL_GPL(cpu_pm_exit);
140 124
@@ -159,7 +143,6 @@ int cpu_cluster_pm_enter(void)
159 int nr_calls; 143 int nr_calls;
160 int ret = 0; 144 int ret = 0;
161 145
162 read_lock(&cpu_pm_notifier_lock);
163 ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls); 146 ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
164 if (ret) 147 if (ret)
165 /* 148 /*
@@ -167,7 +150,6 @@ int cpu_cluster_pm_enter(void)
167 * PM entry who are notified earlier to prepare for it. 150 * PM entry who are notified earlier to prepare for it.
168 */ 151 */
169 cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL); 152 cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
170 read_unlock(&cpu_pm_notifier_lock);
171 153
172 return ret; 154 return ret;
173} 155}
@@ -190,13 +172,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
190 */ 172 */
191int cpu_cluster_pm_exit(void) 173int cpu_cluster_pm_exit(void)
192{ 174{
193 int ret; 175 return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
194
195 read_lock(&cpu_pm_notifier_lock);
196 ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
197 read_unlock(&cpu_pm_notifier_lock);
198
199 return ret;
200} 176}
201EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit); 177EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
202 178