Commit c32b6b8e authored by Ashok Raj's avatar Ashok Raj Committed by Linus Torvalds

[PATCH] create and destroy cpufreq sysfs entries based on cpu notifiers

cpufreq entries in sysfs should only be populated when CPU is online state.
 When we either boot with maxcpus=x and then boot the other cpus by echoing
to sysfs online file, these entries should be created and destroyed when
CPU_DEAD is notified.  Same treatement as cache entries under sysfs.

We place the processor in the lowest frequency, so hw managed P-State
transitions can still work on the other threads to save power.

Primary goal was to just make these directories appear/disapper dynamically.

There is one in this patch i had to do, which i really dont like myself but
probably best if someone handling the cpufreq infrastructure could give
this code right treatment if this is not acceptable.  I guess its probably
good for the first cut.

- Converting lock_cpu_hotplug()/unlock_cpu_hotplug() to disable/enable preempt.
  The locking was smack in the middle of the notification path, when the
  hotplug is already holding the lock. I tried another solution to avoid this
  so avoid taking locks if we know we are from notification path. The solution
  was getting very ugly and i decided this was probably good for this iteration
  until someone who understands cpufreq could do a better job than me.

(akpm: export cpucontrol to GPL modules: drivers/cpufreq/cpufreq_stats.c now
does lock_cpu_hotplug())
Signed-off-by: default avatarAshok Raj <ashok.raj@intel.com>
Signed-off-by: default avatarVenkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Cc: Dave Jones <davej@codemonkey.org.uk>
Cc: Zwane Mwaikambo <zwane@holomorphy.com>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent d434fca7
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
* Copyright (C) 2001 Russell King * Copyright (C) 2001 Russell King
* (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * (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 * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
...@@ -567,6 +570,9 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) ...@@ -567,6 +570,9 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
unsigned long flags; unsigned long flags;
unsigned int j; unsigned int j;
if (cpu_is_offline(cpu))
return 0;
cpufreq_debug_disable_ratelimit(); cpufreq_debug_disable_ratelimit();
dprintk("adding CPU %u\n", cpu); dprintk("adding CPU %u\n", cpu);
...@@ -673,7 +679,7 @@ static int cpufreq_add_dev (struct sys_device * sys_dev) ...@@ -673,7 +679,7 @@ static int cpufreq_add_dev (struct sys_device * sys_dev)
nomem_out: nomem_out:
module_put(cpufreq_driver->owner); module_put(cpufreq_driver->owner);
module_out: module_out:
cpufreq_debug_enable_ratelimit(); cpufreq_debug_enable_ratelimit();
return ret; return ret;
} }
...@@ -762,7 +768,6 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) ...@@ -762,7 +768,6 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev)
down(&data->lock); down(&data->lock);
if (cpufreq_driver->target) if (cpufreq_driver->target)
__cpufreq_governor(data, CPUFREQ_GOV_STOP); __cpufreq_governor(data, CPUFREQ_GOV_STOP);
cpufreq_driver->target = NULL;
up(&data->lock); up(&data->lock);
kobject_unregister(&data->kobj); kobject_unregister(&data->kobj);
...@@ -1109,17 +1114,30 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, ...@@ -1109,17 +1114,30 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int relation) unsigned int relation)
{ {
int retval = -EINVAL; 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, dprintk("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
target_freq, relation); target_freq, relation);
if (cpu_online(policy->cpu) && cpufreq_driver->target) if (cpu_online(policy->cpu) && cpufreq_driver->target)
retval = cpufreq_driver->target(policy, target_freq, relation); retval = cpufreq_driver->target(policy, target_freq, relation);
unlock_cpu_hotplug(); preempt_enable();
return retval; return retval;
} }
EXPORT_SYMBOL_GPL(__cpufreq_driver_target); EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
int cpufreq_driver_target(struct cpufreq_policy *policy, int cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
...@@ -1406,6 +1424,45 @@ int cpufreq_update_policy(unsigned int cpu) ...@@ -1406,6 +1424,45 @@ int cpufreq_update_policy(unsigned int cpu)
} }
EXPORT_SYMBOL(cpufreq_update_policy); 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 * * REGISTER / UNREGISTER CPUFREQ DRIVER *
...@@ -1466,6 +1523,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) ...@@ -1466,6 +1523,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
} }
if (!ret) { if (!ret) {
register_cpu_notifier(&cpufreq_cpu_notifier);
dprintk("driver %s up and running\n", driver_data->name); dprintk("driver %s up and running\n", driver_data->name);
cpufreq_debug_enable_ratelimit(); cpufreq_debug_enable_ratelimit();
} }
...@@ -1497,6 +1555,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) ...@@ -1497,6 +1555,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
dprintk("unregistering driver %s\n", driver->name); dprintk("unregistering driver %s\n", driver->name);
sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver); sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver);
unregister_cpu_notifier(&cpufreq_cpu_notifier);
spin_lock_irqsave(&cpufreq_driver_lock, flags); spin_lock_irqsave(&cpufreq_driver_lock, flags);
cpufreq_driver = NULL; cpufreq_driver = NULL;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/notifier.h>
#include <asm/cputime.h> #include <asm/cputime.h>
static spinlock_t cpufreq_stats_lock; static spinlock_t cpufreq_stats_lock;
...@@ -298,6 +299,27 @@ cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val, ...@@ -298,6 +299,27 @@ cpufreq_stat_notifier_trans (struct notifier_block *nb, unsigned long val,
return 0; 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 = { static struct notifier_block notifier_policy_block = {
.notifier_call = cpufreq_stat_notifier_policy .notifier_call = cpufreq_stat_notifier_policy
}; };
...@@ -311,6 +333,7 @@ __init cpufreq_stats_init(void) ...@@ -311,6 +333,7 @@ __init cpufreq_stats_init(void)
{ {
int ret; int ret;
unsigned int cpu; unsigned int cpu;
spin_lock_init(&cpufreq_stats_lock); spin_lock_init(&cpufreq_stats_lock);
if ((ret = cpufreq_register_notifier(&notifier_policy_block, if ((ret = cpufreq_register_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER))) CPUFREQ_POLICY_NOTIFIER)))
...@@ -323,20 +346,31 @@ __init cpufreq_stats_init(void) ...@@ -323,20 +346,31 @@ __init cpufreq_stats_init(void)
return ret; return ret;
} }
for_each_cpu(cpu) register_cpu_notifier(&cpufreq_stat_cpu_notifier);
cpufreq_update_policy(cpu); 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; return 0;
} }
static void static void
__exit cpufreq_stats_exit(void) __exit cpufreq_stats_exit(void)
{ {
unsigned int cpu; unsigned int cpu;
cpufreq_unregister_notifier(&notifier_policy_block, cpufreq_unregister_notifier(&notifier_policy_block,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
cpufreq_unregister_notifier(&notifier_trans_block, cpufreq_unregister_notifier(&notifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER); CPUFREQ_TRANSITION_NOTIFIER);
for_each_cpu(cpu) unregister_cpu_notifier(&cpufreq_stat_cpu_notifier);
cpufreq_stats_free_table(cpu); 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>"); MODULE_AUTHOR ("Zou Nan hai <nanhai.zou@intel.com>");
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
/* This protects CPUs going up and down... */ /* This protects CPUs going up and down... */
DECLARE_MUTEX(cpucontrol); DECLARE_MUTEX(cpucontrol);
EXPORT_SYMBOL_GPL(cpucontrol);
static struct notifier_block *cpu_chain; static struct notifier_block *cpu_chain;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment