aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/cpufreq/cpufreq.c69
-rw-r--r--drivers/cpufreq/cpufreq_stats.c42
-rw-r--r--kernel/cpu.c1
3 files changed, 103 insertions, 9 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 346906a96e8b..6c6121b85a54 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -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;
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 741b6b191e6a..3597f25d5efa 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -19,6 +19,7 @@
#include <linux/percpu.h>
#include <linux/kobject.h>
#include <linux/spinlock.h>
+#include <linux/notifier.h>
#include <asm/cputime.h>
static spinlock_t cpufreq_stats_lock;
@@ -298,6 +299,27 @@ cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
return 0;
}
+static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ cpufreq_update_policy(cpu);
+ break;
+ case CPU_DEAD:
+ cpufreq_stats_free_table(cpu);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_stat_cpu_notifier =
+{
+ .notifier_call = cpufreq_stat_cpu_callback,
+};
+
static struct notifier_block notifier_policy_block = {
.notifier_call = cpufreq_stat_notifier_policy
};
@@ -311,6 +333,7 @@ __init cpufreq_stats_init(void)
{
int ret;
unsigned int cpu;
+
spin_lock_init(&cpufreq_stats_lock);
if ((ret = cpufreq_register_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER)))
@@ -323,20 +346,31 @@ __init cpufreq_stats_init(void)
return ret;
}
- for_each_cpu(cpu)
- cpufreq_update_policy(cpu);
+ register_cpu_notifier(&cpufreq_stat_cpu_notifier);
+ lock_cpu_hotplug();
+ for_each_online_cpu(cpu) {
+ cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_ONLINE,
+ (void *)(long)cpu);
+ }
+ unlock_cpu_hotplug();
return 0;
}
static void
__exit cpufreq_stats_exit(void)
{
unsigned int cpu;
+
cpufreq_unregister_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER);
cpufreq_unregister_notifier(&notifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER);
- for_each_cpu(cpu)
- cpufreq_stats_free_table(cpu);
+ unregister_cpu_notifier(&cpufreq_stat_cpu_notifier);
+ lock_cpu_hotplug();
+ for_each_online_cpu(cpu) {
+ cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_DEAD,
+ (void *)(long)cpu);
+ }
+ unlock_cpu_hotplug();
}
MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 53d8263ae12e..3619e939182e 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -17,6 +17,7 @@
/* This protects CPUs going up and down... */
DECLARE_MUTEX(cpucontrol);
+EXPORT_SYMBOL_GPL(cpucontrol);
static struct notifier_block *cpu_chain;