aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq/cpufreq_userspace.c
diff options
context:
space:
mode:
authorVenki Pallipadi <venkatesh.pallipadi@intel.com>2007-10-26 13:18:21 -0400
committerDave Jones <davej@redhat.com>2008-02-06 22:57:58 -0500
commit9e76988e9390a4ff4d171f690586d0c58186b47e (patch)
treee033aa977a66b4ea2dc01b0e846eb7f6f8134857 /drivers/cpufreq/cpufreq_userspace.c
parentb25e75899e449456409cfa1a3b042257c03d4355 (diff)
[CPUFREQ] Eliminate cpufreq_userspace scaling_setspeed deadlock
Eliminate cpufreq_userspace scaling_setspeed deadlock. Luming Yu recently uncovered yet another cpufreq related deadlock. One thread that continuously switches the governors and the other thread that repeatedly cats the contents of cpufreq directory causes both these threads to go into a deadlock. Detailed examination of the deadlock showed the exact flow before the deadlock as: Thread 1 Thread 2 ________ ________ cats files under /sys/devices/.../cpufreq/ Set governor to userspace Adds a new sysfs entry for scaling_setspeed cats files under /sys/devices/.../cpufreq/ Set governor to performance Holds cpufreq_rw_sem in write mode Sends a STOP notify to userspace governor cat /sys/devices/.../cpufreq/scaling_setspeed Gets a handle on the above sysfs entry with sysfs_get_active Blocks while trying to get cpufreq_rw_sem in read mode Remove a sysfs entry for scaling_setspeed Blocks on sysfs_deactivate while waiting for earlier get_active (on other thread) to drain At this point both threads go into deadlock and any other thread that tries to do anything with sysfs cpufreq will also block. There seems to be no easy way to avoid this deadlock as long as cpufreq_userspace adds/removes the sysfs entry under same kobject as cpufreq. Below patch moves scaling_setspeed to cpufreq.c, keeping it always and calling back the governor on read/write. This is the cleanest fix I could think of, even though adding two callbacks in governor structure just for this seems unnecessary. Note that the change makes scaling_setspeed under /sys/.../cpufreq permanent and returns <unsupported> when governor is not userspace. Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Signed-off-by: Dave Jones <davej@redhat.com>
Diffstat (limited to 'drivers/cpufreq/cpufreq_userspace.c')
-rw-r--r--drivers/cpufreq/cpufreq_userspace.c40
1 files changed, 7 insertions, 33 deletions
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index f8cdde4bf6cd..cb2ac01a41a1 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -65,12 +65,12 @@ static struct notifier_block userspace_cpufreq_notifier_block = {
65 65
66/** 66/**
67 * cpufreq_set - set the CPU frequency 67 * cpufreq_set - set the CPU frequency
68 * @policy: pointer to policy struct where freq is being set
68 * @freq: target frequency in kHz 69 * @freq: target frequency in kHz
69 * @cpu: CPU for which the frequency is to be set
70 * 70 *
71 * Sets the CPU frequency to freq. 71 * Sets the CPU frequency to freq.
72 */ 72 */
73static int cpufreq_set(unsigned int freq, struct cpufreq_policy *policy) 73static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
74{ 74{
75 int ret = -EINVAL; 75 int ret = -EINVAL;
76 76
@@ -102,34 +102,11 @@ static int cpufreq_set(unsigned int freq, struct cpufreq_policy *policy)
102} 102}
103 103
104 104
105/************************** sysfs interface ************************/ 105static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)
106static ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
107{ 106{
108 return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]); 107 return sprintf(buf, "%u\n", cpu_cur_freq[policy->cpu]);
109} 108}
110 109
111static ssize_t
112store_speed (struct cpufreq_policy *policy, const char *buf, size_t count)
113{
114 unsigned int freq = 0;
115 unsigned int ret;
116
117 ret = sscanf (buf, "%u", &freq);
118 if (ret != 1)
119 return -EINVAL;
120
121 cpufreq_set(freq, policy);
122
123 return count;
124}
125
126static struct freq_attr freq_attr_scaling_setspeed =
127{
128 .attr = { .name = "scaling_setspeed", .mode = 0644 },
129 .show = show_speed,
130 .store = store_speed,
131};
132
133static int cpufreq_governor_userspace(struct cpufreq_policy *policy, 110static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
134 unsigned int event) 111 unsigned int event)
135{ 112{
@@ -142,10 +119,6 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
142 return -EINVAL; 119 return -EINVAL;
143 BUG_ON(!policy->cur); 120 BUG_ON(!policy->cur);
144 mutex_lock(&userspace_mutex); 121 mutex_lock(&userspace_mutex);
145 rc = sysfs_create_file (&policy->kobj,
146 &freq_attr_scaling_setspeed.attr);
147 if (rc)
148 goto start_out;
149 122
150 if (cpus_using_userspace_governor == 0) { 123 if (cpus_using_userspace_governor == 0) {
151 cpufreq_register_notifier( 124 cpufreq_register_notifier(
@@ -160,7 +133,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
160 cpu_cur_freq[cpu] = policy->cur; 133 cpu_cur_freq[cpu] = policy->cur;
161 cpu_set_freq[cpu] = policy->cur; 134 cpu_set_freq[cpu] = policy->cur;
162 dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]); 135 dprintk("managing cpu %u started (%u - %u kHz, currently %u kHz)\n", cpu, cpu_min_freq[cpu], cpu_max_freq[cpu], cpu_cur_freq[cpu]);
163start_out: 136
164 mutex_unlock(&userspace_mutex); 137 mutex_unlock(&userspace_mutex);
165 break; 138 break;
166 case CPUFREQ_GOV_STOP: 139 case CPUFREQ_GOV_STOP:
@@ -176,7 +149,6 @@ start_out:
176 cpu_min_freq[cpu] = 0; 149 cpu_min_freq[cpu] = 0;
177 cpu_max_freq[cpu] = 0; 150 cpu_max_freq[cpu] = 0;
178 cpu_set_freq[cpu] = 0; 151 cpu_set_freq[cpu] = 0;
179 sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
180 dprintk("managing cpu %u stopped\n", cpu); 152 dprintk("managing cpu %u stopped\n", cpu);
181 mutex_unlock(&userspace_mutex); 153 mutex_unlock(&userspace_mutex);
182 break; 154 break;
@@ -211,6 +183,8 @@ start_out:
211struct cpufreq_governor cpufreq_gov_userspace = { 183struct cpufreq_governor cpufreq_gov_userspace = {
212 .name = "userspace", 184 .name = "userspace",
213 .governor = cpufreq_governor_userspace, 185 .governor = cpufreq_governor_userspace,
186 .store_setspeed = cpufreq_set,
187 .show_setspeed = show_speed,
214 .owner = THIS_MODULE, 188 .owner = THIS_MODULE,
215}; 189};
216EXPORT_SYMBOL(cpufreq_gov_userspace); 190EXPORT_SYMBOL(cpufreq_gov_userspace);