Skip to content

Commit

Permalink
cpufreq: Fix creation of symbolic links to policy directories
Browse files Browse the repository at this point in the history
[ Upstream commit 2f0ba79 ]

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 <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
rafaeljw authored and gregkh committed Dec 25, 2017
1 parent e0d1315 commit e9a1ba2
Showing 1 changed file with 21 additions and 17 deletions.
38 changes: 21 additions & 17 deletions drivers/cpufreq/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -1184,10 +1192,10 @@ static int cpufreq_online(unsigned int cpu)
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;
Expand Down Expand Up @@ -1284,13 +1292,15 @@ static int cpufreq_online(unsigned int cpu)

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.
Expand All @@ -1312,16 +1322,10 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)

/* 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)
Expand Down

0 comments on commit e9a1ba2

Please sign in to comment.