From 68b7d00fe42a6039af404a11e3788cc264bde48d Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Sat, 8 Mar 2003 06:37:39 -0100 Subject: [PATCH] [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. --- arch/i386/kernel/cpu/cpufreq/acpi.c | 1 + arch/i386/kernel/cpu/cpufreq/elanfreq.c | 1 + arch/i386/kernel/cpu/cpufreq/gx-suspmod.c | 1 + arch/i386/kernel/cpu/cpufreq/longhaul.c | 1 + arch/i386/kernel/cpu/cpufreq/longrun.c | 1 + arch/i386/kernel/cpu/cpufreq/p4-clockmod.c | 1 + arch/i386/kernel/cpu/cpufreq/powernow-k6.c | 1 + arch/i386/kernel/cpu/cpufreq/powernow-k7.c | 1 + arch/i386/kernel/cpu/cpufreq/speedstep.c | 1 + arch/sparc64/kernel/us3_cpufreq.c | 1 + drivers/cpufreq/userspace.c | 2 +- include/linux/cpufreq.h | 10 +- kernel/cpufreq.c | 357 ++++++++++++--------- 13 files changed, 216 insertions(+), 163 deletions(-) diff --git a/arch/i386/kernel/cpu/cpufreq/acpi.c b/arch/i386/kernel/cpu/cpufreq/acpi.c index 9ea1686ac29c4..d042abfba6c8f 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi.c @@ -619,6 +619,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, .name = "acpi-cpufreq", + .owner = THIS_MODULE, }; diff --git a/arch/i386/kernel/cpu/cpufreq/elanfreq.c b/arch/i386/kernel/cpu/cpufreq/elanfreq.c index c88a077aea080..9214b2e0c3602 100644 --- a/arch/i386/kernel/cpu/cpufreq/elanfreq.c +++ b/arch/i386/kernel/cpu/cpufreq/elanfreq.c @@ -250,6 +250,7 @@ static struct cpufreq_driver elanfreq_driver = { .target = elanfreq_target, .init = elanfreq_cpu_init, .name = "elanfreq", + .owner = THIS_MODULE, }; diff --git a/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c b/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c index 006a2dd80bc87..42b6aeff2028e 100644 --- a/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c +++ b/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c @@ -451,6 +451,7 @@ static struct cpufreq_driver gx_suspmod_driver = { .target = cpufreq_gx_target, .init = cpufreq_gx_cpu_init, .name = "gx-suspmod", + .owner = THIS_MODULE, }; static int __init cpufreq_gx_init(void) diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c index ae97400fb3851..bdce330026df9 100644 --- a/arch/i386/kernel/cpu/cpufreq/longhaul.c +++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c @@ -649,6 +649,7 @@ static struct cpufreq_driver longhaul_driver = { .target = longhaul_target, .init = longhaul_cpu_init, .name = "longhaul", + .owner = THIS_MODULE, }; static int __init longhaul_init (void) diff --git a/arch/i386/kernel/cpu/cpufreq/longrun.c b/arch/i386/kernel/cpu/cpufreq/longrun.c index 460dcda4594e3..2ca833db88a71 100644 --- a/arch/i386/kernel/cpu/cpufreq/longrun.c +++ b/arch/i386/kernel/cpu/cpufreq/longrun.c @@ -253,6 +253,7 @@ static struct cpufreq_driver longrun_driver = { .setpolicy = longrun_set_policy, .init = longrun_cpu_init, .name = "longrun", + .owner = THIS_MODULE, }; diff --git a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c index 592ff14576006..cc2074feec300 100644 --- a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c +++ b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c @@ -236,6 +236,7 @@ static struct cpufreq_driver p4clockmod_driver = { .init = cpufreq_p4_cpu_init, .exit = cpufreq_p4_cpu_exit, .name = "p4-clockmod", + .owner = THIS_MODULE, }; diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c index 0d0701078a564..d78a99d4555bb 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k6.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k6.c @@ -190,6 +190,7 @@ static struct cpufreq_driver powernow_k6_driver = { .init = powernow_k6_cpu_init, .exit = powernow_k6_cpu_exit, .name = "powernow-k6", + .owner = THIS_MODULE, }; diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c index 469ef8bf8d328..664ff84f5903e 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c @@ -377,6 +377,7 @@ static struct cpufreq_driver powernow_driver = { .target = powernow_target, .init = powernow_cpu_init, .name = "powernow-k7", + .owner = THIS_MODULE, }; static int __init powernow_init (void) diff --git a/arch/i386/kernel/cpu/cpufreq/speedstep.c b/arch/i386/kernel/cpu/cpufreq/speedstep.c index db82f7c8edb09..e1a0ea5aa7051 100644 --- a/arch/i386/kernel/cpu/cpufreq/speedstep.c +++ b/arch/i386/kernel/cpu/cpufreq/speedstep.c @@ -658,6 +658,7 @@ static struct cpufreq_driver speedstep_driver = { .verify = speedstep_verify, .target = speedstep_target, .init = speedstep_cpu_init, + .owner = THIS_MODULE, }; diff --git a/arch/sparc64/kernel/us3_cpufreq.c b/arch/sparc64/kernel/us3_cpufreq.c index fd1c359b32520..26df592995a8b 100644 --- a/arch/sparc64/kernel/us3_cpufreq.c +++ b/arch/sparc64/kernel/us3_cpufreq.c @@ -276,6 +276,7 @@ static int __init us3freq_init(void) driver->target = us3freq_target; driver->init = us3freq_cpu_init; driver->exit = us3freq_cpu_exit; + driver->owner = THIS_MODULE, strcpy(driver->name, "UltraSPARC-III"); cpufreq_us3_driver = driver; diff --git a/drivers/cpufreq/userspace.c b/drivers/cpufreq/userspace.c index 0e82c23414e6b..0cd13925340c7 100644 --- a/drivers/cpufreq/userspace.c +++ b/drivers/cpufreq/userspace.c @@ -113,7 +113,7 @@ int cpufreq_set(unsigned int freq, unsigned int cpu) if (freq > cpu_max_freq[cpu]) freq = cpu_max_freq[cpu]; - ret = cpufreq_driver_target_l(¤t_policy[cpu], freq, + ret = cpufreq_driver_target(¤t_policy[cpu], freq, CPUFREQ_RELATION_L); err: diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 3ac1a6a604e3c..17391410ef651 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -70,6 +70,8 @@ struct cpufreq_policy { struct cpufreq_cpuinfo cpuinfo; /* see above */ struct device * dev; struct kobject kobj; + struct semaphore lock; /* CPU ->setpolicy or ->target may + only be called once a time */ }; #define CPUFREQ_ADJUST (0) @@ -132,18 +134,13 @@ struct cpufreq_governor { }; /* pass a target to the cpufreq driver - * _l : (cpufreq_driver_sem is not held) */ inline int cpufreq_driver_target(struct cpufreq_policy *policy, unsigned int target_freq, 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 */ -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); void cpufreq_unregister_governor(struct cpufreq_governor *governor); @@ -165,6 +162,7 @@ struct cpufreq_driver { int (*target) (struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation); + struct module *owner; /* optional, for the moment */ int (*init) (struct cpufreq_policy *policy); int (*exit) (struct cpufreq_policy *policy); diff --git a/kernel/cpufreq.c b/kernel/cpufreq.c index 4dc7256989fc3..c3509f2813990 100644 --- a/kernel/cpufreq.c +++ b/kernel/cpufreq.c @@ -43,12 +43,40 @@ static DECLARE_MUTEX (cpufreq_driver_sem); */ static struct notifier_block *cpufreq_policy_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); +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 * @@ -67,19 +95,19 @@ int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpu return 0; } else { struct cpufreq_governor *t; - down(&cpufreq_driver_sem); + down(&cpufreq_governor_sem); if (!cpufreq_driver || !cpufreq_driver->target) goto out; list_for_each_entry(t, &cpufreq_governor_list, governor_list) { if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { *governor = t; *policy = CPUFREQ_POLICY_GOVERNOR; - up(&cpufreq_driver_sem); + up(&cpufreq_governor_sem); return 0; } } out: - up(&cpufreq_driver_sem); + up(&cpufreq_governor_sem); } return -EINVAL; } @@ -120,14 +148,7 @@ static inline int to_cpu_nr (struct device *dev) static ssize_t show_##file_name \ (struct cpufreq_policy * policy, char *buf) \ { \ - unsigned int value = 0; \ - \ - down(&cpufreq_driver_sem); \ - if (cpufreq_driver) \ - value = policy->object; \ - up(&cpufreq_driver_sem); \ - \ - return sprintf (buf, "%u\n", value); \ + return sprintf (buf, "%u\n", policy->object); \ } show_one(cpuinfo_min_freq, cpuinfo.min_freq); @@ -147,7 +168,7 @@ static ssize_t store_##file_name \ \ ret = cpufreq_get_policy(&new_policy, policy->cpu); \ if (ret) \ - return ret; \ + return -EINVAL; \ \ ret = sscanf (buf, "%u", &new_policy.object); \ if (ret != 1) \ @@ -166,26 +187,16 @@ store_one(scaling_max_freq,max); */ static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf) { - unsigned int value = 0; - 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) { + switch (policy->policy) { case CPUFREQ_POLICY_POWERSAVE: return sprintf(buf, "powersave\n"); case CPUFREQ_POLICY_PERFORMANCE: return sprintf(buf, "performance\n"); 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, */ static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf) { - char value[CPUFREQ_NAME_LEN]; - - 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); + return snprintf(buf, CPUFREQ_NAME_LEN, "%s\n", cpufreq_driver->name); } /** @@ -240,8 +244,7 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy * policy, i += sprintf(buf, "performance powersave"); - down(&cpufreq_driver_sem); - if (!cpufreq_driver || !cpufreq_driver->target) + if (!cpufreq_driver->target) goto out; 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, i += snprintf(&buf[i], CPUFREQ_NAME_LEN, " %s", t->name); } out: - up(&cpufreq_driver_sem); i += sprintf(&buf[i], "\n"); return i; } @@ -288,7 +290,6 @@ static struct attribute * default_attrs[] = { NULL }; - #define to_policy(k) container_of(k,struct cpufreq_policy,kobj) #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) { struct cpufreq_policy * policy = to_policy(kobj); 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, @@ -304,7 +310,12 @@ static ssize_t store(struct kobject * kobj, struct attribute * attr, { struct cpufreq_policy * policy = to_policy(kobj); 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 = { @@ -330,9 +341,11 @@ static int cpufreq_add_dev (struct device * dev) struct cpufreq_policy new_policy; struct cpufreq_policy *policy; - down(&cpufreq_driver_sem); - if (!cpufreq_driver) { - up(&cpufreq_driver_sem); + if (!kset_get(&cpufreq_interface.kset)) + return -EINVAL; + + if (!try_module_get(cpufreq_driver->owner)) { + kset_put(&cpufreq_interface.kset); return -EINVAL; } @@ -343,26 +356,18 @@ static int cpufreq_add_dev (struct device * dev) policy->cpu = cpu; if (cpufreq_driver->init) { ret = cpufreq_driver->init(policy); - if (ret) { - up(&cpufreq_driver_sem); - return -ENODEV; - } + if (ret) + goto out; } /* set default policy on this CPU */ + down(&cpufreq_driver_sem); memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); - - if (cpufreq_driver->target) - cpufreq_governor(cpu, CPUFREQ_GOV_START); - 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 */ policy->kobj.parent = &dev->kobj; policy->kobj.ktype = &ktype_cpufreq; @@ -371,8 +376,17 @@ static int cpufreq_add_dev (struct device * dev) cpufreq_interface.name, KOBJ_NAME_LEN); 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; } @@ -380,21 +394,39 @@ static int cpufreq_add_dev (struct device * dev) /** * cpufreq_remove_dev - remove a CPU device * - * Removes the cpufreq interface for a CPU device. Is called with - * cpufreq_driver_sem locked. + * Removes the cpufreq interface for a CPU device. */ static int cpufreq_remove_dev (struct device * dev) { unsigned int cpu = to_cpu_nr(dev); - if (cpufreq_driver->target) - cpufreq_governor(cpu, CPUFREQ_GOV_STOP); + if (!kset_get(&cpufreq_interface.kset)) + 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) cpufreq_driver->exit(&cpufreq_driver->policy[cpu]); 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; } @@ -420,7 +452,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) { int ret; - down(&cpufreq_notifier_sem); + down_write(&cpufreq_notifier_rwsem); switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: ret = notifier_chain_register(&cpufreq_transition_notifier_list, nb); @@ -431,7 +463,7 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list) default: ret = -EINVAL; } - up(&cpufreq_notifier_sem); + up_write(&cpufreq_notifier_rwsem); return ret; } @@ -452,7 +484,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) { int ret; - down(&cpufreq_notifier_sem); + down_write(&cpufreq_notifier_rwsem); switch (list) { case CPUFREQ_TRANSITION_NOTIFIER: ret = notifier_chain_unregister(&cpufreq_transition_notifier_list, nb); @@ -463,7 +495,7 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) default: ret = -EINVAL; } - up(&cpufreq_notifier_sem); + up_write(&cpufreq_notifier_rwsem); return ret; } @@ -474,71 +506,72 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); * 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, unsigned int target_freq, 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); -static int cpufreq_governor(unsigned int cpu, unsigned int event) +int cpufreq_governor(unsigned int cpu, unsigned int event) { int ret = 0; struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu]; + if (!cpufreq_cpu_get(cpu)) + return -EINVAL; + switch (policy->policy) { 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); + up(&cpufreq_driver->policy[cpu].lock); + } break; 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); + up(&cpufreq_driver->policy[cpu].lock); + } break; case CPUFREQ_POLICY_GOVERNOR: ret = -EINVAL; - if (event == CPUFREQ_GOV_START) - if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner)) - break; + if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner)) + break; ret = cpufreq_driver->policy[cpu].governor->governor(policy, event); - if ((event == CPUFREQ_GOV_STOP) || - (ret && (event == CPUFREQ_GOV_START))) + /* we keep one module reference alive for each CPU governed by this CPU */ + 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); break; default: 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; } -EXPORT_SYMBOL_GPL(cpufreq_governor_l); +EXPORT_SYMBOL_GPL(cpufreq_governor); 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)) return -EBUSY; - down(&cpufreq_driver_sem); + down(&cpufreq_governor_sem); list_for_each_entry(t, &cpufreq_governor_list, governor_list) { if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { - up(&cpufreq_driver_sem); + up(&cpufreq_governor_sem); return -EBUSY; } } list_add(&governor->governor_list, &cpufreq_governor_list); - up(&cpufreq_driver_sem); + + up(&cpufreq_governor_sem); return 0; } @@ -576,7 +610,8 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) if (!governor) return; - down(&cpufreq_driver_sem); + down(&cpufreq_governor_sem); + /* * Unless the user uses rmmod -f, we can be safe. But we never * know, so check whether if it's currently used. If so, @@ -584,17 +619,21 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) */ for (i=0; ipolicy[i].policy == CPUFREQ_POLICY_GOVERNOR) && + if (!cpufreq_cpu_get(i)) + continue; + if ((cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) && (cpufreq_driver->policy[i].governor == governor)) { cpufreq_governor(i, CPUFREQ_GOV_STOP); cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE; 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 */ list_del(&governor->governor_list); - up(&cpufreq_driver_sem); + up(&cpufreq_governor_sem); return; } 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) { - down(&cpufreq_driver_sem); - if (!cpufreq_driver || !policy || - (cpu >= NR_CPUS) || (!cpu_online(cpu))) { - up(&cpufreq_driver_sem); + if (!policy || !cpufreq_cpu_get(cpu)) return -EINVAL; - } + down(&cpufreq_driver_sem); memcpy(policy, &cpufreq_driver->policy[cpu], sizeof(struct cpufreq_policy)); - up(&cpufreq_driver_sem); + cpufreq_cpu_put(cpu); + return 0; } EXPORT_SYMBOL(cpufreq_get_policy); @@ -639,27 +676,23 @@ EXPORT_SYMBOL(cpufreq_get_policy); */ int cpufreq_set_policy(struct cpufreq_policy *policy) { - int ret; + int ret = 0; - down(&cpufreq_driver_sem); - if (!cpufreq_driver || !policy || - (policy->cpu >= NR_CPUS) || (!cpu_online(policy->cpu))) { - up(&cpufreq_driver_sem); + if (!policy || !cpufreq_cpu_get(policy->cpu)) return -EINVAL; - } + down(&cpufreq_driver_sem); memcpy(&policy->cpuinfo, &cpufreq_driver->policy[policy->cpu].cpuinfo, sizeof(struct cpufreq_cpuinfo)); + up(&cpufreq_driver_sem); /* verify the cpu speed can be set within this limit */ ret = cpufreq_driver->verify(policy); - if (ret) { - up(&cpufreq_driver_sem); - return ret; - } + if (ret) + goto error_out; - down(&cpufreq_notifier_sem); + down_read(&cpufreq_notifier_rwsem); /* adjust if necessary - all reasons */ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_ADJUST, @@ -673,17 +706,18 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) which might be different to the first one */ ret = cpufreq_driver->verify(policy); if (ret) { - up(&cpufreq_notifier_sem); - up(&cpufreq_driver_sem); - return ret; + up_read(&cpufreq_notifier_rwsem); + goto error_out; } /* notification of the new policy */ notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_NOTIFY, 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].max = policy->max; @@ -711,9 +745,11 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); } } - up(&cpufreq_driver_sem); + error_out: + cpufreq_cpu_put(policy->cpu); + return ret; } EXPORT_SYMBOL(cpufreq_set_policy); @@ -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) { - down(&cpufreq_notifier_sem); + down_read(&cpufreq_notifier_rwsem); switch (state) { case CPUFREQ_PRECHANGE: 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) cpufreq_driver->policy[freqs->cpu].cur = freqs->new; break; } - up(&cpufreq_notifier_sem); + up_read(&cpufreq_notifier_rwsem); } 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 ret = 0; - - if (cpufreq_driver) - return -EBUSY; - if (!driver_data || !driver_data->verify || !driver_data->init || ((!driver_data->setpolicy) && (!driver_data->target))) 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; + up(&cpufreq_driver_sem); cpufreq_driver->policy = kmalloc(NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); if (!cpufreq_driver->policy) { - up(&cpufreq_driver_sem); + cpufreq_driver = NULL; 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); @@ -831,20 +870,20 @@ EXPORT_SYMBOL_GPL(cpufreq_register_driver); */ int cpufreq_unregister_driver(struct cpufreq_driver *driver) { - down(&cpufreq_driver_sem); + if (!kset_get(&cpufreq_interface.kset)) + return 0; - if (!cpufreq_driver || - (driver != cpufreq_driver)) { - up(&cpufreq_driver_sem); + if (!cpufreq_driver || (driver != cpufreq_driver)) { + kset_put(&cpufreq_interface.kset); return -EINVAL; } + kset_put(&cpufreq_interface.kset); interface_unregister(&cpufreq_interface); - if (driver) - kfree(cpufreq_driver->policy); + down(&cpufreq_driver_sem); + kfree(cpufreq_driver->policy); cpufreq_driver = NULL; - up(&cpufreq_driver_sem); return 0; @@ -868,22 +907,28 @@ int cpufreq_restore(void) if (in_interrupt()) 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;ipolicy[i], sizeof(struct cpufreq_policy)); up(&cpufreq_driver_sem); - ret += cpufreq_set_policy(&policy); + + cpufreq_cpu_put(i); } + module_put(cpufreq_driver->owner); + error_out: + kset_put(&cpufreq_interface.kset); + return ret; } EXPORT_SYMBOL_GPL(cpufreq_restore); -- 2.53.0