Skip to content

Commit

Permalink
intel_idle: Convert to hotplug state machine
Browse files Browse the repository at this point in the history
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: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Acked-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Sebastian Andrzej Siewior authored and Rafael J. Wysocki committed Dec 1, 2016
1 parent 29d7bba commit fb1013a
Showing 1 changed file with 39 additions and 64 deletions.
103 changes: 39 additions & 64 deletions drivers/idle/intel_idle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
Expand Down Expand Up @@ -951,58 +949,23 @@ 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;

rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);
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;

Expand Down Expand Up @@ -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)) {
Expand All @@ -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)
Expand All @@ -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;
goto init_driver_fail;
}

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;
}
}
__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);

Expand Down

0 comments on commit fb1013a

Please sign in to comment.