Commit 42f921a6 authored by Viresh Kumar's avatar Viresh Kumar Committed by Rafael J. Wysocki

cpufreq: remove sysfs files for CPUs which failed to come back after resume

There are cases where cpufreq_add_dev() may fail for some CPUs
during system resume. With the current code we will still have
sysfs cpufreq files for those CPUs and struct cpufreq_policy
would be already freed for them. Hence any operation on those
sysfs files would result in kernel warnings.

Example of problems resulting from resume errors (from Bjørn Mork):

WARNING: CPU: 0 PID: 6055 at fs/sysfs/file.c:343 sysfs_open_file+0x77/0x212()
missing sysfs attribute operations for kobject: (null)
Modules linked in: [stripped as irrelevant]
CPU: 0 PID: 6055 Comm: grep Tainted: G      D      3.13.0-rc2 #153
Hardware name: LENOVO 2776LEG/2776LEG, BIOS 6EET55WW (3.15 ) 12/19/2011
 0000000000000009 ffff8802327ebb78 ffffffff81380b0e 0000000000000006
 ffff8802327ebbc8 ffff8802327ebbb8 ffffffff81038635 0000000000000000
 ffffffff811823c7 ffff88021a19e688 ffff88021a19e688 ffff8802302f9310
Call Trace:
 [<ffffffff81380b0e>] dump_stack+0x55/0x76
 [<ffffffff81038635>] warn_slowpath_common+0x7c/0x96
 [<ffffffff811823c7>] ? sysfs_open_file+0x77/0x212
 [<ffffffff810386e3>] warn_slowpath_fmt+0x41/0x43
 [<ffffffff81182dec>] ? sysfs_get_active+0x6b/0x82
 [<ffffffff81182382>] ? sysfs_open_file+0x32/0x212
 [<ffffffff811823c7>] sysfs_open_file+0x77/0x212
 [<ffffffff81182350>] ? sysfs_schedule_callback+0x1ac/0x1ac
 [<ffffffff81122562>] do_dentry_open+0x17c/0x257
 [<ffffffff8112267e>] finish_open+0x41/0x4f
 [<ffffffff81130225>] do_last+0x80c/0x9ba
 [<ffffffff8112dbbd>] ? inode_permission+0x40/0x42
 [<ffffffff81130606>] path_openat+0x233/0x4a1
 [<ffffffff81130b7e>] do_filp_open+0x35/0x85
 [<ffffffff8113b787>] ? __alloc_fd+0x172/0x184
 [<ffffffff811232ea>] do_sys_open+0x6b/0xfa
 [<ffffffff811233a7>] SyS_openat+0xf/0x11
 [<ffffffff8138c812>] system_call_fastpath+0x16/0x1b

To fix this, remove those sysfs files or put the associated kobject
in case of such errors. Also, to make it simple, remove the cpufreq
sysfs links from all the CPUs (except for the policy->cpu) during
suspend, as that operation won't result in a loss of sysfs file
permissions and we can create those links during resume just fine.

Fixes: 5302c3fb ("cpufreq: Perform light-weight init/teardown during suspend/resume")
Reported-and-tested-by: default avatarBjørn Mork <bjorn@mork.no>
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
Cc: 3.12+ <stable@vger.kernel.org> # 3.12+
[rjw: Changelog]
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 319e2e3f
...@@ -845,8 +845,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy) ...@@ -845,8 +845,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy)
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
unsigned int cpu, struct device *dev, unsigned int cpu, struct device *dev)
bool frozen)
{ {
int ret = 0; int ret = 0;
unsigned long flags; unsigned long flags;
...@@ -877,11 +876,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, ...@@ -877,11 +876,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
} }
} }
/* Don't touch sysfs links during light-weight init */ return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
if (!frozen)
ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
return ret;
} }
#endif #endif
...@@ -926,6 +921,27 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void) ...@@ -926,6 +921,27 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void)
return NULL; return NULL;
} }
static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy)
{
struct kobject *kobj;
struct completion *cmp;
down_read(&policy->rwsem);
kobj = &policy->kobj;
cmp = &policy->kobj_unregister;
up_read(&policy->rwsem);
kobject_put(kobj);
/*
* We need to make sure that the underlying kobj is
* actually not referenced anymore by anybody before we
* proceed with unloading.
*/
pr_debug("waiting for dropping of refcount\n");
wait_for_completion(cmp);
pr_debug("wait complete\n");
}
static void cpufreq_policy_free(struct cpufreq_policy *policy) static void cpufreq_policy_free(struct cpufreq_policy *policy)
{ {
free_cpumask_var(policy->related_cpus); free_cpumask_var(policy->related_cpus);
...@@ -986,7 +1002,7 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, ...@@ -986,7 +1002,7 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) { list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) {
if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) { if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) {
read_unlock_irqrestore(&cpufreq_driver_lock, flags); read_unlock_irqrestore(&cpufreq_driver_lock, flags);
ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev, frozen); ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev);
up_read(&cpufreq_rwsem); up_read(&cpufreq_rwsem);
return ret; return ret;
} }
...@@ -1096,7 +1112,10 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, ...@@ -1096,7 +1112,10 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
if (cpufreq_driver->exit) if (cpufreq_driver->exit)
cpufreq_driver->exit(policy); cpufreq_driver->exit(policy);
err_set_policy_cpu: err_set_policy_cpu:
if (frozen)
cpufreq_policy_put_kobj(policy);
cpufreq_policy_free(policy); cpufreq_policy_free(policy);
nomem_out: nomem_out:
up_read(&cpufreq_rwsem); up_read(&cpufreq_rwsem);
...@@ -1118,7 +1137,7 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) ...@@ -1118,7 +1137,7 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
} }
static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy,
unsigned int old_cpu, bool frozen) unsigned int old_cpu)
{ {
struct device *cpu_dev; struct device *cpu_dev;
int ret; int ret;
...@@ -1126,10 +1145,6 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy, ...@@ -1126,10 +1145,6 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy,
/* first sibling now owns the new sysfs dir */ /* first sibling now owns the new sysfs dir */
cpu_dev = get_cpu_device(cpumask_any_but(policy->cpus, old_cpu)); cpu_dev = get_cpu_device(cpumask_any_but(policy->cpus, old_cpu));
/* Don't touch sysfs files during light-weight tear-down */
if (frozen)
return cpu_dev->id;
sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
ret = kobject_move(&policy->kobj, &cpu_dev->kobj); ret = kobject_move(&policy->kobj, &cpu_dev->kobj);
if (ret) { if (ret) {
...@@ -1196,7 +1211,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, ...@@ -1196,7 +1211,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev,
if (!frozen) if (!frozen)
sysfs_remove_link(&dev->kobj, "cpufreq"); sysfs_remove_link(&dev->kobj, "cpufreq");
} else if (cpus > 1) { } else if (cpus > 1) {
new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu, frozen); new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu);
if (new_cpu >= 0) { if (new_cpu >= 0) {
update_policy_cpu(policy, new_cpu); update_policy_cpu(policy, new_cpu);
...@@ -1218,8 +1233,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev, ...@@ -1218,8 +1233,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev,
int ret; int ret;
unsigned long flags; unsigned long flags;
struct cpufreq_policy *policy; struct cpufreq_policy *policy;
struct kobject *kobj;
struct completion *cmp;
read_lock_irqsave(&cpufreq_driver_lock, flags); read_lock_irqsave(&cpufreq_driver_lock, flags);
policy = per_cpu(cpufreq_cpu_data, cpu); policy = per_cpu(cpufreq_cpu_data, cpu);
...@@ -1249,22 +1262,8 @@ static int __cpufreq_remove_dev_finish(struct device *dev, ...@@ -1249,22 +1262,8 @@ static int __cpufreq_remove_dev_finish(struct device *dev,
} }
} }
if (!frozen) { if (!frozen)
down_read(&policy->rwsem); cpufreq_policy_put_kobj(policy);
kobj = &policy->kobj;
cmp = &policy->kobj_unregister;
up_read(&policy->rwsem);
kobject_put(kobj);
/*
* We need to make sure that the underlying kobj is
* actually not referenced anymore by anybody before we
* proceed with unloading.
*/
pr_debug("waiting for dropping of refcount\n");
wait_for_completion(cmp);
pr_debug("wait complete\n");
}
/* /*
* Perform the ->exit() even during light-weight tear-down, * Perform the ->exit() even during light-weight tear-down,
......
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