|
@@ -1036,6 +1036,25 @@ module_out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
|
|
|
+{
|
|
|
+ int j;
|
|
|
+
|
|
|
+ policy->last_cpu = policy->cpu;
|
|
|
+ policy->cpu = cpu;
|
|
|
+
|
|
|
+ for_each_cpu(j, policy->cpus) {
|
|
|
+ if (!cpu_online(j))
|
|
|
+ continue;
|
|
|
+ per_cpu(cpufreq_policy_cpu, j) = cpu;
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef CONFIG_CPU_FREQ_TABLE
|
|
|
+ cpufreq_frequency_table_update_policy_cpu(policy);
|
|
|
+#endif
|
|
|
+ blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
|
|
|
+ CPUFREQ_UPDATE_POLICY_CPU, policy);
|
|
|
+}
|
|
|
|
|
|
/**
|
|
|
* __cpufreq_remove_dev - remove a CPU device
|
|
@@ -1046,132 +1065,92 @@ module_out:
|
|
|
*/
|
|
|
static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
|
|
|
{
|
|
|
- unsigned int cpu = dev->id;
|
|
|
+ unsigned int cpu = dev->id, ret, cpus;
|
|
|
unsigned long flags;
|
|
|
struct cpufreq_policy *data;
|
|
|
struct kobject *kobj;
|
|
|
struct completion *cmp;
|
|
|
-#ifdef CONFIG_SMP
|
|
|
struct device *cpu_dev;
|
|
|
- unsigned int j;
|
|
|
-#endif
|
|
|
|
|
|
- pr_debug("unregistering CPU %u\n", cpu);
|
|
|
+ pr_debug("%s: unregistering CPU %u\n", __func__, cpu);
|
|
|
|
|
|
spin_lock_irqsave(&cpufreq_driver_lock, flags);
|
|
|
data = per_cpu(cpufreq_cpu_data, cpu);
|
|
|
|
|
|
if (!data) {
|
|
|
+ pr_debug("%s: No cpu_data found\n", __func__);
|
|
|
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
unlock_policy_rwsem_write(cpu);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
- per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
|
|
|
|
|
-#ifdef CONFIG_SMP
|
|
|
- /* if this isn't the CPU which is the parent of the kobj, we
|
|
|
- * only need to unlink, put and exit
|
|
|
- */
|
|
|
- if (unlikely(cpu != data->cpu)) {
|
|
|
- pr_debug("removing link\n");
|
|
|
+ if (cpufreq_driver->target)
|
|
|
__cpufreq_governor(data, CPUFREQ_GOV_STOP);
|
|
|
- cpumask_clear_cpu(cpu, data->cpus);
|
|
|
- spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_START);
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
|
|
|
-
|
|
|
- kobj = &dev->kobj;
|
|
|
- cpufreq_cpu_put(data);
|
|
|
- unlock_policy_rwsem_write(cpu);
|
|
|
- sysfs_remove_link(kobj, "cpufreq");
|
|
|
- return 0;
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
-#ifdef CONFIG_SMP
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
|
strncpy(per_cpu(cpufreq_cpu_governor, cpu), data->governor->name,
|
|
|
CPUFREQ_NAME_LEN);
|
|
|
#endif
|
|
|
|
|
|
- /* if we have other CPUs still registered, we need to unlink them,
|
|
|
- * or else wait_for_completion below will lock up. Clean the
|
|
|
- * per_cpu(cpufreq_cpu_data) while holding the lock, and remove
|
|
|
- * the sysfs links afterwards.
|
|
|
- */
|
|
|
- if (unlikely(cpumask_weight(data->cpus) > 1)) {
|
|
|
- for_each_cpu(j, data->cpus) {
|
|
|
- if (j == cpu)
|
|
|
- continue;
|
|
|
- per_cpu(cpufreq_cpu_data, j) = NULL;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
+ per_cpu(cpufreq_cpu_data, cpu) = NULL;
|
|
|
+ cpus = cpumask_weight(data->cpus);
|
|
|
+ cpumask_clear_cpu(cpu, data->cpus);
|
|
|
|
|
|
- if (unlikely(cpumask_weight(data->cpus) > 1)) {
|
|
|
- for_each_cpu(j, data->cpus) {
|
|
|
- if (j == cpu)
|
|
|
- continue;
|
|
|
- pr_debug("removing link for cpu %u\n", j);
|
|
|
-#ifdef CONFIG_HOTPLUG_CPU
|
|
|
- strncpy(per_cpu(cpufreq_cpu_governor, j),
|
|
|
- data->governor->name, CPUFREQ_NAME_LEN);
|
|
|
-#endif
|
|
|
- cpu_dev = get_cpu_device(j);
|
|
|
- kobj = &cpu_dev->kobj;
|
|
|
+ if (unlikely((cpu == data->cpu) && (cpus > 1))) {
|
|
|
+ /* first sibling now owns the new sysfs dir */
|
|
|
+ cpu_dev = get_cpu_device(cpumask_first(data->cpus));
|
|
|
+ sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
|
|
|
+ ret = kobject_move(&data->kobj, &cpu_dev->kobj);
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: Failed to move kobj: %d", __func__, ret);
|
|
|
+ cpumask_set_cpu(cpu, data->cpus);
|
|
|
+ ret = sysfs_create_link(&cpu_dev->kobj, &data->kobj,
|
|
|
+ "cpufreq");
|
|
|
+ spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
unlock_policy_rwsem_write(cpu);
|
|
|
- sysfs_remove_link(kobj, "cpufreq");
|
|
|
- lock_policy_rwsem_write(cpu);
|
|
|
- cpufreq_cpu_put(data);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
+
|
|
|
+ update_policy_cpu(data, cpu_dev->id);
|
|
|
+ pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
|
|
|
+ __func__, cpu_dev->id, cpu);
|
|
|
}
|
|
|
-#else
|
|
|
- spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
-#endif
|
|
|
|
|
|
- if (cpufreq_driver->target)
|
|
|
- __cpufreq_governor(data, CPUFREQ_GOV_STOP);
|
|
|
+ spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
|
|
|
|
|
- kobj = &data->kobj;
|
|
|
- cmp = &data->kobj_unregister;
|
|
|
+ pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
|
|
|
+ cpufreq_cpu_put(data);
|
|
|
unlock_policy_rwsem_write(cpu);
|
|
|
- kobject_put(kobj);
|
|
|
+ sysfs_remove_link(&dev->kobj, "cpufreq");
|
|
|
|
|
|
- /* 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");
|
|
|
-
|
|
|
- lock_policy_rwsem_write(cpu);
|
|
|
- if (cpufreq_driver->exit)
|
|
|
- cpufreq_driver->exit(data);
|
|
|
- unlock_policy_rwsem_write(cpu);
|
|
|
+ /* If cpu is last user of policy, free policy */
|
|
|
+ if (cpus == 1) {
|
|
|
+ lock_policy_rwsem_write(cpu);
|
|
|
+ kobj = &data->kobj;
|
|
|
+ cmp = &data->kobj_unregister;
|
|
|
+ unlock_policy_rwsem_write(cpu);
|
|
|
+ kobject_put(kobj);
|
|
|
|
|
|
-#ifdef CONFIG_HOTPLUG_CPU
|
|
|
- /* when the CPU which is the parent of the kobj is hotplugged
|
|
|
- * offline, check for siblings, and create cpufreq sysfs interface
|
|
|
- * and symlinks
|
|
|
- */
|
|
|
- if (unlikely(cpumask_weight(data->cpus) > 1)) {
|
|
|
- /* first sibling now owns the new sysfs dir */
|
|
|
- cpumask_clear_cpu(cpu, data->cpus);
|
|
|
- cpufreq_add_dev(get_cpu_device(cpumask_first(data->cpus)), NULL);
|
|
|
+ /* 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");
|
|
|
|
|
|
- /* finally remove our own symlink */
|
|
|
lock_policy_rwsem_write(cpu);
|
|
|
- __cpufreq_remove_dev(dev, sif);
|
|
|
- }
|
|
|
-#endif
|
|
|
+ if (cpufreq_driver->exit)
|
|
|
+ cpufreq_driver->exit(data);
|
|
|
+ unlock_policy_rwsem_write(cpu);
|
|
|
|
|
|
- free_cpumask_var(data->related_cpus);
|
|
|
- free_cpumask_var(data->cpus);
|
|
|
- kfree(data);
|
|
|
+ free_cpumask_var(data->related_cpus);
|
|
|
+ free_cpumask_var(data->cpus);
|
|
|
+ kfree(data);
|
|
|
+ } else if (cpufreq_driver->target) {
|
|
|
+ __cpufreq_governor(data, CPUFREQ_GOV_START);
|
|
|
+ __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|