aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq/cpufreq.c
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2013-01-14 08:23:03 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-02-01 18:01:14 -0500
commitb8eed8af94f9203e0cc39245ea335f4b8dc1ed31 (patch)
tree39c9e57baa6daf91295c331c6b995c9d6286b2bc /drivers/cpufreq/cpufreq.c
parentf85178048c083520bd920921744dd2c4a797fbc5 (diff)
cpufreq: Simplify __cpufreq_remove_dev()
__cpufreq_remove_dev() is called on multiple occasions: cpufreq_driver unregister and cpu removals. Current implementation of this routine is overly complex without much need. If the cpu to be removed is the policy->cpu, we remove the policy first and add all other cpus again from policy->cpus and then finally call __cpufreq_remove_dev() again to remove the cpu to be deleted. Haahhhh.. There exist a simple solution to removal of a cpu: - Simply use the old policy structure - update its fields like: policy->cpu, etc. - notify any users of cpufreq, which depend on changing policy->cpu Hence this patch, which tries to implement the above theory. It is tested well by myself on ARM big.LITTLE TC2 SoC, which has 5 cores (2 A15 and 3 A7). Both A15's share same struct policy and all A7's share same policy structure. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Tested-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r--drivers/cpufreq/cpufreq.c161
1 files changed, 70 insertions, 91 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 034d1836884b..9af14a8bbcdb 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1036,6 +1036,25 @@ module_out:
1036 return ret; 1036 return ret;
1037} 1037}
1038 1038
1039static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
1040{
1041 int j;
1042
1043 policy->last_cpu = policy->cpu;
1044 policy->cpu = cpu;
1045
1046 for_each_cpu(j, policy->cpus) {
1047 if (!cpu_online(j))
1048 continue;
1049 per_cpu(cpufreq_policy_cpu, j) = cpu;
1050 }
1051
1052#ifdef CONFIG_CPU_FREQ_TABLE
1053 cpufreq_frequency_table_update_policy_cpu(policy);
1054#endif
1055 blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
1056 CPUFREQ_UPDATE_POLICY_CPU, policy);
1057}
1039 1058
1040/** 1059/**
1041 * __cpufreq_remove_dev - remove a CPU device 1060 * __cpufreq_remove_dev - remove a CPU device
@@ -1046,132 +1065,92 @@ module_out:
1046 */ 1065 */
1047static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) 1066static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
1048{ 1067{
1049 unsigned int cpu = dev->id; 1068 unsigned int cpu = dev->id, ret, cpus;
1050 unsigned long flags; 1069 unsigned long flags;
1051 struct cpufreq_policy *data; 1070 struct cpufreq_policy *data;
1052 struct kobject *kobj; 1071 struct kobject *kobj;
1053 struct completion *cmp; 1072 struct completion *cmp;
1054#ifdef CONFIG_SMP
1055 struct device *cpu_dev; 1073 struct device *cpu_dev;
1056 unsigned int j;
1057#endif
1058 1074
1059 pr_debug("unregistering CPU %u\n", cpu); 1075 pr_debug("%s: unregistering CPU %u\n", __func__, cpu);
1060 1076
1061 spin_lock_irqsave(&cpufreq_driver_lock, flags); 1077 spin_lock_irqsave(&cpufreq_driver_lock, flags);
1062 data = per_cpu(cpufreq_cpu_data, cpu); 1078 data = per_cpu(cpufreq_cpu_data, cpu);
1063 1079
1064 if (!data) { 1080 if (!data) {
1081 pr_debug("%s: No cpu_data found\n", __func__);
1065 spin_unlock_irqrestore(&cpufreq_driver_lock, flags); 1082 spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
1066 unlock_policy_rwsem_write(cpu); 1083 unlock_policy_rwsem_write(cpu);
1067 return -EINVAL; 1084 return -EINVAL;
1068 } 1085 }
1069 per_cpu(cpufreq_cpu_data, cpu) = NULL;
1070 1086
1071#ifdef CONFIG_SMP 1087 if (cpufreq_driver->target)
1072 /* if this isn't the CPU which is the parent of the kobj, we
1073 * only need to unlink, put and exit
1074 */
1075 if (unlikely(cpu != data->cpu)) {
1076 pr_debug("removing link\n");
1077 __cpufreq_governor(data, CPUFREQ_GOV_STOP); 1088 __cpufreq_governor(data, CPUFREQ_GOV_STOP);
1078 cpumask_clear_cpu(cpu, data->cpus);
1079 spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
1080
1081 __cpufreq_governor(data, CPUFREQ_GOV_START);
1082 __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
1083
1084 kobj = &dev->kobj;
1085 cpufreq_cpu_put(data);
1086 unlock_policy_rwsem_write(cpu);
1087 sysfs_remove_link(kobj, "cpufreq");
1088 return 0;
1089 }
1090#endif
1091
1092#ifdef CONFIG_SMP
1093 1089
1094#ifdef CONFIG_HOTPLUG_CPU 1090#ifdef CONFIG_HOTPLUG_CPU
1095 strncpy(per_cpu(cpufreq_cpu_governor, cpu), data->governor->name, 1091 strncpy(per_cpu(cpufreq_cpu_governor, cpu), data->governor->name,
1096 CPUFREQ_NAME_LEN); 1092 CPUFREQ_NAME_LEN);
1097#endif 1093#endif
1098 1094
1099 /* if we have other CPUs still registered, we need to unlink them, 1095 per_cpu(cpufreq_cpu_data, cpu) = NULL;
1100 * or else wait_for_completion below will lock up. Clean the 1096 cpus = cpumask_weight(data->cpus);
1101 * per_cpu(cpufreq_cpu_data) while holding the lock, and remove 1097 cpumask_clear_cpu(cpu, data->cpus);
1102 * the sysfs links afterwards.
1103 */
1104 if (unlikely(cpumask_weight(data->cpus) > 1)) {
1105 for_each_cpu(j, data->cpus) {
1106 if (j == cpu)
1107 continue;
1108 per_cpu(cpufreq_cpu_data, j) = NULL;
1109 }
1110 }
1111
1112 spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
1113 1098
1114 if (unlikely(cpumask_weight(data->cpus) > 1)) { 1099 if (unlikely((cpu == data->cpu) && (cpus > 1))) {
1115 for_each_cpu(j, data->cpus) { 1100 /* first sibling now owns the new sysfs dir */
1116 if (j == cpu) 1101 cpu_dev = get_cpu_device(cpumask_first(data->cpus));
1117 continue; 1102 sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
1118 pr_debug("removing link for cpu %u\n", j); 1103 ret = kobject_move(&data->kobj, &cpu_dev->kobj);
1119#ifdef CONFIG_HOTPLUG_CPU 1104 if (ret) {
1120 strncpy(per_cpu(cpufreq_cpu_governor, j), 1105 pr_err("%s: Failed to move kobj: %d", __func__, ret);
1121 data->governor->name, CPUFREQ_NAME_LEN); 1106 cpumask_set_cpu(cpu, data->cpus);
1122#endif 1107 ret = sysfs_create_link(&cpu_dev->kobj, &data->kobj,
1123 cpu_dev = get_cpu_device(j); 1108 "cpufreq");
1124 kobj = &cpu_dev->kobj; 1109 spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
1125 unlock_policy_rwsem_write(cpu); 1110 unlock_policy_rwsem_write(cpu);
1126 sysfs_remove_link(kobj, "cpufreq"); 1111 return -EINVAL;
1127 lock_policy_rwsem_write(cpu);
1128 cpufreq_cpu_put(data);
1129 } 1112 }
1113
1114 update_policy_cpu(data, cpu_dev->id);
1115 pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
1116 __func__, cpu_dev->id, cpu);
1130 } 1117 }
1131#else
1132 spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
1133#endif
1134 1118
1135 if (cpufreq_driver->target) 1119 spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
1136 __cpufreq_governor(data, CPUFREQ_GOV_STOP);
1137 1120
1138 kobj = &data->kobj; 1121 pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
1139 cmp = &data->kobj_unregister; 1122 cpufreq_cpu_put(data);
1140 unlock_policy_rwsem_write(cpu); 1123 unlock_policy_rwsem_write(cpu);
1141 kobject_put(kobj); 1124 sysfs_remove_link(&dev->kobj, "cpufreq");
1142 1125
1143 /* we need to make sure that the underlying kobj is actually 1126 /* If cpu is last user of policy, free policy */
1144 * not referenced anymore by anybody before we proceed with 1127 if (cpus == 1) {
1145 * unloading. 1128 lock_policy_rwsem_write(cpu);
1146 */ 1129 kobj = &data->kobj;
1147 pr_debug("waiting for dropping of refcount\n"); 1130 cmp = &data->kobj_unregister;
1148 wait_for_completion(cmp); 1131 unlock_policy_rwsem_write(cpu);
1149 pr_debug("wait complete\n"); 1132 kobject_put(kobj);
1150
1151 lock_policy_rwsem_write(cpu);
1152 if (cpufreq_driver->exit)
1153 cpufreq_driver->exit(data);
1154 unlock_policy_rwsem_write(cpu);
1155 1133
1156#ifdef CONFIG_HOTPLUG_CPU 1134 /* we need to make sure that the underlying kobj is actually
1157 /* when the CPU which is the parent of the kobj is hotplugged 1135 * not referenced anymore by anybody before we proceed with
1158 * offline, check for siblings, and create cpufreq sysfs interface 1136 * unloading.
1159 * and symlinks 1137 */
1160 */ 1138 pr_debug("waiting for dropping of refcount\n");
1161 if (unlikely(cpumask_weight(data->cpus) > 1)) { 1139 wait_for_completion(cmp);
1162 /* first sibling now owns the new sysfs dir */ 1140 pr_debug("wait complete\n");
1163 cpumask_clear_cpu(cpu, data->cpus);
1164 cpufreq_add_dev(get_cpu_device(cpumask_first(data->cpus)), NULL);
1165 1141
1166 /* finally remove our own symlink */
1167 lock_policy_rwsem_write(cpu); 1142 lock_policy_rwsem_write(cpu);
1168 __cpufreq_remove_dev(dev, sif); 1143 if (cpufreq_driver->exit)
1169 } 1144 cpufreq_driver->exit(data);
1170#endif 1145 unlock_policy_rwsem_write(cpu);
1171 1146
1172 free_cpumask_var(data->related_cpus); 1147 free_cpumask_var(data->related_cpus);
1173 free_cpumask_var(data->cpus); 1148 free_cpumask_var(data->cpus);
1174 kfree(data); 1149 kfree(data);
1150 } else if (cpufreq_driver->target) {
1151 __cpufreq_governor(data, CPUFREQ_GOV_START);
1152 __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
1153 }
1175 1154
1176 return 0; 1155 return 0;
1177} 1156}