Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 486

kernel-2.6.18-238.el5.src.rpm

From: Doug Chapman <dchapman@redhat.com>
Date: Thu, 6 Dec 2007 16:15:23 -0500
Subject: [cpufreq] governor: use new rwsem locking in work cb
Message-id: 1196975723.1083.7.camel@athlon
O-Subject: [RHEL 5.2 PATCH 3/5] hang at 'Disabling ondemand cpu frequency scaling'
Bugzilla: 253416

Patch 3/5 for BZ 253416

Backport of:

commit 56463b78cdca8e9ff8cc1759bca0c0777a061d6b
Author: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Date:   Mon Feb 5 16:12:45 2007 -0800

    [CPUFREQ] ondemand governor use new cpufreq rwsem locking in work callback

    Eliminate flush_workqueue in cpufreq_governor(STOP) callpath. Using flush
    there has a deadlock potential as in

    http://uwsg.iu.edu/hypermail/linux/kernel/0611.3/1223.html

    Also, cleanup the locking issues with do_dbs_timer delayed_work callback.
    As it changes the CPU frequency using __cpufreq_target, it needs to have
    policy_rwsem in write mode, which also protects it from hot plug.

diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 7ce7ca9..9218115 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -307,18 +307,25 @@ static void do_dbs_timer(struct cpu_dbs_info_s *dbs_info)
 {
 	unsigned int cpu = dbs_info->cpu;
 
-	if (!dbs_info->enable)
+	if (lock_policy_rwsem_write(cpu) < 0)
 		return;
 
+	if (!dbs_info->enable){
+		unlock_policy_rwsem_write(cpu);
+		return;
+	}
+
 	lock_cpu_hotplug();
 	dbs_check_cpu(dbs_info);
 	unlock_cpu_hotplug();
 	queue_delayed_work_on(cpu, kondemand_wq, &dbs_info->work,
 			usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
+	unlock_policy_rwsem_write(cpu);
 }
 
 static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
 {
+	dbs_info->enable = 1;
 	INIT_WORK(&dbs_info->work, do_dbs_timer, dbs_info);
 	queue_delayed_work_on(dbs_info->cpu, kondemand_wq, &dbs_info->work,
 			usecs_to_jiffies(dbs_tuners_ins.sampling_rate));
@@ -329,7 +336,6 @@ static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
 {
 	dbs_info->enable = 0;
 	cancel_delayed_work(&dbs_info->work);
-	flush_workqueue(kondemand_wq);
 }
 
 static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
@@ -357,15 +363,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
 
 		mutex_lock(&dbs_mutex);
 		dbs_enable++;
-		if (dbs_enable == 1) {
-			kondemand_wq = create_workqueue("kondemand");
-			if (!kondemand_wq) {
-				printk(KERN_ERR "Creation of kondemand failed\n");
-				dbs_enable--;
-				mutex_unlock(&dbs_mutex);
-				return -ENOSPC;
-			}
-		}
+
 		for_each_cpu_mask(j, policy->cpus) {
 			struct cpu_dbs_info_s *j_dbs_info;
 			j_dbs_info = &per_cpu(cpu_dbs_info, j);
@@ -375,7 +373,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
 			j_dbs_info->prev_cpu_wall = get_jiffies_64();
 		}
 		this_dbs_info->cpu = cpu;
-		this_dbs_info->enable = 1;
 		sysfs_create_group(&policy->kobj, &dbs_attr_group);
 		/*
 		 * Start the timerschedule work, when this governor
@@ -406,9 +403,6 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
 		dbs_timer_exit(this_dbs_info);
 		sysfs_remove_group(&policy->kobj, &dbs_attr_group);
 		dbs_enable--;
-		if (dbs_enable == 0)
-			destroy_workqueue(kondemand_wq);
-
 		mutex_unlock(&dbs_mutex);
 
 		break;
@@ -437,12 +431,18 @@ static struct cpufreq_governor cpufreq_gov_dbs = {
 
 static int __init cpufreq_gov_dbs_init(void)
 {
+	kondemand_wq = create_workqueue("kondemand");
+	if (!kondemand_wq) {
+		printk(KERN_ERR "Creation of kondemand failed\n");
+		return -EFAULT;
+	}
 	return cpufreq_register_governor(&cpufreq_gov_dbs);
 }
 
 static void __exit cpufreq_gov_dbs_exit(void)
 {
 	cpufreq_unregister_governor(&cpufreq_gov_dbs);
+	destroy_workqueue(kondemand_wq);
 }