Fwd: [PATCH] cpufreq (4/7): updated cpufreq ref-counting and locking scheme

Russell King (rmk@arm.linux.org.uk)
Sun, 9 Mar 2003 19:36:36 +0000


----- 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(&current_policy[cpu], freq,
+ ret = cpufreq_driver_target(&current_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/