Skip to content

Commit e9825d1

Browse files
committed
Merge tag 'pm-7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management fixes from Rafael Wysocki: "These fix an idle loop issue exposed by recent changes and a race condition related to device removal in the runtime PM core code: - Consolidate the handling of two special cases in the idle loop that occur when only one CPU idle state is present (Rafael Wysocki) - Fix a race condition related to device removal in the runtime PM core code that may cause a stale device object pointer to be dereferenced (Bart Van Assche)" * tag 'pm-7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: PM: runtime: Fix a race condition related to device removal sched: idle: Consolidate the handling of two special cases
2 parents d107dc8 + 9633370 commit e9825d1

2 files changed

Lines changed: 22 additions & 9 deletions

File tree

drivers/base/power/runtime.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1895,6 +1895,7 @@ void pm_runtime_reinit(struct device *dev)
18951895
void pm_runtime_remove(struct device *dev)
18961896
{
18971897
__pm_runtime_disable(dev, false);
1898+
flush_work(&dev->power.work);
18981899
pm_runtime_reinit(dev);
18991900
}
19001901

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)