Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/cpufreq
[sfrench/cifs-2.6.git] / drivers / cpufreq / cpufreq.c
index 346906a96e8bdc6bc82c55b6d62cad5b09c1d4d8..25acf478c9e82c5f2d40cd6c89e9c8fcdb2862be 100644 (file)
@@ -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);
 
@@ -587,12 +593,11 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
                goto module_out;
        }
 
-       policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
+       policy = kzalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
        if (!policy) {
                ret = -ENOMEM;
                goto nomem_out;
        }
-       memset(policy, 0, sizeof(struct cpufreq_policy));
 
        policy->cpu = cpu;
        policy->cpus = cpumask_of_cpu(cpu);
@@ -673,7 +678,7 @@ err_out:
 
 nomem_out:
        module_put(cpufreq_driver->owner);
- module_out:
+module_out:
        cpufreq_debug_enable_ratelimit();
        return ret;
 }
@@ -762,7 +767,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 +1113,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 +1423,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 +1522,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 +1554,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;