Commit 9e76988e authored by Venki Pallipadi's avatar Venki Pallipadi Committed by Dave Jones

[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: default avatarVenkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent b25e7589
...@@ -601,6 +601,31 @@ static ssize_t show_affected_cpus (struct cpufreq_policy * policy, char *buf) ...@@ -601,6 +601,31 @@ static ssize_t show_affected_cpus (struct cpufreq_policy * policy, char *buf)
return i; return i;
} }
static ssize_t store_scaling_setspeed(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
unsigned int freq = 0;
unsigned int ret;
if (!policy->governor->store_setspeed)
return -EINVAL;
ret = sscanf(buf, "%u", &freq);
if (ret != 1)
return -EINVAL;
policy->governor->store_setspeed(policy, freq);
return count;
}
static ssize_t show_scaling_setspeed(struct cpufreq_policy *policy, char *buf)
{
if (!policy->governor->show_setspeed)
return sprintf(buf, "<unsupported>\n");
return policy->governor->show_setspeed(policy, buf);
}
#define define_one_ro(_name) \ #define define_one_ro(_name) \
static struct freq_attr _name = \ static struct freq_attr _name = \
...@@ -624,6 +649,7 @@ define_one_ro(affected_cpus); ...@@ -624,6 +649,7 @@ define_one_ro(affected_cpus);
define_one_rw(scaling_min_freq); define_one_rw(scaling_min_freq);
define_one_rw(scaling_max_freq); define_one_rw(scaling_max_freq);
define_one_rw(scaling_governor); define_one_rw(scaling_governor);
define_one_rw(scaling_setspeed);
static struct attribute * default_attrs[] = { static struct attribute * default_attrs[] = {
&cpuinfo_min_freq.attr, &cpuinfo_min_freq.attr,
...@@ -634,6 +660,7 @@ static struct attribute * default_attrs[] = { ...@@ -634,6 +660,7 @@ static struct attribute * default_attrs[] = {
&scaling_governor.attr, &scaling_governor.attr,
&scaling_driver.attr, &scaling_driver.attr,
&scaling_available_governors.attr, &scaling_available_governors.attr,
&scaling_setspeed.attr,
NULL NULL
}; };
......
...@@ -65,12 +65,12 @@ static struct notifier_block userspace_cpufreq_notifier_block = { ...@@ -65,12 +65,12 @@ static struct notifier_block userspace_cpufreq_notifier_block = {
/** /**
* cpufreq_set - set the CPU frequency * cpufreq_set - set the CPU frequency
* @policy: pointer to policy struct where freq is being set
* @freq: target frequency in kHz * @freq: target frequency in kHz
* @cpu: CPU for which the frequency is to be set
* *
* Sets the CPU frequency to freq. * Sets the CPU frequency to freq.
*/ */
static int cpufreq_set(unsigned int freq, struct cpufreq_policy *policy) static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
{ {
int ret = -EINVAL; int ret = -EINVAL;
...@@ -102,34 +102,11 @@ static int cpufreq_set(unsigned int freq, struct cpufreq_policy *policy) ...@@ -102,34 +102,11 @@ static int cpufreq_set(unsigned int freq, struct cpufreq_policy *policy)
} }
/************************** sysfs interface ************************/ static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)
static ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
{ {
return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]); return sprintf(buf, "%u\n", cpu_cur_freq[policy->cpu]);
} }
static ssize_t
store_speed (struct cpufreq_policy *policy, const char *buf, size_t count)
{
unsigned int freq = 0;
unsigned int ret;
ret = sscanf (buf, "%u", &freq);
if (ret != 1)
return -EINVAL;
cpufreq_set(freq, policy);
return count;
}
static struct freq_attr freq_attr_scaling_setspeed =
{
.attr = { .name = "scaling_setspeed", .mode = 0644 },
.show = show_speed,
.store = store_speed,
};
static int cpufreq_governor_userspace(struct cpufreq_policy *policy, static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
unsigned int event) unsigned int event)
{ {
...@@ -142,10 +119,6 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ...@@ -142,10 +119,6 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
return -EINVAL; return -EINVAL;
BUG_ON(!policy->cur); BUG_ON(!policy->cur);
mutex_lock(&userspace_mutex); mutex_lock(&userspace_mutex);
rc = sysfs_create_file (&policy->kobj,
&freq_attr_scaling_setspeed.attr);
if (rc)
goto start_out;
if (cpus_using_userspace_governor == 0) { if (cpus_using_userspace_governor == 0) {
cpufreq_register_notifier( cpufreq_register_notifier(
...@@ -160,7 +133,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ...@@ -160,7 +133,7 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
cpu_cur_freq[cpu] = policy->cur; cpu_cur_freq[cpu] = policy->cur;
cpu_set_freq[cpu] = policy->cur; cpu_set_freq[cpu] = policy->cur;
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]); 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]);
start_out:
mutex_unlock(&userspace_mutex); mutex_unlock(&userspace_mutex);
break; break;
case CPUFREQ_GOV_STOP: case CPUFREQ_GOV_STOP:
...@@ -176,7 +149,6 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ...@@ -176,7 +149,6 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
cpu_min_freq[cpu] = 0; cpu_min_freq[cpu] = 0;
cpu_max_freq[cpu] = 0; cpu_max_freq[cpu] = 0;
cpu_set_freq[cpu] = 0; cpu_set_freq[cpu] = 0;
sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
dprintk("managing cpu %u stopped\n", cpu); dprintk("managing cpu %u stopped\n", cpu);
mutex_unlock(&userspace_mutex); mutex_unlock(&userspace_mutex);
break; break;
...@@ -211,6 +183,8 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy, ...@@ -211,6 +183,8 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
struct cpufreq_governor cpufreq_gov_userspace = { struct cpufreq_governor cpufreq_gov_userspace = {
.name = "userspace", .name = "userspace",
.governor = cpufreq_governor_userspace, .governor = cpufreq_governor_userspace,
.store_setspeed = cpufreq_set,
.show_setspeed = show_speed,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
EXPORT_SYMBOL(cpufreq_gov_userspace); EXPORT_SYMBOL(cpufreq_gov_userspace);
......
...@@ -167,6 +167,10 @@ struct cpufreq_governor { ...@@ -167,6 +167,10 @@ struct cpufreq_governor {
char name[CPUFREQ_NAME_LEN]; char name[CPUFREQ_NAME_LEN];
int (*governor) (struct cpufreq_policy *policy, int (*governor) (struct cpufreq_policy *policy,
unsigned int event); unsigned int event);
ssize_t (*show_setspeed) (struct cpufreq_policy *policy,
char *buf);
int (*store_setspeed) (struct cpufreq_policy *policy,
unsigned int freq);
unsigned int max_transition_latency; /* HW must be able to switch to unsigned int max_transition_latency; /* HW must be able to switch to
next freq faster than this value in nano secs or we next freq faster than this value in nano secs or we
will fallback to performance governor */ will fallback to performance governor */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment