Commit 68b7d00f authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds

[CPUFREQ] updated cpufreq ref-counting and locking scheme

This patch takes use of the now-working cpufreq_interface.kset and
cpufreq_policy.kobj to use reference counting within the cpufreq core
wherever this is more appropriate than the previous approach -- using one
semaphore. Additionally, the callbacks to the driver modules are protected
now.
parent 4efee5ca
...@@ -619,6 +619,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = { ...@@ -619,6 +619,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.init = acpi_cpufreq_cpu_init, .init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit, .exit = acpi_cpufreq_cpu_exit,
.name = "acpi-cpufreq", .name = "acpi-cpufreq",
.owner = THIS_MODULE,
}; };
......
...@@ -250,6 +250,7 @@ static struct cpufreq_driver elanfreq_driver = { ...@@ -250,6 +250,7 @@ static struct cpufreq_driver elanfreq_driver = {
.target = elanfreq_target, .target = elanfreq_target,
.init = elanfreq_cpu_init, .init = elanfreq_cpu_init,
.name = "elanfreq", .name = "elanfreq",
.owner = THIS_MODULE,
}; };
......
...@@ -451,6 +451,7 @@ static struct cpufreq_driver gx_suspmod_driver = { ...@@ -451,6 +451,7 @@ static struct cpufreq_driver gx_suspmod_driver = {
.target = cpufreq_gx_target, .target = cpufreq_gx_target,
.init = cpufreq_gx_cpu_init, .init = cpufreq_gx_cpu_init,
.name = "gx-suspmod", .name = "gx-suspmod",
.owner = THIS_MODULE,
}; };
static int __init cpufreq_gx_init(void) static int __init cpufreq_gx_init(void)
......
...@@ -649,6 +649,7 @@ static struct cpufreq_driver longhaul_driver = { ...@@ -649,6 +649,7 @@ static struct cpufreq_driver longhaul_driver = {
.target = longhaul_target, .target = longhaul_target,
.init = longhaul_cpu_init, .init = longhaul_cpu_init,
.name = "longhaul", .name = "longhaul",
.owner = THIS_MODULE,
}; };
static int __init longhaul_init (void) static int __init longhaul_init (void)
......
...@@ -253,6 +253,7 @@ static struct cpufreq_driver longrun_driver = { ...@@ -253,6 +253,7 @@ static struct cpufreq_driver longrun_driver = {
.setpolicy = longrun_set_policy, .setpolicy = longrun_set_policy,
.init = longrun_cpu_init, .init = longrun_cpu_init,
.name = "longrun", .name = "longrun",
.owner = THIS_MODULE,
}; };
......
...@@ -236,6 +236,7 @@ static struct cpufreq_driver p4clockmod_driver = { ...@@ -236,6 +236,7 @@ static struct cpufreq_driver p4clockmod_driver = {
.init = cpufreq_p4_cpu_init, .init = cpufreq_p4_cpu_init,
.exit = cpufreq_p4_cpu_exit, .exit = cpufreq_p4_cpu_exit,
.name = "p4-clockmod", .name = "p4-clockmod",
.owner = THIS_MODULE,
}; };
......
...@@ -190,6 +190,7 @@ static struct cpufreq_driver powernow_k6_driver = { ...@@ -190,6 +190,7 @@ static struct cpufreq_driver powernow_k6_driver = {
.init = powernow_k6_cpu_init, .init = powernow_k6_cpu_init,
.exit = powernow_k6_cpu_exit, .exit = powernow_k6_cpu_exit,
.name = "powernow-k6", .name = "powernow-k6",
.owner = THIS_MODULE,
}; };
......
...@@ -377,6 +377,7 @@ static struct cpufreq_driver powernow_driver = { ...@@ -377,6 +377,7 @@ static struct cpufreq_driver powernow_driver = {
.target = powernow_target, .target = powernow_target,
.init = powernow_cpu_init, .init = powernow_cpu_init,
.name = "powernow-k7", .name = "powernow-k7",
.owner = THIS_MODULE,
}; };
static int __init powernow_init (void) static int __init powernow_init (void)
......
...@@ -658,6 +658,7 @@ static struct cpufreq_driver speedstep_driver = { ...@@ -658,6 +658,7 @@ static struct cpufreq_driver speedstep_driver = {
.verify = speedstep_verify, .verify = speedstep_verify,
.target = speedstep_target, .target = speedstep_target,
.init = speedstep_cpu_init, .init = speedstep_cpu_init,
.owner = THIS_MODULE,
}; };
......
...@@ -276,6 +276,7 @@ static int __init us3freq_init(void) ...@@ -276,6 +276,7 @@ static int __init us3freq_init(void)
driver->target = us3freq_target; driver->target = us3freq_target;
driver->init = us3freq_cpu_init; driver->init = us3freq_cpu_init;
driver->exit = us3freq_cpu_exit; driver->exit = us3freq_cpu_exit;
driver->owner = THIS_MODULE,
strcpy(driver->name, "UltraSPARC-III"); strcpy(driver->name, "UltraSPARC-III");
cpufreq_us3_driver = driver; cpufreq_us3_driver = driver;
......
...@@ -113,7 +113,7 @@ int cpufreq_set(unsigned int freq, unsigned int cpu) ...@@ -113,7 +113,7 @@ int cpufreq_set(unsigned int freq, unsigned int cpu)
if (freq > cpu_max_freq[cpu]) if (freq > cpu_max_freq[cpu])
freq = cpu_max_freq[cpu]; freq = cpu_max_freq[cpu];
ret = cpufreq_driver_target_l(&current_policy[cpu], freq, ret = cpufreq_driver_target(&current_policy[cpu], freq,
CPUFREQ_RELATION_L); CPUFREQ_RELATION_L);
err: err:
......
...@@ -70,6 +70,8 @@ struct cpufreq_policy { ...@@ -70,6 +70,8 @@ struct cpufreq_policy {
struct cpufreq_cpuinfo cpuinfo; /* see above */ struct cpufreq_cpuinfo cpuinfo; /* see above */
struct device * dev; struct device * dev;
struct kobject kobj; struct kobject kobj;
struct semaphore lock; /* CPU ->setpolicy or ->target may
only be called once a time */
}; };
#define CPUFREQ_ADJUST (0) #define CPUFREQ_ADJUST (0)
...@@ -132,18 +134,13 @@ struct cpufreq_governor { ...@@ -132,18 +134,13 @@ struct cpufreq_governor {
}; };
/* pass a target to the cpufreq driver /* pass a target to the cpufreq driver
* _l : (cpufreq_driver_sem is not held)
*/ */
inline int cpufreq_driver_target(struct cpufreq_policy *policy, inline int cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation); unsigned int relation);
inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation);
/* pass an event to the cpufreq governor */ /* pass an event to the cpufreq governor */
int cpufreq_governor_l(unsigned int cpu, unsigned int event); int cpufreq_governor(unsigned int cpu, unsigned int event);
int cpufreq_register_governor(struct cpufreq_governor *governor); int cpufreq_register_governor(struct cpufreq_governor *governor);
void cpufreq_unregister_governor(struct cpufreq_governor *governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor);
...@@ -165,6 +162,7 @@ struct cpufreq_driver { ...@@ -165,6 +162,7 @@ struct cpufreq_driver {
int (*target) (struct cpufreq_policy *policy, int (*target) (struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation); unsigned int relation);
struct module *owner;
/* optional, for the moment */ /* optional, for the moment */
int (*init) (struct cpufreq_policy *policy); int (*init) (struct cpufreq_policy *policy);
int (*exit) (struct cpufreq_policy *policy); int (*exit) (struct cpufreq_policy *policy);
......
...@@ -43,12 +43,40 @@ static DECLARE_MUTEX (cpufreq_driver_sem); ...@@ -43,12 +43,40 @@ static DECLARE_MUTEX (cpufreq_driver_sem);
*/ */
static struct notifier_block *cpufreq_policy_notifier_list; static struct notifier_block *cpufreq_policy_notifier_list;
static struct notifier_block *cpufreq_transition_notifier_list; static struct notifier_block *cpufreq_transition_notifier_list;
static DECLARE_MUTEX (cpufreq_notifier_sem); static DECLARE_RWSEM (cpufreq_notifier_rwsem);
LIST_HEAD(cpufreq_governor_list); LIST_HEAD(cpufreq_governor_list);
static DECLARE_MUTEX (cpufreq_governor_sem);
static int cpufreq_governor(unsigned int cpu, unsigned int event); static struct device_interface cpufreq_interface;
static int cpufreq_cpu_get(unsigned int cpu) {
if (cpu >= NR_CPUS)
return 0;
if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!try_module_get(cpufreq_driver->owner)) {
kset_put(&cpufreq_interface.kset);
return 0;
}
if (!kobject_get(&cpufreq_driver->policy[cpu].kobj)) {
module_put(cpufreq_driver->owner);
kset_put(&cpufreq_interface.kset);
return 0;
}
return 1;
}
static void cpufreq_cpu_put(unsigned int cpu) {
kobject_put(&cpufreq_driver->policy[cpu].kobj);
module_put(cpufreq_driver->owner);
kset_put(&cpufreq_interface.kset);
}
/********************************************************************* /*********************************************************************
* SYSFS INTERFACE * * SYSFS INTERFACE *
...@@ -67,19 +95,19 @@ int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpu ...@@ -67,19 +95,19 @@ int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpu
return 0; return 0;
} else { } else {
struct cpufreq_governor *t; struct cpufreq_governor *t;
down(&cpufreq_driver_sem); down(&cpufreq_governor_sem);
if (!cpufreq_driver || !cpufreq_driver->target) if (!cpufreq_driver || !cpufreq_driver->target)
goto out; goto out;
list_for_each_entry(t, &cpufreq_governor_list, governor_list) { list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) {
*governor = t; *governor = t;
*policy = CPUFREQ_POLICY_GOVERNOR; *policy = CPUFREQ_POLICY_GOVERNOR;
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
return 0; return 0;
} }
} }
out: out:
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
} }
return -EINVAL; return -EINVAL;
} }
...@@ -120,14 +148,7 @@ static inline int to_cpu_nr (struct device *dev) ...@@ -120,14 +148,7 @@ static inline int to_cpu_nr (struct device *dev)
static ssize_t show_##file_name \ static ssize_t show_##file_name \
(struct cpufreq_policy * policy, char *buf) \ (struct cpufreq_policy * policy, char *buf) \
{ \ { \
unsigned int value = 0; \ return sprintf (buf, "%u\n", policy->object); \
\
down(&cpufreq_driver_sem); \
if (cpufreq_driver) \
value = policy->object; \
up(&cpufreq_driver_sem); \
\
return sprintf (buf, "%u\n", value); \
} }
show_one(cpuinfo_min_freq, cpuinfo.min_freq); show_one(cpuinfo_min_freq, cpuinfo.min_freq);
...@@ -147,7 +168,7 @@ static ssize_t store_##file_name \ ...@@ -147,7 +168,7 @@ static ssize_t store_##file_name \
\ \
ret = cpufreq_get_policy(&new_policy, policy->cpu); \ ret = cpufreq_get_policy(&new_policy, policy->cpu); \
if (ret) \ if (ret) \
return ret; \ return -EINVAL; \
\ \
ret = sscanf (buf, "%u", &new_policy.object); \ ret = sscanf (buf, "%u", &new_policy.object); \
if (ret != 1) \ if (ret != 1) \
...@@ -166,26 +187,16 @@ store_one(scaling_max_freq,max); ...@@ -166,26 +187,16 @@ store_one(scaling_max_freq,max);
*/ */
static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf) static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf)
{ {
unsigned int value = 0; switch (policy->policy) {
char value2[CPUFREQ_NAME_LEN];
down(&cpufreq_driver_sem);
if (cpufreq_driver)
value = policy->policy;
if (value == CPUFREQ_POLICY_GOVERNOR)
strncpy(value2, policy->governor->name, CPUFREQ_NAME_LEN);
up(&cpufreq_driver_sem);
switch (value) {
case CPUFREQ_POLICY_POWERSAVE: case CPUFREQ_POLICY_POWERSAVE:
return sprintf(buf, "powersave\n"); return sprintf(buf, "powersave\n");
case CPUFREQ_POLICY_PERFORMANCE: case CPUFREQ_POLICY_PERFORMANCE:
return sprintf(buf, "performance\n"); return sprintf(buf, "performance\n");
case CPUFREQ_POLICY_GOVERNOR: case CPUFREQ_POLICY_GOVERNOR:
return sprintf(buf, "%s\n", value2); return snprintf(buf, CPUFREQ_NAME_LEN, "%s\n", policy->governor->name);
default:
return -EINVAL;
} }
return -EINVAL;
} }
...@@ -220,14 +231,7 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy, ...@@ -220,14 +231,7 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy,
*/ */
static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf) static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf)
{ {
char value[CPUFREQ_NAME_LEN]; return snprintf(buf, CPUFREQ_NAME_LEN, "%s\n", cpufreq_driver->name);
down(&cpufreq_driver_sem);
if (cpufreq_driver)
strncpy(value, cpufreq_driver->name, CPUFREQ_NAME_LEN);
up(&cpufreq_driver_sem);
return sprintf(buf, "%s\n", value);
} }
/** /**
...@@ -240,8 +244,7 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy * policy, ...@@ -240,8 +244,7 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy * policy,
i += sprintf(buf, "performance powersave"); i += sprintf(buf, "performance powersave");
down(&cpufreq_driver_sem); if (!cpufreq_driver->target)
if (!cpufreq_driver || !cpufreq_driver->target)
goto out; goto out;
list_for_each_entry(t, &cpufreq_governor_list, governor_list) { list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
...@@ -250,7 +253,6 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy * policy, ...@@ -250,7 +253,6 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy * policy,
i += snprintf(&buf[i], CPUFREQ_NAME_LEN, " %s", t->name); i += snprintf(&buf[i], CPUFREQ_NAME_LEN, " %s", t->name);
} }
out: out:
up(&cpufreq_driver_sem);
i += sprintf(&buf[i], "\n"); i += sprintf(&buf[i], "\n");
return i; return i;
} }
...@@ -288,7 +290,6 @@ static struct attribute * default_attrs[] = { ...@@ -288,7 +290,6 @@ static struct attribute * default_attrs[] = {
NULL NULL
}; };
#define to_policy(k) container_of(k,struct cpufreq_policy,kobj) #define to_policy(k) container_of(k,struct cpufreq_policy,kobj)
#define to_attr(a) container_of(a,struct freq_attr,attr) #define to_attr(a) container_of(a,struct freq_attr,attr)
...@@ -296,7 +297,12 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf) ...@@ -296,7 +297,12 @@ static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf)
{ {
struct cpufreq_policy * policy = to_policy(kobj); struct cpufreq_policy * policy = to_policy(kobj);
struct freq_attr * fattr = to_attr(attr); struct freq_attr * fattr = to_attr(attr);
return fattr->show ? fattr->show(policy,buf) : 0; ssize_t ret;
if (!cpufreq_cpu_get(policy->cpu))
return -EINVAL;
ret = fattr->show ? fattr->show(policy,buf) : 0;
cpufreq_cpu_put(policy->cpu);
return ret;
} }
static ssize_t store(struct kobject * kobj, struct attribute * attr, static ssize_t store(struct kobject * kobj, struct attribute * attr,
...@@ -304,7 +310,12 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr, ...@@ -304,7 +310,12 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr,
{ {
struct cpufreq_policy * policy = to_policy(kobj); struct cpufreq_policy * policy = to_policy(kobj);
struct freq_attr * fattr = to_attr(attr); struct freq_attr * fattr = to_attr(attr);
return fattr->store ? fattr->store(policy,buf,count) : 0; ssize_t ret;
if (!cpufreq_cpu_get(policy->cpu))
return -EINVAL;
ret = fattr->store ? fattr->store(policy,buf,count) : 0;
cpufreq_cpu_put(policy->cpu);
return ret;
} }
static struct sysfs_ops sysfs_ops = { static struct sysfs_ops sysfs_ops = {
...@@ -330,9 +341,11 @@ static int cpufreq_add_dev (struct device * dev) ...@@ -330,9 +341,11 @@ static int cpufreq_add_dev (struct device * dev)
struct cpufreq_policy new_policy; struct cpufreq_policy new_policy;
struct cpufreq_policy *policy; struct cpufreq_policy *policy;
down(&cpufreq_driver_sem); if (!kset_get(&cpufreq_interface.kset))
if (!cpufreq_driver) { return -EINVAL;
up(&cpufreq_driver_sem);
if (!try_module_get(cpufreq_driver->owner)) {
kset_put(&cpufreq_interface.kset);
return -EINVAL; return -EINVAL;
} }
...@@ -343,26 +356,18 @@ static int cpufreq_add_dev (struct device * dev) ...@@ -343,26 +356,18 @@ static int cpufreq_add_dev (struct device * dev)
policy->cpu = cpu; policy->cpu = cpu;
if (cpufreq_driver->init) { if (cpufreq_driver->init) {
ret = cpufreq_driver->init(policy); ret = cpufreq_driver->init(policy);
if (ret) { if (ret)
up(&cpufreq_driver_sem); goto out;
return -ENODEV;
}
} }
/* set default policy on this CPU */ /* set default policy on this CPU */
down(&cpufreq_driver_sem);
memcpy(&new_policy, memcpy(&new_policy,
policy, policy,
sizeof(struct cpufreq_policy)); sizeof(struct cpufreq_policy));
if (cpufreq_driver->target)
cpufreq_governor(cpu, CPUFREQ_GOV_START);
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
ret = cpufreq_set_policy(&new_policy);
if (ret)
return -EINVAL;
down(&cpufreq_driver_sem);
init_MUTEX(&policy->lock);
/* prepare interface data */ /* prepare interface data */
policy->kobj.parent = &dev->kobj; policy->kobj.parent = &dev->kobj;
policy->kobj.ktype = &ktype_cpufreq; policy->kobj.ktype = &ktype_cpufreq;
...@@ -371,8 +376,17 @@ static int cpufreq_add_dev (struct device * dev) ...@@ -371,8 +376,17 @@ static int cpufreq_add_dev (struct device * dev)
cpufreq_interface.name, KOBJ_NAME_LEN); cpufreq_interface.name, KOBJ_NAME_LEN);
ret = kobject_register(&policy->kobj); ret = kobject_register(&policy->kobj);
if (ret)
goto out;
/* set default policy */
ret = cpufreq_set_policy(&new_policy);
if (ret)
kobject_unregister(&policy->kobj);
up(&cpufreq_driver_sem); out:
module_put(cpufreq_driver->owner);
kset_put(&cpufreq_interface.kset);
return ret; return ret;
} }
...@@ -380,21 +394,39 @@ static int cpufreq_add_dev (struct device * dev) ...@@ -380,21 +394,39 @@ static int cpufreq_add_dev (struct device * dev)
/** /**
* cpufreq_remove_dev - remove a CPU device * cpufreq_remove_dev - remove a CPU device
* *
* Removes the cpufreq interface for a CPU device. Is called with * Removes the cpufreq interface for a CPU device.
* cpufreq_driver_sem locked.
*/ */
static int cpufreq_remove_dev (struct device * dev) static int cpufreq_remove_dev (struct device * dev)
{ {
unsigned int cpu = to_cpu_nr(dev); unsigned int cpu = to_cpu_nr(dev);
if (cpufreq_driver->target) if (!kset_get(&cpufreq_interface.kset))
cpufreq_governor(cpu, CPUFREQ_GOV_STOP); return -EINVAL;
if (!kobject_get(&cpufreq_driver->policy[cpu].kobj)) {
kset_put(&cpufreq_interface.kset);
return -EINVAL;
}
down(&cpufreq_driver_sem);
if ((cpufreq_driver->target) &&
(cpufreq_driver->policy[cpu].policy == CPUFREQ_POLICY_GOVERNOR)) {
cpufreq_driver->policy[cpu].governor->governor(&cpufreq_driver->policy[cpu], CPUFREQ_GOV_STOP);
module_put(cpufreq_driver->policy[cpu].governor->owner);
}
/* we may call driver->exit here without checking for try_module_exit
* as it's either the driver which wants to unload or we have a CPU
* removal AND driver removal at the same time...
*/
if (cpufreq_driver->exit) if (cpufreq_driver->exit)
cpufreq_driver->exit(&cpufreq_driver->policy[cpu]); cpufreq_driver->exit(&cpufreq_driver->policy[cpu]);
kobject_unregister(&cpufreq_driver->policy[cpu].kobj); kobject_unregister(&cpufreq_driver->policy[cpu].kobj);
up(&cpufreq_driver_sem);
kobject_put(&cpufreq_driver->policy[cpu].kobj);
kset_put(&cpufreq_interface.kset);
return 0; return 0;
} }
...@@ -420,7 +452,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) ...@@ -420,7 +452,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
{ {
int ret; int ret;
down(&cpufreq_notifier_sem); down_write(&cpufreq_notifier_rwsem);
switch (list) { switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER: case CPUFREQ_TRANSITION_NOTIFIER:
ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb);
...@@ -431,7 +463,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) ...@@ -431,7 +463,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
up(&cpufreq_notifier_sem); up_write(&cpufreq_notifier_rwsem);
return ret; return ret;
} }
...@@ -452,7 +484,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) ...@@ -452,7 +484,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
{ {
int ret; int ret;
down(&cpufreq_notifier_sem); down_write(&cpufreq_notifier_rwsem);
switch (list) { switch (list) {
case CPUFREQ_TRANSITION_NOTIFIER: case CPUFREQ_TRANSITION_NOTIFIER:
ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb);
...@@ -463,7 +495,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) ...@@ -463,7 +495,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list)
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
up(&cpufreq_notifier_sem); up_write(&cpufreq_notifier_rwsem);
return ret; return ret;
} }
...@@ -474,71 +506,72 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); ...@@ -474,71 +506,72 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
* GOVERNORS * * GOVERNORS *
*********************************************************************/ *********************************************************************/
inline int cpufreq_driver_target_l(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int ret;
down(&cpufreq_driver_sem);
if (!cpufreq_driver)
ret = -EINVAL;
else
ret = cpufreq_driver->target(policy, target_freq, relation);
up(&cpufreq_driver_sem);
return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_driver_target_l);
inline int cpufreq_driver_target(struct cpufreq_policy *policy, inline int cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
{ {
return cpufreq_driver->target(policy, target_freq, relation); unsigned int ret;
unsigned int cpu = policy->cpu;
if (!cpufreq_cpu_get(cpu))
return -EINVAL;
down(&cpufreq_driver->policy[cpu].lock);
ret = cpufreq_driver->target(policy, target_freq, relation);
up(&cpufreq_driver->policy[cpu].lock);
cpufreq_cpu_put(cpu);
return ret;
} }
EXPORT_SYMBOL_GPL(cpufreq_driver_target); EXPORT_SYMBOL_GPL(cpufreq_driver_target);
static int cpufreq_governor(unsigned int cpu, unsigned int event) int cpufreq_governor(unsigned int cpu, unsigned int event)
{ {
int ret = 0; int ret = 0;
struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu]; struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu];
if (!cpufreq_cpu_get(cpu))
return -EINVAL;
switch (policy->policy) { switch (policy->policy) {
case CPUFREQ_POLICY_POWERSAVE: case CPUFREQ_POLICY_POWERSAVE:
if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) {
down(&cpufreq_driver->policy[cpu].lock);
ret = cpufreq_driver->target(policy, policy->min, CPUFREQ_RELATION_L); ret = cpufreq_driver->target(policy, policy->min, CPUFREQ_RELATION_L);
up(&cpufreq_driver->policy[cpu].lock);
}
break; break;
case CPUFREQ_POLICY_PERFORMANCE: case CPUFREQ_POLICY_PERFORMANCE:
if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) {
down(&cpufreq_driver->policy[cpu].lock);
ret = cpufreq_driver->target(policy, policy->max, CPUFREQ_RELATION_H); ret = cpufreq_driver->target(policy, policy->max, CPUFREQ_RELATION_H);
up(&cpufreq_driver->policy[cpu].lock);
}
break; break;
case CPUFREQ_POLICY_GOVERNOR: case CPUFREQ_POLICY_GOVERNOR:
ret = -EINVAL; ret = -EINVAL;
if (event == CPUFREQ_GOV_START) if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner))
if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner)) break;
break;
ret = cpufreq_driver->policy[cpu].governor->governor(policy, event); ret = cpufreq_driver->policy[cpu].governor->governor(policy, event);
if ((event == CPUFREQ_GOV_STOP) || /* we keep one module reference alive for each CPU governed by this CPU */
(ret && (event == CPUFREQ_GOV_START))) if ((event != CPUFREQ_GOV_START) || ret)
module_put(cpufreq_driver->policy[cpu].governor->owner);
if ((event == CPUFREQ_GOV_STOP) && !ret)
module_put(cpufreq_driver->policy[cpu].governor->owner); module_put(cpufreq_driver->policy[cpu].governor->owner);
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
} }
return ret;
}
cpufreq_cpu_put(cpu);
int cpufreq_governor_l(unsigned int cpu, unsigned int event)
{
int ret = 0;
down(&cpufreq_driver_sem);
ret = cpufreq_governor(cpu, event);
up(&cpufreq_driver_sem);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cpufreq_governor_l); EXPORT_SYMBOL_GPL(cpufreq_governor);
int cpufreq_register_governor(struct cpufreq_governor *governor) int cpufreq_register_governor(struct cpufreq_governor *governor)
...@@ -553,16 +586,17 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) ...@@ -553,16 +586,17 @@ int cpufreq_register_governor(struct cpufreq_governor *governor)
if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN)) if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN))
return -EBUSY; return -EBUSY;
down(&cpufreq_driver_sem); down(&cpufreq_governor_sem);
list_for_each_entry(t, &cpufreq_governor_list, governor_list) { list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
return -EBUSY; return -EBUSY;
} }
} }
list_add(&governor->governor_list, &cpufreq_governor_list); list_add(&governor->governor_list, &cpufreq_governor_list);
up(&cpufreq_driver_sem);
up(&cpufreq_governor_sem);
return 0; return 0;
} }
...@@ -576,7 +610,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) ...@@ -576,7 +610,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
if (!governor) if (!governor)
return; return;
down(&cpufreq_driver_sem); down(&cpufreq_governor_sem);
/* /*
* Unless the user uses rmmod -f, we can be safe. But we never * Unless the user uses rmmod -f, we can be safe. But we never
* know, so check whether if it's currently used. If so, * know, so check whether if it's currently used. If so,
...@@ -584,17 +619,21 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) ...@@ -584,17 +619,21 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
*/ */
for (i=0; i<NR_CPUS; i++) for (i=0; i<NR_CPUS; i++)
{ {
if (cpufreq_driver && if (!cpufreq_cpu_get(i))
(cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) && continue;
if ((cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) &&
(cpufreq_driver->policy[i].governor == governor)) { (cpufreq_driver->policy[i].governor == governor)) {
cpufreq_governor(i, CPUFREQ_GOV_STOP); cpufreq_governor(i, CPUFREQ_GOV_STOP);
cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE; cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE;
cpufreq_governor(i, CPUFREQ_GOV_START); cpufreq_governor(i, CPUFREQ_GOV_START);
cpufreq_governor(i, CPUFREQ_GOV_LIMITS);
} }
cpufreq_cpu_put(i);
} }
/* now we can safely remove it from the list */ /* now we can safely remove it from the list */
list_del(&governor->governor_list); list_del(&governor->governor_list);
up(&cpufreq_driver_sem); up(&cpufreq_governor_sem);
return; return;
} }
EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
...@@ -613,19 +652,17 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); ...@@ -613,19 +652,17 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
*/ */
int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu)
{ {
down(&cpufreq_driver_sem); if (!policy || !cpufreq_cpu_get(cpu))
if (!cpufreq_driver || !policy ||
(cpu >= NR_CPUS) || (!cpu_online(cpu))) {
up(&cpufreq_driver_sem);
return -EINVAL; return -EINVAL;
}
down(&cpufreq_driver_sem);
memcpy(policy, memcpy(policy,
&cpufreq_driver->policy[cpu], &cpufreq_driver->policy[cpu],
sizeof(struct cpufreq_policy)); sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
cpufreq_cpu_put(cpu);
return 0; return 0;
} }
EXPORT_SYMBOL(cpufreq_get_policy); EXPORT_SYMBOL(cpufreq_get_policy);
...@@ -639,27 +676,23 @@ EXPORT_SYMBOL(cpufreq_get_policy); ...@@ -639,27 +676,23 @@ EXPORT_SYMBOL(cpufreq_get_policy);
*/ */
int cpufreq_set_policy(struct cpufreq_policy *policy) int cpufreq_set_policy(struct cpufreq_policy *policy)
{ {
int ret; int ret = 0;
down(&cpufreq_driver_sem); if (!policy || !cpufreq_cpu_get(policy->cpu))
if (!cpufreq_driver || !policy ||
(policy->cpu >= NR_CPUS) || (!cpu_online(policy->cpu))) {
up(&cpufreq_driver_sem);
return -EINVAL; return -EINVAL;
}
down(&cpufreq_driver_sem);
memcpy(&policy->cpuinfo, memcpy(&policy->cpuinfo,
&cpufreq_driver->policy[policy->cpu].cpuinfo, &cpufreq_driver->policy[policy->cpu].cpuinfo,
sizeof(struct cpufreq_cpuinfo)); sizeof(struct cpufreq_cpuinfo));
up(&cpufreq_driver_sem);
/* verify the cpu speed can be set within this limit */ /* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(policy); ret = cpufreq_driver->verify(policy);
if (ret) { if (ret)
up(&cpufreq_driver_sem); goto error_out;
return ret;
}
down(&cpufreq_notifier_sem); down_read(&cpufreq_notifier_rwsem);
/* adjust if necessary - all reasons */ /* adjust if necessary - all reasons */
notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST,
...@@ -673,17 +706,18 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) ...@@ -673,17 +706,18 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
which might be different to the first one */ which might be different to the first one */
ret = cpufreq_driver->verify(policy); ret = cpufreq_driver->verify(policy);
if (ret) { if (ret) {
up(&cpufreq_notifier_sem); up_read(&cpufreq_notifier_rwsem);
up(&cpufreq_driver_sem); goto error_out;
return ret;
} }
/* notification of the new policy */ /* notification of the new policy */
notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY,
policy); policy);
up(&cpufreq_notifier_sem); up_read(&cpufreq_notifier_rwsem);
/* from here on we limit it to one limit and/or governor change running at the moment */
down(&cpufreq_driver_sem);
cpufreq_driver->policy[policy->cpu].min = policy->min; cpufreq_driver->policy[policy->cpu].min = policy->min;
cpufreq_driver->policy[policy->cpu].max = policy->max; cpufreq_driver->policy[policy->cpu].max = policy->max;
...@@ -711,9 +745,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) ...@@ -711,9 +745,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy)
cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS);
} }
} }
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
error_out:
cpufreq_cpu_put(policy->cpu);
return ret; return ret;
} }
EXPORT_SYMBOL(cpufreq_set_policy); EXPORT_SYMBOL(cpufreq_set_policy);
...@@ -759,7 +795,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) ...@@ -759,7 +795,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
*/ */
void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
{ {
down(&cpufreq_notifier_sem); down_read(&cpufreq_notifier_rwsem);
switch (state) { switch (state) {
case CPUFREQ_PRECHANGE: case CPUFREQ_PRECHANGE:
notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs); notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs);
...@@ -771,7 +807,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) ...@@ -771,7 +807,7 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
cpufreq_driver->policy[freqs->cpu].cur = freqs->new; cpufreq_driver->policy[freqs->cpu].cur = freqs->new;
break; break;
} }
up(&cpufreq_notifier_sem); up_read(&cpufreq_notifier_rwsem);
} }
EXPORT_SYMBOL_GPL(cpufreq_notify_transition); EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
...@@ -793,30 +829,33 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition); ...@@ -793,30 +829,33 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
*/ */
int cpufreq_register_driver(struct cpufreq_driver *driver_data) int cpufreq_register_driver(struct cpufreq_driver *driver_data)
{ {
int ret = 0;
if (cpufreq_driver)
return -EBUSY;
if (!driver_data || !driver_data->verify || !driver_data->init || if (!driver_data || !driver_data->verify || !driver_data->init ||
((!driver_data->setpolicy) && (!driver_data->target))) ((!driver_data->setpolicy) && (!driver_data->target)))
return -EINVAL; return -EINVAL;
down(&cpufreq_driver_sem);
if (kset_get(&cpufreq_interface.kset)) {
kset_put(&cpufreq_interface.kset);
return -EBUSY;
}
down(&cpufreq_driver_sem);
if (cpufreq_driver) {
up(&cpufreq_driver_sem);
return -EBUSY;
}
cpufreq_driver = driver_data; cpufreq_driver = driver_data;
up(&cpufreq_driver_sem);
cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!cpufreq_driver->policy) { if (!cpufreq_driver->policy) {
up(&cpufreq_driver_sem); cpufreq_driver = NULL;
return -ENOMEM; return -ENOMEM;
} }
memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem);
ret = interface_register(&cpufreq_interface); memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy));
return ret; return interface_register(&cpufreq_interface);
} }
EXPORT_SYMBOL_GPL(cpufreq_register_driver); EXPORT_SYMBOL_GPL(cpufreq_register_driver);
...@@ -831,20 +870,20 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver); ...@@ -831,20 +870,20 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver);
*/ */
int cpufreq_unregister_driver(struct cpufreq_driver *driver) int cpufreq_unregister_driver(struct cpufreq_driver *driver)
{ {
down(&cpufreq_driver_sem); if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!cpufreq_driver || if (!cpufreq_driver || (driver != cpufreq_driver)) {
(driver != cpufreq_driver)) { kset_put(&cpufreq_interface.kset);
up(&cpufreq_driver_sem);
return -EINVAL; return -EINVAL;
} }
kset_put(&cpufreq_interface.kset);
interface_unregister(&cpufreq_interface); interface_unregister(&cpufreq_interface);
if (driver) down(&cpufreq_driver_sem);
kfree(cpufreq_driver->policy); kfree(cpufreq_driver->policy);
cpufreq_driver = NULL; cpufreq_driver = NULL;
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
return 0; return 0;
...@@ -868,22 +907,28 @@ int cpufreq_restore(void) ...@@ -868,22 +907,28 @@ int cpufreq_restore(void)
if (in_interrupt()) if (in_interrupt())
panic("cpufreq_restore() called from interrupt context!"); panic("cpufreq_restore() called from interrupt context!");
if (!kset_get(&cpufreq_interface.kset))
return 0;
if (!try_module_get(cpufreq_driver->owner))
goto error_out;
for (i=0;i<NR_CPUS;i++) { for (i=0;i<NR_CPUS;i++) {
if (!cpu_online(i)) if (!cpu_online(i) || !cpufreq_cpu_get(i))
continue; continue;
down(&cpufreq_driver_sem); down(&cpufreq_driver_sem);
if (!cpufreq_driver) {
up(&cpufreq_driver_sem);
return 0;
}
memcpy(&policy, &cpufreq_driver->policy[i], sizeof(struct cpufreq_policy)); memcpy(&policy, &cpufreq_driver->policy[i], sizeof(struct cpufreq_policy));
up(&cpufreq_driver_sem); up(&cpufreq_driver_sem);
ret += cpufreq_set_policy(&policy); ret += cpufreq_set_policy(&policy);
cpufreq_cpu_put(i);
} }
module_put(cpufreq_driver->owner);
error_out:
kset_put(&cpufreq_interface.kset);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cpufreq_restore); EXPORT_SYMBOL_GPL(cpufreq_restore);
......
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