public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* Fwd: [PATCH] cpufreq (4/7): updated cpufreq ref-counting and locking scheme
@ 2003-03-09 19:36 Russell King
  0 siblings, 0 replies; only message in thread
From: Russell King @ 2003-03-09 19:36 UTC (permalink / raw)
  To: Linux Kernel List

----- 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


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2003-03-09 19:29 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-09 19:36 Fwd: [PATCH] cpufreq (4/7): updated cpufreq ref-counting and locking scheme Russell King

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox