Skip to content

Commit a914443

Browse files
bebarinorjwysocki
authored andcommitted
cpufreq: Fix sysfs deadlock with concurrent hotplug/frequency switch
Running one program that continuously hotplugs and replugs a cpu concurrently with another program that continuously writes to the scaling_setspeed node eventually deadlocks with: ============================================= [ INFO: possible recursive locking detected ] 3.4.0 #37 Tainted: G W --------------------------------------------- filemonkey/122 is trying to acquire lock: (s_active#13){++++.+}, at: [<c01a3d28>] sysfs_remove_dir+0x9c/0xb4 but task is already holding lock: (s_active#13){++++.+}, at: [<c01a22f0>] sysfs_write_file+0xe8/0x140 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(s_active#13); lock(s_active#13); *** DEADLOCK *** May be due to missing lock nesting notation 2 locks held by filemonkey/122: #0: (&buffer->mutex){+.+.+.}, at: [<c01a2230>] sysfs_write_file+0x28/0x140 #1: (s_active#13){++++.+}, at: [<c01a22f0>] sysfs_write_file+0xe8/0x140 stack backtrace: [<c0014fcc>] (unwind_backtrace+0x0/0x120) from [<c00ca600>] (validate_chain+0x6f8/0x1054) [<c00ca600>] (validate_chain+0x6f8/0x1054) from [<c00cb778>] (__lock_acquire+0x81c/0x8d8) [<c00cb778>] (__lock_acquire+0x81c/0x8d8) from [<c00cb9c0>] (lock_acquire+0x18c/0x1e8) [<c00cb9c0>] (lock_acquire+0x18c/0x1e8) from [<c01a3ba8>] (sysfs_addrm_finish+0xd0/0x180) [<c01a3ba8>] (sysfs_addrm_finish+0xd0/0x180) from [<c01a3d28>] (sysfs_remove_dir+0x9c/0xb4) [<c01a3d28>] (sysfs_remove_dir+0x9c/0xb4) from [<c02d0e5c>] (kobject_del+0x10/0x38) [<c02d0e5c>] (kobject_del+0x10/0x38) from [<c02d0f74>] (kobject_release+0xf0/0x194) [<c02d0f74>] (kobject_release+0xf0/0x194) from [<c0565a98>] (cpufreq_cpu_put+0xc/0x24) [<c0565a98>] (cpufreq_cpu_put+0xc/0x24) from [<c05683f0>] (store+0x6c/0x74) [<c05683f0>] (store+0x6c/0x74) from [<c01a2314>] (sysfs_write_file+0x10c/0x140) [<c01a2314>] (sysfs_write_file+0x10c/0x140) from [<c014af44>] (vfs_write+0xb0/0x128) [<c014af44>] (vfs_write+0xb0/0x128) from [<c014b06c>] (sys_write+0x3c/0x68) [<c014b06c>] (sys_write+0x3c/0x68) from [<c000e0e0>] (ret_fast_syscall+0x0/0x3c) This is because store() in cpufreq.c indirectly calls kobject_get() via cpufreq_cpu_get() and is the last one to call kobject_put() via cpufreq_cpu_put(). Sysfs code should not call kobject_get() or kobject_put() directly (see the comment around sysfs_schedule_callback() for more information). Fix this deadlock by introducing two new functions: struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu) void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data) which do the same thing as cpufreq_cpu_{get,put}() but don't call kobject functions. To easily trigger this deadlock you can insert an msleep() with a reasonably large value right after the fail label at the bottom of the store() function in cpufreq.c and then write scaling_setspeed in one task and offline the cpu in another. The first task will hang and be detected by the hung task detector. Signed-off-by: Stephen Boyd <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 53df1ad commit a914443

File tree

1 file changed

+27
-8
lines changed

1 file changed

+27
-8
lines changed

drivers/cpufreq/cpufreq.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ void disable_cpufreq(void)
138138
static LIST_HEAD(cpufreq_governor_list);
139139
static DEFINE_MUTEX(cpufreq_governor_mutex);
140140

141-
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
141+
static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)
142142
{
143143
struct cpufreq_policy *data;
144144
unsigned long flags;
@@ -162,7 +162,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
162162
if (!data)
163163
goto err_out_put_module;
164164

165-
if (!kobject_get(&data->kobj))
165+
if (!sysfs && !kobject_get(&data->kobj))
166166
goto err_out_put_module;
167167

168168
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
@@ -175,16 +175,35 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
175175
err_out:
176176
return NULL;
177177
}
178+
179+
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
180+
{
181+
return __cpufreq_cpu_get(cpu, false);
182+
}
178183
EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
179184

185+
static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu)
186+
{
187+
return __cpufreq_cpu_get(cpu, true);
188+
}
180189

181-
void cpufreq_cpu_put(struct cpufreq_policy *data)
190+
static void __cpufreq_cpu_put(struct cpufreq_policy *data, bool sysfs)
182191
{
183-
kobject_put(&data->kobj);
192+
if (!sysfs)
193+
kobject_put(&data->kobj);
184194
module_put(cpufreq_driver->owner);
185195
}
196+
197+
void cpufreq_cpu_put(struct cpufreq_policy *data)
198+
{
199+
__cpufreq_cpu_put(data, false);
200+
}
186201
EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
187202

203+
static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data)
204+
{
205+
__cpufreq_cpu_put(data, true);
206+
}
188207

189208
/*********************************************************************
190209
* EXTERNALLY AFFECTING FREQUENCY CHANGES *
@@ -617,7 +636,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
617636
struct cpufreq_policy *policy = to_policy(kobj);
618637
struct freq_attr *fattr = to_attr(attr);
619638
ssize_t ret = -EINVAL;
620-
policy = cpufreq_cpu_get(policy->cpu);
639+
policy = cpufreq_cpu_get_sysfs(policy->cpu);
621640
if (!policy)
622641
goto no_policy;
623642

@@ -631,7 +650,7 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
631650

632651
unlock_policy_rwsem_read(policy->cpu);
633652
fail:
634-
cpufreq_cpu_put(policy);
653+
cpufreq_cpu_put_sysfs(policy);
635654
no_policy:
636655
return ret;
637656
}
@@ -642,7 +661,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
642661
struct cpufreq_policy *policy = to_policy(kobj);
643662
struct freq_attr *fattr = to_attr(attr);
644663
ssize_t ret = -EINVAL;
645-
policy = cpufreq_cpu_get(policy->cpu);
664+
policy = cpufreq_cpu_get_sysfs(policy->cpu);
646665
if (!policy)
647666
goto no_policy;
648667

@@ -656,7 +675,7 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
656675

657676
unlock_policy_rwsem_write(policy->cpu);
658677
fail:
659-
cpufreq_cpu_put(policy);
678+
cpufreq_cpu_put_sysfs(policy);
660679
no_policy:
661680
return ret;
662681
}

0 commit comments

Comments
 (0)