Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fb1013a0 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Rafael J. Wysocki
Browse files

intel_idle: Convert to hotplug state machine



Install the callbacks via the state machine and let the core invoke the
callbacks on the already online CPUs.

The two smp_call_function_single() invocations in intel_idle_cpu_init() have
been removed because intel_idle_cpu_init() is now invoked via the hotplug
callback which runs on the target CPU. The IRQ-off calling convention for
auto_demotion_disable() and c1e_promotion_disable() has not been preserved
because only those two modify the MSR during CPU intialization.

Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: default avatarJacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 29d7bbad
Loading
Loading
Loading
Loading
+39 −64
Original line number Diff line number Diff line
@@ -98,8 +98,6 @@ static int intel_idle(struct cpuidle_device *dev,
			struct cpuidle_driver *drv, int index);
static void intel_idle_freeze(struct cpuidle_device *dev,
			      struct cpuidle_driver *drv, int index);
static int intel_idle_cpu_init(int cpu);

static struct cpuidle_state *cpuidle_state_table;

/*
@@ -951,50 +949,15 @@ static void intel_idle_freeze(struct cpuidle_device *dev,
	mwait_idle_with_hints(eax, ecx);
}

static void __setup_broadcast_timer(void *arg)
static void __setup_broadcast_timer(bool on)
{
	unsigned long on = (unsigned long)arg;

	if (on)
		tick_broadcast_enable();
	else
		tick_broadcast_disable();
}

static int cpu_hotplug_notify(struct notifier_block *n,
			      unsigned long action, void *hcpu)
{
	int hotcpu = (unsigned long)hcpu;
	struct cpuidle_device *dev;

	switch (action & ~CPU_TASKS_FROZEN) {
	case CPU_ONLINE:

		if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE)
			__setup_broadcast_timer((void *)true);

		/*
		 * Some systems can hotplug a cpu at runtime after
		 * the kernel has booted, we have to initialize the
		 * driver in this case
		 */
		dev = per_cpu_ptr(intel_idle_cpuidle_devices, hotcpu);
		if (dev->registered)
			break;

		if (intel_idle_cpu_init(hotcpu))
			return NOTIFY_BAD;

		break;
	}
	return NOTIFY_OK;
}

static struct notifier_block cpu_hotplug_notifier = {
	.notifier_call = cpu_hotplug_notify,
};

static void auto_demotion_disable(void *dummy)
static void auto_demotion_disable(void)
{
	unsigned long long msr_bits;

@@ -1002,7 +965,7 @@ static void auto_demotion_disable(void *dummy)
	msr_bits &= ~(icpu->auto_demotion_disable_flags);
	wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
}
static void c1e_promotion_disable(void *dummy)
static void c1e_promotion_disable(void)
{
	unsigned long long msr_bits;

@@ -1422,12 +1385,11 @@ static void __init intel_idle_cpuidle_driver_init(void)
 * allocate, initialize, register cpuidle_devices
 * @cpu: cpu/core to initialize
 */
static int intel_idle_cpu_init(int cpu)
static int intel_idle_cpu_init(unsigned int cpu)
{
	struct cpuidle_device *dev;

	dev = per_cpu_ptr(intel_idle_cpuidle_devices, cpu);

	dev->cpu = cpu;

	if (cpuidle_register_device(dev)) {
@@ -1436,17 +1398,36 @@ static int intel_idle_cpu_init(int cpu)
	}

	if (icpu->auto_demotion_disable_flags)
		smp_call_function_single(cpu, auto_demotion_disable, NULL, 1);
		auto_demotion_disable();

	if (icpu->disable_promotion_to_c1e)
		smp_call_function_single(cpu, c1e_promotion_disable, NULL, 1);
		c1e_promotion_disable();

	return 0;
}

static int intel_idle_cpu_online(unsigned int cpu)
{
	struct cpuidle_device *dev;

	if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE)
		__setup_broadcast_timer(true);

	/*
	 * Some systems can hotplug a cpu at runtime after
	 * the kernel has booted, we have to initialize the
	 * driver in this case
	 */
	dev = per_cpu_ptr(intel_idle_cpuidle_devices, cpu);
	if (!dev->registered)
		return intel_idle_cpu_init(cpu);

	return 0;
}

static int __init intel_idle_init(void)
{
	int retval, i;
	int retval;

	/* Do not load intel_idle at all for now if idle= is passed */
	if (boot_option_idle_override != IDLE_NO_OVERRIDE)
@@ -1466,35 +1447,29 @@ static int __init intel_idle_init(void)
		struct cpuidle_driver *drv = cpuidle_get_driver();
		printk(KERN_DEBUG PREFIX "intel_idle yielding to %s",
			drv ? drv->name : "none");
		free_percpu(intel_idle_cpuidle_devices);
		return retval;
	}

	cpu_notifier_register_begin();

	for_each_online_cpu(i) {
		retval = intel_idle_cpu_init(i);
		if (retval) {
			intel_idle_cpuidle_devices_uninit();
			cpu_notifier_register_done();
			cpuidle_unregister_driver(&intel_idle_driver);
			free_percpu(intel_idle_cpuidle_devices);
			return retval;
		}
		goto init_driver_fail;
	}
	__register_cpu_notifier(&cpu_hotplug_notifier);

	if (boot_cpu_has(X86_FEATURE_ARAT))	/* Always Reliable APIC Timer */
		lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE;
	else
		on_each_cpu(__setup_broadcast_timer, (void *)true, 1);

	cpu_notifier_register_done();
	retval = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "idle/intel:online",
				   intel_idle_cpu_online, NULL);
	if (retval < 0)
		goto hp_setup_fail;

	pr_debug(PREFIX "lapic_timer_reliable_states 0x%x\n",
		lapic_timer_reliable_states);

	return 0;

hp_setup_fail:
	intel_idle_cpuidle_devices_uninit();
	cpuidle_unregister_driver(&intel_idle_driver);
init_driver_fail:
	free_percpu(intel_idle_cpuidle_devices);
	return retval;

}
device_initcall(intel_idle_init);