| From foo@baz Thu Dec 21 09:02:40 CET 2017 |
| From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> |
| Date: Mon, 27 Mar 2017 19:33:09 +0200 |
| Subject: cpufreq: Fix creation of symbolic links to policy directories |
| |
| From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com> |
| |
| |
| [ Upstream commit 2f0ba790df51721794c11abc7a076d407392f648 ] |
| |
| The cpufreq core only tries to create symbolic links from CPU |
| directories in sysfs to policy directories in cpufreq_add_dev(), |
| either when a given CPU is registered or when the cpufreq driver |
| is registered, whichever happens first. That is not sufficient, |
| however, because cpufreq_add_dev() may be called for an offline CPU |
| whose policy object has not been created yet and, quite obviously, |
| the symbolic cannot be added in that case. |
| |
| Fix that by making cpufreq_online() attempt to add symbolic links to |
| policy objects for the CPUs in the related_cpus mask of every new |
| policy object created by it. |
| |
| The cpufreq_driver_lock locking around the for_each_cpu() loop |
| in cpufreq_online() is dropped, because it is not necessary and the |
| code is somewhat simpler without it. Moreover, failures to create |
| a symbolic link will not be regarded as hard errors any more and |
| the CPUs without those links will not be taken offline automatically, |
| but that should not be problematic in practice. |
| |
| Reported-and-tested-by: Prashanth Prakash <pprakash@codeaurora.org> |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Sasha Levin <alexander.levin@verizon.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/cpufreq/cpufreq.c | 38 +++++++++++++++++++++----------------- |
| 1 file changed, 21 insertions(+), 17 deletions(-) |
| |
| --- a/drivers/cpufreq/cpufreq.c |
| +++ b/drivers/cpufreq/cpufreq.c |
| @@ -918,11 +918,19 @@ static struct kobj_type ktype_cpufreq = |
| .release = cpufreq_sysfs_release, |
| }; |
| |
| -static int add_cpu_dev_symlink(struct cpufreq_policy *policy, |
| - struct device *dev) |
| +static void add_cpu_dev_symlink(struct cpufreq_policy *policy, unsigned int cpu) |
| { |
| + struct device *dev = get_cpu_device(cpu); |
| + |
| + if (!dev) |
| + return; |
| + |
| + if (cpumask_test_and_set_cpu(cpu, policy->real_cpus)) |
| + return; |
| + |
| dev_dbg(dev, "%s: Adding symlink\n", __func__); |
| - return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq"); |
| + if (sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq")) |
| + dev_err(dev, "cpufreq symlink creation failed\n"); |
| } |
| |
| static void remove_cpu_dev_symlink(struct cpufreq_policy *policy, |
| @@ -1184,10 +1192,10 @@ static int cpufreq_online(unsigned int c |
| policy->user_policy.min = policy->min; |
| policy->user_policy.max = policy->max; |
| |
| - write_lock_irqsave(&cpufreq_driver_lock, flags); |
| - for_each_cpu(j, policy->related_cpus) |
| + for_each_cpu(j, policy->related_cpus) { |
| per_cpu(cpufreq_cpu_data, j) = policy; |
| - write_unlock_irqrestore(&cpufreq_driver_lock, flags); |
| + add_cpu_dev_symlink(policy, j); |
| + } |
| } else { |
| policy->min = policy->user_policy.min; |
| policy->max = policy->user_policy.max; |
| @@ -1284,13 +1292,15 @@ out_exit_policy: |
| |
| if (cpufreq_driver->exit) |
| cpufreq_driver->exit(policy); |
| + |
| + for_each_cpu(j, policy->real_cpus) |
| + remove_cpu_dev_symlink(policy, get_cpu_device(j)); |
| + |
| out_free_policy: |
| cpufreq_policy_free(policy, !new_policy); |
| return ret; |
| } |
| |
| -static int cpufreq_offline(unsigned int cpu); |
| - |
| /** |
| * cpufreq_add_dev - the cpufreq interface for a CPU device. |
| * @dev: CPU device. |
| @@ -1312,16 +1322,10 @@ static int cpufreq_add_dev(struct device |
| |
| /* Create sysfs link on CPU registration */ |
| policy = per_cpu(cpufreq_cpu_data, cpu); |
| - if (!policy || cpumask_test_and_set_cpu(cpu, policy->real_cpus)) |
| - return 0; |
| + if (policy) |
| + add_cpu_dev_symlink(policy, cpu); |
| |
| - ret = add_cpu_dev_symlink(policy, dev); |
| - if (ret) { |
| - cpumask_clear_cpu(cpu, policy->real_cpus); |
| - cpufreq_offline(cpu); |
| - } |
| - |
| - return ret; |
| + return 0; |
| } |
| |
| static int cpufreq_offline(unsigned int cpu) |