Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / devfreq: passive: Keep cpufreq_policy for possible cpus

The passive governor requires the cpu data to get the next target frequency
of devfreq device if depending on cpu. In order to reduce the unnecessary
memory data, keep cpufreq_policy data for possible cpus instead of NR_CPU.

Tested-by: Chen-Yu Tsai <wenst@chromium.org>
Tested-by: Johnson Wang <johnson.wang@mediatek.com>
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>

+64 -18
+3
drivers/devfreq/governor.h
··· 49 49 50 50 /** 51 51 * struct devfreq_cpu_data - Hold the per-cpu data 52 + * @node: list node 52 53 * @dev: reference to cpu device. 53 54 * @first_cpu: the cpumask of the first cpu of a policy. 54 55 * @opp_table: reference to cpu opp table. ··· 61 60 * This is auto-populated by the governor. 62 61 */ 63 62 struct devfreq_cpu_data { 63 + struct list_head node; 64 + 64 65 struct device *dev; 65 66 unsigned int first_cpu; 66 67
+59 -16
drivers/devfreq/governor_passive.c
··· 1 - // SPDX-License-Identifier: GPL-2.0-only 1 + // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 3 * linux/drivers/devfreq/governor_passive.c 4 4 * ··· 17 17 #include "governor.h" 18 18 19 19 #define HZ_PER_KHZ 1000 20 + 21 + static struct devfreq_cpu_data * 22 + get_parent_cpu_data(struct devfreq_passive_data *p_data, 23 + struct cpufreq_policy *policy) 24 + { 25 + struct devfreq_cpu_data *parent_cpu_data; 26 + 27 + if (!p_data || !policy) 28 + return NULL; 29 + 30 + list_for_each_entry(parent_cpu_data, &p_data->cpu_data_list, node) 31 + if (parent_cpu_data->first_cpu == cpumask_first(policy->related_cpus)) 32 + return parent_cpu_data; 33 + 34 + return NULL; 35 + } 20 36 21 37 static unsigned long get_target_freq_by_required_opp(struct device *p_dev, 22 38 struct opp_table *p_opp_table, ··· 67 51 struct devfreq_passive_data *p_data = 68 52 (struct devfreq_passive_data *)devfreq->data; 69 53 struct devfreq_cpu_data *parent_cpu_data; 54 + struct cpufreq_policy *policy; 70 55 unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent; 71 56 unsigned long dev_min, dev_max; 72 57 unsigned long freq = 0; 58 + int ret = 0; 73 59 74 60 for_each_online_cpu(cpu) { 75 - parent_cpu_data = p_data->parent_cpu_data[cpu]; 76 - if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu) 61 + policy = cpufreq_cpu_get(cpu); 62 + if (!policy) { 63 + ret = -EINVAL; 77 64 continue; 65 + } 66 + 67 + parent_cpu_data = get_parent_cpu_data(p_data, policy); 68 + if (!parent_cpu_data) { 69 + cpufreq_cpu_put(policy); 70 + continue; 71 + } 78 72 79 73 /* Get target freq via required opps */ 80 74 cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ; ··· 93 67 devfreq->opp_table, &cpu_cur); 94 68 if (freq) { 95 69 *target_freq = max(freq, *target_freq); 70 + cpufreq_cpu_put(policy); 96 71 continue; 97 72 } 98 73 ··· 108 81 freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100); 109 82 110 83 *target_freq = max(freq, *target_freq); 84 + cpufreq_cpu_put(policy); 111 85 } 112 86 113 - return 0; 87 + return ret; 114 88 } 115 89 116 90 static int get_target_freq_with_devfreq(struct devfreq *devfreq, ··· 196 168 unsigned int cur_freq; 197 169 int ret; 198 170 199 - if (event != CPUFREQ_POSTCHANGE || !freqs || 200 - !p_data->parent_cpu_data[freqs->policy->cpu]) 171 + if (event != CPUFREQ_POSTCHANGE || !freqs) 201 172 return 0; 202 173 203 - parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu]; 204 - if (parent_cpu_data->cur_freq == freqs->new) 174 + parent_cpu_data = get_parent_cpu_data(p_data, freqs->policy); 175 + if (!parent_cpu_data || parent_cpu_data->cur_freq == freqs->new) 205 176 return 0; 206 177 207 178 cur_freq = parent_cpu_data->cur_freq; ··· 223 196 struct devfreq_passive_data *p_data 224 197 = (struct devfreq_passive_data *)devfreq->data; 225 198 struct devfreq_cpu_data *parent_cpu_data; 226 - int cpu, ret; 199 + int cpu, ret = 0; 227 200 228 201 if (p_data->nb.notifier_call) { 229 202 ret = cpufreq_unregister_notifier(&p_data->nb, ··· 233 206 } 234 207 235 208 for_each_possible_cpu(cpu) { 236 - parent_cpu_data = p_data->parent_cpu_data[cpu]; 237 - if (!parent_cpu_data) 209 + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); 210 + if (!policy) { 211 + ret = -EINVAL; 238 212 continue; 213 + } 239 214 215 + parent_cpu_data = get_parent_cpu_data(p_data, policy); 216 + if (!parent_cpu_data) { 217 + cpufreq_cpu_put(policy); 218 + continue; 219 + } 220 + 221 + list_del(&parent_cpu_data->node); 240 222 if (parent_cpu_data->opp_table) 241 223 dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); 242 224 kfree(parent_cpu_data); 225 + cpufreq_cpu_put(policy); 243 226 } 244 227 245 - return 0; 228 + return ret; 246 229 } 247 230 248 231 static int cpufreq_passive_register_notifier(struct devfreq *devfreq) ··· 267 230 unsigned int cpu; 268 231 int ret; 269 232 233 + p_data->cpu_data_list 234 + = (struct list_head)LIST_HEAD_INIT(p_data->cpu_data_list); 235 + 270 236 p_data->nb.notifier_call = cpufreq_passive_notifier_call; 271 237 ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER); 272 238 if (ret) { ··· 279 239 } 280 240 281 241 for_each_possible_cpu(cpu) { 282 - if (p_data->parent_cpu_data[cpu]) 283 - continue; 284 - 285 242 policy = cpufreq_cpu_get(cpu); 286 243 if (!policy) { 287 244 ret = -EPROBE_DEFER; 288 245 goto err; 246 + } 247 + 248 + parent_cpu_data = get_parent_cpu_data(p_data, policy); 249 + if (parent_cpu_data) { 250 + cpufreq_cpu_put(policy); 251 + continue; 289 252 } 290 253 291 254 parent_cpu_data = kzalloc(sizeof(*parent_cpu_data), ··· 319 276 parent_cpu_data->min_freq = policy->cpuinfo.min_freq; 320 277 parent_cpu_data->max_freq = policy->cpuinfo.max_freq; 321 278 322 - p_data->parent_cpu_data[cpu] = parent_cpu_data; 279 + list_add_tail(&parent_cpu_data->node, &p_data->cpu_data_list); 323 280 cpufreq_cpu_put(policy); 324 281 } 325 282
+2 -2
include/linux/devfreq.h
··· 309 309 * @this: the devfreq instance of own device. 310 310 * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or 311 311 * CPUFREQ_TRANSITION_NOTIFIER list. 312 - * @parent_cpu_data: the state min/max/current frequency of all online cpu's. 312 + * @cpu_data_list: the list of cpu frequency data for all cpufreq_policy. 313 313 * 314 314 * The devfreq_passive_data have to set the devfreq instance of parent 315 315 * device with governors except for the passive governor. But, don't need to ··· 329 329 /* For passive governor's internal use. Don't need to set them */ 330 330 struct devfreq *this; 331 331 struct notifier_block nb; 332 - struct devfreq_cpu_data *parent_cpu_data[NR_CPUS]; 332 + struct list_head cpu_data_list; 333 333 }; 334 334 #endif 335 335