|
@@ -4,6 +4,9 @@
|
|
|
* Copyright (C) 2001 Russell King
|
|
|
* (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
|
|
|
*
|
|
|
+ * Oct 2005 - Ashok Raj <ashok.raj@intel.com>
|
|
|
+ * Added handling for CPU hotplug
|
|
|
+ *
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
* published by the Free Software Foundation.
|
|
@@ -567,6 +570,9 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
|
|
|
unsigned long flags;
|
|
|
unsigned int j;
|
|
|
|
|
|
+ if (cpu_is_offline(cpu))
|
|
|
+ return 0;
|
|
|
+
|
|
|
cpufreq_debug_disable_ratelimit();
|
|
|
dprintk("adding CPU %u\n", cpu);
|
|
|
|
|
@@ -673,7 +679,7 @@ err_out:
|
|
|
|
|
|
nomem_out:
|
|
|
module_put(cpufreq_driver->owner);
|
|
|
- module_out:
|
|
|
+module_out:
|
|
|
cpufreq_debug_enable_ratelimit();
|
|
|
return ret;
|
|
|
}
|
|
@@ -762,7 +768,6 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev)
|
|
|
down(&data->lock);
|
|
|
if (cpufreq_driver->target)
|
|
|
__cpufreq_governor(data, CPUFREQ_GOV_STOP);
|
|
|
- cpufreq_driver->target = NULL;
|
|
|
up(&data->lock);
|
|
|
|
|
|
kobject_unregister(&data->kobj);
|
|
@@ -1109,17 +1114,30 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
|
|
|
unsigned int relation)
|
|
|
{
|
|
|
int retval = -EINVAL;
|
|
|
- lock_cpu_hotplug();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Converted the lock_cpu_hotplug to preempt_disable()
|
|
|
+ * and preempt_enable(). This is a bit kludgy and relies on how cpu
|
|
|
+ * hotplug works. All we need is a guarantee that cpu hotplug won't make
|
|
|
+ * progress on any cpu. Once we do preempt_disable(), this would ensure
|
|
|
+ * that hotplug threads don't get onto this cpu, thereby delaying
|
|
|
+ * the cpu remove process.
|
|
|
+ *
|
|
|
+ * We removed the lock_cpu_hotplug since we need to call this function
|
|
|
+ * via cpu hotplug callbacks, which result in locking the cpu hotplug
|
|
|
+ * thread itself. Agree this is not very clean, cpufreq community
|
|
|
+ * could improve this if required. - Ashok Raj <ashok.raj@intel.com>
|
|
|
+ */
|
|
|
+ preempt_disable();
|
|
|
dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
|
|
|
target_freq, relation);
|
|
|
if (cpu_online(policy->cpu) && cpufreq_driver->target)
|
|
|
retval = cpufreq_driver->target(policy, target_freq, relation);
|
|
|
- unlock_cpu_hotplug();
|
|
|
+ preempt_enable();
|
|
|
return retval;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
|
|
|
|
|
|
-
|
|
|
int cpufreq_driver_target(struct cpufreq_policy *policy,
|
|
|
unsigned int target_freq,
|
|
|
unsigned int relation)
|
|
@@ -1406,6 +1424,45 @@ int cpufreq_update_policy(unsigned int cpu)
|
|
|
}
|
|
|
EXPORT_SYMBOL(cpufreq_update_policy);
|
|
|
|
|
|
+static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb,
|
|
|
+ unsigned long action, void *hcpu)
|
|
|
+{
|
|
|
+ unsigned int cpu = (unsigned long)hcpu;
|
|
|
+ struct cpufreq_policy *policy;
|
|
|
+ struct sys_device *sys_dev;
|
|
|
+
|
|
|
+ sys_dev = get_cpu_sysdev(cpu);
|
|
|
+
|
|
|
+ if (sys_dev) {
|
|
|
+ switch (action) {
|
|
|
+ case CPU_ONLINE:
|
|
|
+ cpufreq_add_dev(sys_dev);
|
|
|
+ break;
|
|
|
+ case CPU_DOWN_PREPARE:
|
|
|
+ /*
|
|
|
+ * We attempt to put this cpu in lowest frequency
|
|
|
+ * possible before going down. This will permit
|
|
|
+ * hardware-managed P-State to switch other related
|
|
|
+ * threads to min or higher speeds if possible.
|
|
|
+ */
|
|
|
+ policy = cpufreq_cpu_data[cpu];
|
|
|
+ if (policy) {
|
|
|
+ cpufreq_driver_target(policy, policy->min,
|
|
|
+ CPUFREQ_RELATION_H);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CPU_DEAD:
|
|
|
+ cpufreq_remove_dev(sys_dev);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NOTIFY_OK;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block cpufreq_cpu_notifier =
|
|
|
+{
|
|
|
+ .notifier_call = cpufreq_cpu_callback,
|
|
|
+};
|
|
|
|
|
|
/*********************************************************************
|
|
|
* REGISTER / UNREGISTER CPUFREQ DRIVER *
|
|
@@ -1466,6 +1523,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
|
|
|
}
|
|
|
|
|
|
if (!ret) {
|
|
|
+ register_cpu_notifier(&cpufreq_cpu_notifier);
|
|
|
dprintk("driver %s up and running\n", driver_data->name);
|
|
|
cpufreq_debug_enable_ratelimit();
|
|
|
}
|
|
@@ -1497,6 +1555,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
|
|
|
dprintk("unregistering driver %s\n", driver->name);
|
|
|
|
|
|
sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver);
|
|
|
+ unregister_cpu_notifier(&cpufreq_cpu_notifier);
|
|
|
|
|
|
spin_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
cpufreq_driver = NULL;
|