Skip to content

Commit f4c31b0

Browse files
committed
sched: idle: Consolidate the handling of two special cases
There are two special cases in the idle loop that are handled inconsistently even though they are analogous. The first one is when a cpuidle driver is absent and the default CPU idle time power management implemented by the architecture code is used. In that case, the scheduler tick is stopped every time before invoking default_idle_call(). The second one is when a cpuidle driver is present, but there is only one idle state in its table. In that case, the scheduler tick is never stopped at all. Since each of these approaches has its drawbacks, reconcile them with the help of one simple heuristic. Namely, stop the tick if the CPU has been woken up by it in the previous iteration of the idle loop, or let it tick otherwise. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Christian Loehle <christian.loehle@arm.com> Reviewed-by: Frederic Weisbecker <frederic@kernel.org> Reviewed-by: Qais Yousef <qyousef@layalina.io> Reviewed-by: Aboorva Devarajan <aboorvad@linux.ibm.com> Fixes: ed98c34 ("sched: idle: Do not stop the tick before cpuidle_idle_call()") [ rjw: Added Fixes tag, changelog edits ] Link: https://patch.msgid.link/4741364.LvFx2qVVIh@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent f338e77 commit f4c31b0

1 file changed

Lines changed: 21 additions & 9 deletions

File tree

kernel/sched/idle.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
161161
return cpuidle_enter(drv, dev, next_state);
162162
}
163163

164+
static void idle_call_stop_or_retain_tick(bool stop_tick)
165+
{
166+
if (stop_tick || tick_nohz_tick_stopped())
167+
tick_nohz_idle_stop_tick();
168+
else
169+
tick_nohz_idle_retain_tick();
170+
}
171+
164172
/**
165173
* cpuidle_idle_call - the main idle function
166174
*
@@ -170,7 +178,7 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
170178
* set, and it returns with polling set. If it ever stops polling, it
171179
* must clear the polling bit.
172180
*/
173-
static void cpuidle_idle_call(void)
181+
static void cpuidle_idle_call(bool stop_tick)
174182
{
175183
struct cpuidle_device *dev = cpuidle_get_device();
176184
struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
@@ -186,7 +194,7 @@ static void cpuidle_idle_call(void)
186194
}
187195

188196
if (cpuidle_not_available(drv, dev)) {
189-
tick_nohz_idle_stop_tick();
197+
idle_call_stop_or_retain_tick(stop_tick);
190198

191199
default_idle_call();
192200
goto exit_idle;
@@ -222,25 +230,27 @@ static void cpuidle_idle_call(void)
222230
next_state = cpuidle_find_deepest_state(drv, dev, max_latency_ns);
223231
call_cpuidle(drv, dev, next_state);
224232
} else if (drv->state_count > 1) {
225-
bool stop_tick = true;
233+
/*
234+
* stop_tick is expected to be true by default by cpuidle
235+
* governors, which allows them to select idle states with
236+
* target residency above the tick period length.
237+
*/
238+
stop_tick = true;
226239

227240
/*
228241
* Ask the cpuidle framework to choose a convenient idle state.
229242
*/
230243
next_state = cpuidle_select(drv, dev, &stop_tick);
231244

232-
if (stop_tick || tick_nohz_tick_stopped())
233-
tick_nohz_idle_stop_tick();
234-
else
235-
tick_nohz_idle_retain_tick();
245+
idle_call_stop_or_retain_tick(stop_tick);
236246

237247
entered_state = call_cpuidle(drv, dev, next_state);
238248
/*
239249
* Give the governor an opportunity to reflect on the outcome
240250
*/
241251
cpuidle_reflect(dev, entered_state);
242252
} else {
243-
tick_nohz_idle_retain_tick();
253+
idle_call_stop_or_retain_tick(stop_tick);
244254

245255
/*
246256
* If there is only a single idle state (or none), there is
@@ -268,6 +278,7 @@ static void cpuidle_idle_call(void)
268278
static void do_idle(void)
269279
{
270280
int cpu = smp_processor_id();
281+
bool got_tick = false;
271282

272283
/*
273284
* Check if we need to update blocked load
@@ -338,8 +349,9 @@ static void do_idle(void)
338349
tick_nohz_idle_restart_tick();
339350
cpu_idle_poll();
340351
} else {
341-
cpuidle_idle_call();
352+
cpuidle_idle_call(got_tick);
342353
}
354+
got_tick = tick_nohz_idle_got_tick();
343355
arch_cpu_idle_exit();
344356
}
345357

0 commit comments

Comments
 (0)