Messages in this thread Patch in this message | | | Date | Sun, 9 Mar 2003 19:36:36 +0000 | From | Russell King <> | Subject | Fwd: [PATCH] cpufreq (4/7): updated cpufreq ref-counting and locking scheme |
| |
----- Forwarded message from Dominik Brodowski <linux@brodo.de> -----
From: Dominik Brodowski <linux@brodo.de> To: torvalds@transmeta.com Cc: cpufreq@www.linux.org.uk Subject: [PATCH] cpufreq (4/7): updated cpufreq ref-counting and locking scheme Date: Fri, 7 Mar 2003 11:09:10 +0100
Hi Linus,
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.
Please apply, Dominik
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 -ruN linux-original/arch/i386/kernel/cpu/cpufreq/acpi.c linux/arch/i386/kernel/cpu/cpufreq/acpi.c --- linux-original/arch/i386/kernel/cpu/cpufreq/acpi.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/acpi.c 2003-03-06 21:19:15.000000000 +0100 @@ -619,6 +619,7 @@ .init = acpi_cpufreq_cpu_init, .exit = acpi_cpufreq_cpu_exit, .name = "acpi-cpufreq", + .owner = THIS_MODULE, }; diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/elanfreq.c linux/arch/i386/kernel/cpu/cpufreq/elanfreq.c --- linux-original/arch/i386/kernel/cpu/cpufreq/elanfreq.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/elanfreq.c 2003-03-06 21:19:15.000000000 +0100 @@ -250,6 +250,7 @@ .target = elanfreq_target, .init = elanfreq_cpu_init, .name = "elanfreq", + .owner = THIS_MODULE, }; diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c linux/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c --- linux-original/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/gx-suspmod.c 2003-03-06 21:19:15.000000000 +0100 @@ -451,6 +451,7 @@ .target = cpufreq_gx_target, .init = cpufreq_gx_cpu_init, .name = "gx-suspmod", + .owner = THIS_MODULE, }; static int __init cpufreq_gx_init(void) diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/longhaul.c linux/arch/i386/kernel/cpu/cpufreq/longhaul.c --- linux-original/arch/i386/kernel/cpu/cpufreq/longhaul.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/longhaul.c 2003-03-06 21:19:15.000000000 +0100 @@ -649,6 +649,7 @@ .target = longhaul_target, .init = longhaul_cpu_init, .name = "longhaul", + .owner = THIS_MODULE, }; static int __init longhaul_init (void) diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/longrun.c linux/arch/i386/kernel/cpu/cpufreq/longrun.c --- linux-original/arch/i386/kernel/cpu/cpufreq/longrun.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/longrun.c 2003-03-06 21:19:15.000000000 +0100 @@ -253,6 +253,7 @@ .setpolicy = longrun_set_policy, .init = longrun_cpu_init, .name = "longrun", + .owner = THIS_MODULE, }; diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c linux/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c --- linux-original/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c 2003-03-06 21:19:15.000000000 +0100 @@ -236,6 +236,7 @@ .init = cpufreq_p4_cpu_init, .exit = cpufreq_p4_cpu_exit, .name = "p4-clockmod", + .owner = THIS_MODULE, }; diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k6.c linux/arch/i386/kernel/cpu/cpufreq/powernow-k6.c --- linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k6.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/powernow-k6.c 2003-03-06 21:19:15.000000000 +0100 @@ -190,6 +190,7 @@ .init = powernow_k6_cpu_init, .exit = powernow_k6_cpu_exit, .name = "powernow-k6", + .owner = THIS_MODULE, }; diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k7.c linux/arch/i386/kernel/cpu/cpufreq/powernow-k7.c --- linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k7.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/powernow-k7.c 2003-03-06 21:19:15.000000000 +0100 @@ -377,6 +377,7 @@ .target = powernow_target, .init = powernow_cpu_init, .name = "powernow-k7", + .owner = THIS_MODULE, }; static int __init powernow_init (void) diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep.c linux/arch/i386/kernel/cpu/cpufreq/speedstep.c --- linux-original/arch/i386/kernel/cpu/cpufreq/speedstep.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/i386/kernel/cpu/cpufreq/speedstep.c 2003-03-06 21:19:15.000000000 +0100 @@ -658,6 +658,7 @@ .verify = speedstep_verify, .target = speedstep_target, .init = speedstep_cpu_init, + .owner = THIS_MODULE, }; diff -ruN linux-original/arch/sparc64/kernel/us3_cpufreq.c linux/arch/sparc64/kernel/us3_cpufreq.c --- linux-original/arch/sparc64/kernel/us3_cpufreq.c 2003-03-06 19:13:31.000000000 +0100 +++ linux/arch/sparc64/kernel/us3_cpufreq.c 2003-03-06 21:19:16.000000000 +0100 @@ -276,6 +276,7 @@ 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 -ruN linux-original/drivers/cpufreq/userspace.c linux/drivers/cpufreq/userspace.c --- linux-original/drivers/cpufreq/userspace.c 2003-03-06 21:18:02.000000000 +0100 +++ linux/drivers/cpufreq/userspace.c 2003-03-06 21:19:16.000000000 +0100 @@ -113,7 +113,7 @@ 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 -ruN linux-original/include/linux/cpufreq.h linux/include/linux/cpufreq.h --- linux-original/include/linux/cpufreq.h 2003-03-06 21:19:04.000000000 +0100 +++ linux/include/linux/cpufreq.h 2003-03-06 21:19:16.000000000 +0100 @@ -70,6 +70,8 @@ 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 @@ }; /* 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 @@ 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 -ruN linux-original/kernel/cpufreq.c linux/kernel/cpufreq.c --- linux-original/kernel/cpufreq.c 2003-03-06 21:19:04.000000000 +0100 +++ linux/kernel/cpufreq.c 2003-03-06 21:23:33.000000000 +0100 @@ -43,12 +43,40 @@ */ 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 @@ 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 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 @@ \ 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 @@ */ 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 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ /** * 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 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 @@ default: ret = -EINVAL; } - up(&cpufreq_notifier_sem); + up_write(&cpufreq_notifier_rwsem); return ret; } @@ -452,7 +484,7 @@ { 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 @@ default: ret = -EINVAL; } - up(&cpufreq_notifier_sem); + up_write(&cpufreq_notifier_rwsem); return ret; } @@ -474,71 +506,72 @@ * 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 @@ 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 @@ 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 @@ */ for (i=0; i<NR_CPUS; i++) { - if (cpufreq_driver && - (cpufreq_driver->policy[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 @@ */ 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 @@ */ 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 @@ 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 @@ 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 @@ */ 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 @@ 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 @@ */ 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 @@ */ 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 @@ 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;i<NR_CPUS;i++) { - if (!cpu_online(i)) + if (!cpu_online(i) || !cpufreq_cpu_get(i)) continue; down(&cpufreq_driver_sem); - if (!cpufreq_driver) { - up(&cpufreq_driver_sem); - return 0; - } - memcpy(&policy, &cpufreq_driver->policy[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); _______________________________________________ Cpufreq mailing list Cpufreq@www.linux.org.uk http://www.linux.org.uk/mailman/listinfo/cpufreq ----- End forwarded message ----- -- Russell King (rmk@arm.linux.org.uk) The developer of ARM Linux http://www.arm.linux.org.uk/personal/aboutme.html
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |