Commit e9825d1c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents d107dc8c 96333706
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1895,6 +1895,7 @@ void pm_runtime_reinit(struct device *dev)
void pm_runtime_remove(struct device *dev)
{
	__pm_runtime_disable(dev, false);
	flush_work(&dev->power.work);
	pm_runtime_reinit(dev);
}

+21 −9
Original line number Diff line number Diff line
@@ -161,6 +161,14 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
	return cpuidle_enter(drv, dev, next_state);
}

static void idle_call_stop_or_retain_tick(bool stop_tick)
{
	if (stop_tick || tick_nohz_tick_stopped())
		tick_nohz_idle_stop_tick();
	else
		tick_nohz_idle_retain_tick();
}

/**
 * cpuidle_idle_call - the main idle function
 *
@@ -170,7 +178,7 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
 * set, and it returns with polling set.  If it ever stops polling, it
 * must clear the polling bit.
 */
static void cpuidle_idle_call(void)
static void cpuidle_idle_call(bool stop_tick)
{
	struct cpuidle_device *dev = cpuidle_get_device();
	struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev);
@@ -186,7 +194,7 @@ static void cpuidle_idle_call(void)
	}

	if (cpuidle_not_available(drv, dev)) {
		tick_nohz_idle_stop_tick();
		idle_call_stop_or_retain_tick(stop_tick);

		default_idle_call();
		goto exit_idle;
@@ -222,17 +230,19 @@ static void cpuidle_idle_call(void)
		next_state = cpuidle_find_deepest_state(drv, dev, max_latency_ns);
		call_cpuidle(drv, dev, next_state);
	} else if (drv->state_count > 1) {
		bool stop_tick = true;
		/*
		 * stop_tick is expected to be true by default by cpuidle
		 * governors, which allows them to select idle states with
		 * target residency above the tick period length.
		 */
		stop_tick = true;

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

		if (stop_tick || tick_nohz_tick_stopped())
			tick_nohz_idle_stop_tick();
		else
			tick_nohz_idle_retain_tick();
		idle_call_stop_or_retain_tick(stop_tick);

		entered_state = call_cpuidle(drv, dev, next_state);
		/*
@@ -240,7 +250,7 @@ static void cpuidle_idle_call(void)
		 */
		cpuidle_reflect(dev, entered_state);
	} else {
		tick_nohz_idle_retain_tick();
		idle_call_stop_or_retain_tick(stop_tick);

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

	/*
	 * Check if we need to update blocked load
@@ -338,8 +349,9 @@ static void do_idle(void)
			tick_nohz_idle_restart_tick();
			cpu_idle_poll();
		} else {
			cpuidle_idle_call();
			cpuidle_idle_call(got_tick);
		}
		got_tick = tick_nohz_idle_got_tick();
		arch_cpu_idle_exit();
	}