cpufreq: Introduce a flag ('frozen') to separate full vs temporary init/teardown

During suspend/resume we would like to do a light-weight init/teardown of
CPUs in the cpufreq subsystem and preserve certain things such as sysfs files
etc across suspend/resume transitions. Add a flag called 'frozen' to help
distinguish the full init/teardown sequence from the light-weight one.

Signed-off-by: Srivatsa S. Bhat <srivatsa.bhat@linux.vnet.ibm.com>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Srivatsa S. Bhat 2013-07-30 04:24:49 +05:30 committed by Rafael J. Wysocki
parent f9ba680d23
commit a82fab2928
1 changed files with 49 additions and 27 deletions

View File

@ -903,7 +903,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy)
#ifdef CONFIG_HOTPLUG_CPU
static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling,
struct device *dev)
struct device *dev, bool frozen)
{
struct cpufreq_policy *policy;
int ret = 0, has_target = !!cpufreq_driver->target;
@ -931,13 +931,18 @@ static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling,
__cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
}
ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
if (ret) {
/* Don't touch sysfs links during light-weight init */
if (frozen) {
/* Drop the extra refcount that we took above */
cpufreq_cpu_put(policy);
return ret;
return 0;
}
return 0;
ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
if (ret)
cpufreq_cpu_put(policy);
return ret;
}
#endif
@ -972,16 +977,8 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy)
kfree(policy);
}
/**
* cpufreq_add_dev - add a CPU device
*
* Adds the cpufreq interface for a CPU device.
*
* The Oracle says: try running cpufreq registration/unregistration concurrently
* with with cpu hotplugging and all hell will break loose. Tried to clean this
* mess up, but more thorough testing is needed. - Mathieu
*/
static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
bool frozen)
{
unsigned int j, cpu = dev->id;
int ret = -ENOMEM;
@ -1013,7 +1010,8 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling);
if (cp && cpumask_test_cpu(cpu, cp->related_cpus)) {
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
return cpufreq_add_policy_cpu(cpu, sibling, dev);
return cpufreq_add_policy_cpu(cpu, sibling, dev,
frozen);
}
}
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
@ -1079,9 +1077,11 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
}
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
ret = cpufreq_add_dev_interface(cpu, policy, dev);
if (ret)
goto err_out_unregister;
if (!frozen) {
ret = cpufreq_add_dev_interface(cpu, policy, dev);
if (ret)
goto err_out_unregister;
}
cpufreq_init_policy(policy);
@ -1112,6 +1112,20 @@ module_out:
return ret;
}
/**
* cpufreq_add_dev - add a CPU device
*
* Adds the cpufreq interface for a CPU device.
*
* The Oracle says: try running cpufreq registration/unregistration concurrently
* with with cpu hotplugging and all hell will break loose. Tried to clean this
* mess up, but more thorough testing is needed. - Mathieu
*/
static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
return __cpufreq_add_dev(dev, sif, false);
}
static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
{
int j;
@ -1130,7 +1144,7 @@ static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
}
static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *data,
unsigned int old_cpu)
unsigned int old_cpu, bool frozen)
{
struct device *cpu_dev;
unsigned long flags;
@ -1138,6 +1152,11 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *data,
/* first sibling now owns the new sysfs dir */
cpu_dev = get_cpu_device(cpumask_first(data->cpus));
/* Don't touch sysfs files during light-weight tear-down */
if (frozen)
return cpu_dev->id;
sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
ret = kobject_move(&data->kobj, &cpu_dev->kobj);
if (ret) {
@ -1169,7 +1188,7 @@ static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *data,
* This routine frees the rwsem before returning.
*/
static int __cpufreq_remove_dev(struct device *dev,
struct subsys_interface *sif)
struct subsys_interface *sif, bool frozen)
{
unsigned int cpu = dev->id, cpus;
int new_cpu;
@ -1208,17 +1227,20 @@ static int __cpufreq_remove_dev(struct device *dev,
cpumask_clear_cpu(cpu, data->cpus);
unlock_policy_rwsem_write(cpu);
if (cpu != data->cpu) {
if (cpu != data->cpu && !frozen) {
sysfs_remove_link(&dev->kobj, "cpufreq");
} else if (cpus > 1) {
new_cpu = cpufreq_nominate_new_policy_cpu(data, cpu);
new_cpu = cpufreq_nominate_new_policy_cpu(data, cpu, frozen);
if (new_cpu >= 0) {
WARN_ON(lock_policy_rwsem_write(cpu));
update_policy_cpu(data, new_cpu);
unlock_policy_rwsem_write(cpu);
pr_debug("%s: policy Kobject moved to cpu: %d "
"from: %d\n",__func__, new_cpu, cpu);
if (!frozen) {
pr_debug("%s: policy Kobject moved to cpu: %d "
"from: %d\n",__func__, new_cpu, cpu);
}
}
}
@ -1266,7 +1288,7 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
if (cpu_is_offline(cpu))
return 0;
retval = __cpufreq_remove_dev(dev, sif);
retval = __cpufreq_remove_dev(dev, sif, false);
return retval;
}
@ -1992,7 +2014,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
break;
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
__cpufreq_remove_dev(dev, NULL);
__cpufreq_remove_dev(dev, NULL, false);
break;
case CPU_DOWN_FAILED:
case CPU_DOWN_FAILED_FROZEN: