aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-03-02 16:26:55 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-03-05 17:13:19 -0500
commitef2b22ac540c018bd574d1846ab95b9bfcf38702 (patch)
treecfd0282b0bb687f84364d70333a89dafff1fb6ca /kernel
parentdfcacc154fb38fdb2c243c3dbbdc1f26a64cedc8 (diff)
cpuidle / sleep: Use broadcast timer for states that stop local timer
Commit 381063133246 (PM / sleep: Re-implement suspend-to-idle handling) overlooked the fact that entering some sufficiently deep idle states by CPUs may cause their local timers to stop and in those cases it is necessary to switch over to a broadcast timer prior to entering the idle state. If the cpuidle driver in use does not provide the new ->enter_freeze callback for any of the idle states, that problem affects suspend-to-idle too, but it is not taken into account after the changes made by commit 381063133246. Fix that by changing the definition of cpuidle_enter_freeze() and re-arranging of the code in cpuidle_idle_call(), so the former does not call cpuidle_enter() any more and the fallback case is handled by cpuidle_idle_call() directly. Fixes: 381063133246 (PM / sleep: Re-implement suspend-to-idle handling) Reported-and-tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/sched/idle.c30
1 files changed, 21 insertions, 9 deletions
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
index 84b93b68482a..80014a178342 100644
--- a/kernel/sched/idle.c
+++ b/kernel/sched/idle.c
@@ -82,6 +82,7 @@ static void cpuidle_idle_call(void)
82 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); 82 struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
83 int next_state, entered_state; 83 int next_state, entered_state;
84 unsigned int broadcast; 84 unsigned int broadcast;
85 bool reflect;
85 86
86 /* 87 /*
87 * Check if the idle task must be rescheduled. If it is the 88 * Check if the idle task must be rescheduled. If it is the
@@ -105,6 +106,9 @@ static void cpuidle_idle_call(void)
105 */ 106 */
106 rcu_idle_enter(); 107 rcu_idle_enter();
107 108
109 if (cpuidle_not_available(drv, dev))
110 goto use_default;
111
108 /* 112 /*
109 * Suspend-to-idle ("freeze") is a system state in which all user space 113 * Suspend-to-idle ("freeze") is a system state in which all user space
110 * has been frozen, all I/O devices have been suspended and the only 114 * has been frozen, all I/O devices have been suspended and the only
@@ -115,15 +119,22 @@ static void cpuidle_idle_call(void)
115 * until a proper wakeup interrupt happens. 119 * until a proper wakeup interrupt happens.
116 */ 120 */
117 if (idle_should_freeze()) { 121 if (idle_should_freeze()) {
118 cpuidle_enter_freeze(); 122 entered_state = cpuidle_enter_freeze(drv, dev);
119 goto exit_idle; 123 if (entered_state >= 0) {
120 } 124 local_irq_enable();
125 goto exit_idle;
126 }
121 127
122 /* 128 reflect = false;
123 * Ask the cpuidle framework to choose a convenient idle state. 129 next_state = cpuidle_find_deepest_state(drv, dev);
124 * Fall back to the default arch idle method on errors. 130 } else {
125 */ 131 reflect = true;
126 next_state = cpuidle_select(drv, dev); 132 /*
133 * Ask the cpuidle framework to choose a convenient idle state.
134 */
135 next_state = cpuidle_select(drv, dev);
136 }
137 /* Fall back to the default arch idle method on errors. */
127 if (next_state < 0) 138 if (next_state < 0)
128 goto use_default; 139 goto use_default;
129 140
@@ -170,7 +181,8 @@ static void cpuidle_idle_call(void)
170 /* 181 /*
171 * Give the governor an opportunity to reflect on the outcome 182 * Give the governor an opportunity to reflect on the outcome
172 */ 183 */
173 cpuidle_reflect(dev, entered_state); 184 if (reflect)
185 cpuidle_reflect(dev, entered_state);
174 186
175exit_idle: 187exit_idle:
176 __current_set_polling(); 188 __current_set_polling();