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

cppc_cpufreq: replace per-cpu data array with a list

The cppc_cpudata per-cpu storage was inefficient (1) additional to causing
functional issues (2) when CPUs are hotplugged out, due to per-cpu data
being improperly initialised.

(1) The amount of information needed for CPPC performance control in its
cpufreq driver depends on the domain (PSD) coordination type:

ANY: One set of CPPC control and capability data (e.g desired
performance, highest/lowest performance, etc) applies to all
CPUs in the domain.

ALL: Same as ANY. To be noted that this type is not currently
supported. When supported, information about which CPUs
belong to a domain is needed in order for frequency change
requests to be sent to each of them.

HW: It's necessary to store CPPC control and capability
information for all the CPUs. HW will then coordinate the
performance state based on their limitations and requests.

NONE: Same as HW. No HW coordination is expected.

Despite this, the previous initialisation code would indiscriminately
allocate memory for all CPUs (all_cpu_data) and unnecessarily
duplicate performance capabilities and the domain sharing mask and type
for each possible CPU.

(2) With the current per-cpu structure, when having ANY coordination,
the cppc_cpudata cpu information is not initialised (will remain 0)
for all CPUs in a policy, other than policy->cpu. When policy->cpu is
hotplugged out, the driver will incorrectly use the uninitialised (0)
value of the other CPUs when making frequency changes. Additionally,
the previous values stored in the perf_ctrls.desired_perf will be
lost when policy->cpu changes.

Therefore replace the array of per cpu data with a list. The memory for
each structure is allocated at policy init, where a single structure
can be allocated per policy, not per cpu. In order to accommodate the
struct list_head node in the cppc_cpudata structure, the now unused cpu
and cur_policy variables are removed.

For example, on a arm64 Juno platform with 6 CPUs: (0, 1, 2, 3) in PSD1,
(4, 5) in PSD2 - ANY coordination, the memory allocation comparison shows:

Before patch:

- ANY coordination:
total slack req alloc/free caller
0 0 0 0/1 _kernel_size_le_hi32+0x0xffff800008ff7810
0 0 0 0/6 _kernel_size_le_hi32+0x0xffff800008ff7808
128 80 48 1/0 _kernel_size_le_hi32+0x0xffff800008ffc070
768 0 768 6/0 _kernel_size_le_hi32+0x0xffff800008ffc0e4

After patch:

- ANY coordination:
total slack req alloc/free caller
256 0 256 2/0 _kernel_size_le_hi32+0x0xffff800008fed410
0 0 0 0/2 _kernel_size_le_hi32+0x0xffff800008fed274

Additional notes:
- A pointer to the policy's cppc_cpudata is stored in policy->driver_data
- Driver registration is skipped if _CPC entries are not present.

Signed-off-by: Ionela Voinescu <ionela.voinescu@arm.com>
Tested-by: Mian Yousaf Kaukab <ykaukab@suse.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Ionela Voinescu and committed by
Rafael J. Wysocki
a28b2bfc cfdc589f

+154 -167
+60 -81
drivers/acpi/cppc_acpi.c
··· 413 413 return result; 414 414 } 415 415 416 + bool acpi_cpc_valid(void) 417 + { 418 + struct cpc_desc *cpc_ptr; 419 + int cpu; 420 + 421 + for_each_possible_cpu(cpu) { 422 + cpc_ptr = per_cpu(cpc_desc_ptr, cpu); 423 + if (!cpc_ptr) 424 + return false; 425 + } 426 + 427 + return true; 428 + } 429 + EXPORT_SYMBOL_GPL(acpi_cpc_valid); 430 + 416 431 /** 417 - * acpi_get_psd_map - Map the CPUs in a common freq domain. 418 - * @all_cpu_data: Ptrs to CPU specific CPPC data including PSD info. 432 + * acpi_get_psd_map - Map the CPUs in the freq domain of a given cpu 433 + * @cpu: Find all CPUs that share a domain with cpu. 434 + * @cpu_data: Pointer to CPU specific CPPC data including PSD info. 419 435 * 420 436 * Return: 0 for success or negative value for err. 421 437 */ 422 - int acpi_get_psd_map(struct cppc_cpudata **all_cpu_data) 438 + int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data) 423 439 { 424 - int count_target; 425 - int retval = 0; 426 - unsigned int i, j; 427 - cpumask_var_t covered_cpus; 428 - struct cppc_cpudata *pr, *match_pr; 429 - struct acpi_psd_package *pdomain; 430 - struct acpi_psd_package *match_pdomain; 431 440 struct cpc_desc *cpc_ptr, *match_cpc_ptr; 432 - 433 - if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL)) 434 - return -ENOMEM; 441 + struct acpi_psd_package *match_pdomain; 442 + struct acpi_psd_package *pdomain; 443 + int count_target, i; 435 444 436 445 /* 437 446 * Now that we have _PSD data from all CPUs, let's setup P-state 438 447 * domain info. 439 448 */ 449 + cpc_ptr = per_cpu(cpc_desc_ptr, cpu); 450 + if (!cpc_ptr) 451 + return -EFAULT; 452 + 453 + pdomain = &(cpc_ptr->domain_info); 454 + cpumask_set_cpu(cpu, cpu_data->shared_cpu_map); 455 + if (pdomain->num_processors <= 1) 456 + return 0; 457 + 458 + /* Validate the Domain info */ 459 + count_target = pdomain->num_processors; 460 + if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) 461 + cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ALL; 462 + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) 463 + cpu_data->shared_type = CPUFREQ_SHARED_TYPE_HW; 464 + else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) 465 + cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY; 466 + 440 467 for_each_possible_cpu(i) { 441 - if (cpumask_test_cpu(i, covered_cpus)) 468 + if (i == cpu) 442 469 continue; 443 470 444 - pr = all_cpu_data[i]; 445 - cpc_ptr = per_cpu(cpc_desc_ptr, i); 446 - if (!cpc_ptr) { 447 - retval = -EFAULT; 448 - goto err_ret; 449 - } 471 + match_cpc_ptr = per_cpu(cpc_desc_ptr, i); 472 + if (!match_cpc_ptr) 473 + goto err_fault; 450 474 451 - pdomain = &(cpc_ptr->domain_info); 452 - cpumask_set_cpu(i, pr->shared_cpu_map); 453 - cpumask_set_cpu(i, covered_cpus); 454 - if (pdomain->num_processors <= 1) 475 + match_pdomain = &(match_cpc_ptr->domain_info); 476 + if (match_pdomain->domain != pdomain->domain) 455 477 continue; 456 478 457 - /* Validate the Domain info */ 458 - count_target = pdomain->num_processors; 459 - if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL) 460 - pr->shared_type = CPUFREQ_SHARED_TYPE_ALL; 461 - else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL) 462 - pr->shared_type = CPUFREQ_SHARED_TYPE_HW; 463 - else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY) 464 - pr->shared_type = CPUFREQ_SHARED_TYPE_ANY; 479 + /* Here i and cpu are in the same domain */ 480 + if (match_pdomain->num_processors != count_target) 481 + goto err_fault; 465 482 466 - for_each_possible_cpu(j) { 467 - if (i == j) 468 - continue; 483 + if (pdomain->coord_type != match_pdomain->coord_type) 484 + goto err_fault; 469 485 470 - match_cpc_ptr = per_cpu(cpc_desc_ptr, j); 471 - if (!match_cpc_ptr) { 472 - retval = -EFAULT; 473 - goto err_ret; 474 - } 475 - 476 - match_pdomain = &(match_cpc_ptr->domain_info); 477 - if (match_pdomain->domain != pdomain->domain) 478 - continue; 479 - 480 - /* Here i and j are in the same domain */ 481 - if (match_pdomain->num_processors != count_target) { 482 - retval = -EFAULT; 483 - goto err_ret; 484 - } 485 - 486 - if (pdomain->coord_type != match_pdomain->coord_type) { 487 - retval = -EFAULT; 488 - goto err_ret; 489 - } 490 - 491 - cpumask_set_cpu(j, covered_cpus); 492 - cpumask_set_cpu(j, pr->shared_cpu_map); 493 - } 494 - 495 - for_each_cpu(j, pr->shared_cpu_map) { 496 - if (i == j) 497 - continue; 498 - 499 - match_pr = all_cpu_data[j]; 500 - match_pr->shared_type = pr->shared_type; 501 - cpumask_copy(match_pr->shared_cpu_map, 502 - pr->shared_cpu_map); 503 - } 486 + cpumask_set_cpu(i, cpu_data->shared_cpu_map); 504 487 } 505 - goto out; 506 488 507 - err_ret: 508 - for_each_possible_cpu(i) { 509 - pr = all_cpu_data[i]; 489 + return 0; 510 490 511 - /* Assume no coordination on any error parsing domain info */ 512 - cpumask_clear(pr->shared_cpu_map); 513 - cpumask_set_cpu(i, pr->shared_cpu_map); 514 - pr->shared_type = CPUFREQ_SHARED_TYPE_NONE; 515 - } 516 - out: 517 - free_cpumask_var(covered_cpus); 518 - return retval; 491 + err_fault: 492 + /* Assume no coordination on any error parsing domain info */ 493 + cpumask_clear(cpu_data->shared_cpu_map); 494 + cpumask_set_cpu(cpu, cpu_data->shared_cpu_map); 495 + cpu_data->shared_type = CPUFREQ_SHARED_TYPE_NONE; 496 + 497 + return -EFAULT; 519 498 } 520 499 EXPORT_SYMBOL_GPL(acpi_get_psd_map); 521 500
+91 -83
drivers/cpufreq/cppc_cpufreq.c
··· 30 30 #define DMI_PROCESSOR_MAX_SPEED 0x14 31 31 32 32 /* 33 - * These structs contain information parsed from per CPU 34 - * ACPI _CPC structures. 35 - * e.g. For each CPU the highest, lowest supported 36 - * performance capabilities, desired performance level 37 - * requested etc. 33 + * This list contains information parsed from per CPU ACPI _CPC and _PSD 34 + * structures: e.g. the highest and lowest supported performance, capabilities, 35 + * desired performance, level requested etc. Depending on the share_type, not 36 + * all CPUs will have an entry in the list. 38 37 */ 39 - static struct cppc_cpudata **all_cpu_data; 38 + static LIST_HEAD(cpu_data_list); 39 + 40 40 static bool boost_supported; 41 41 42 42 struct cppc_workaround_oem_info { ··· 148 148 static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, 149 149 unsigned int target_freq, 150 150 unsigned int relation) 151 + 151 152 { 152 - struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu]; 153 + struct cppc_cpudata *cpu_data = policy->driver_data; 153 154 unsigned int cpu = policy->cpu; 154 155 struct cpufreq_freqs freqs; 155 156 u32 desired_perf; ··· 184 183 185 184 static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) 186 185 { 187 - struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu]; 186 + struct cppc_cpudata *cpu_data = policy->driver_data; 188 187 struct cppc_perf_caps *caps = &cpu_data->perf_caps; 189 188 unsigned int cpu = policy->cpu; 190 189 int ret; ··· 195 194 if (ret) 196 195 pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", 197 196 caps->lowest_perf, cpu, ret); 197 + 198 + /* Remove CPU node from list and free driver data for policy */ 199 + free_cpumask_var(cpu_data->shared_cpu_map); 200 + list_del(&cpu_data->node); 201 + kfree(policy->driver_data); 202 + policy->driver_data = NULL; 198 203 } 199 204 200 205 /* ··· 246 239 } 247 240 #endif 248 241 249 - static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) 242 + 243 + static struct cppc_cpudata *cppc_cpufreq_get_cpu_data(unsigned int cpu) 250 244 { 251 - struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu]; 252 - struct cppc_perf_caps *caps = &cpu_data->perf_caps; 253 - unsigned int cpu = policy->cpu; 254 - int i, ret = 0; 245 + struct cppc_cpudata *cpu_data; 246 + int ret; 255 247 256 - cpu_data->cpu = cpu; 257 - ret = cppc_get_perf_caps(cpu, caps); 248 + cpu_data = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL); 249 + if (!cpu_data) 250 + goto out; 258 251 252 + if (!zalloc_cpumask_var(&cpu_data->shared_cpu_map, GFP_KERNEL)) 253 + goto free_cpu; 254 + 255 + ret = acpi_get_psd_map(cpu, cpu_data); 259 256 if (ret) { 260 - pr_debug("Err reading CPU%d perf capabilities. ret:%d\n", 261 - cpu, ret); 262 - return ret; 257 + pr_debug("Err parsing CPU%d PSD data: ret:%d\n", cpu, ret); 258 + goto free_mask; 259 + } 260 + 261 + ret = cppc_get_perf_caps(cpu, &cpu_data->perf_caps); 262 + if (ret) { 263 + pr_debug("Err reading CPU%d perf caps: ret:%d\n", cpu, ret); 264 + goto free_mask; 263 265 } 264 266 265 267 /* Convert the lowest and nominal freq from MHz to KHz */ 266 - caps->lowest_freq *= 1000; 267 - caps->nominal_freq *= 1000; 268 + cpu_data->perf_caps.lowest_freq *= 1000; 269 + cpu_data->perf_caps.nominal_freq *= 1000; 270 + 271 + list_add(&cpu_data->node, &cpu_data_list); 272 + 273 + return cpu_data; 274 + 275 + free_mask: 276 + free_cpumask_var(cpu_data->shared_cpu_map); 277 + free_cpu: 278 + kfree(cpu_data); 279 + out: 280 + return NULL; 281 + } 282 + 283 + static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) 284 + { 285 + unsigned int cpu = policy->cpu; 286 + struct cppc_cpudata *cpu_data; 287 + struct cppc_perf_caps *caps; 288 + int ret; 289 + 290 + cpu_data = cppc_cpufreq_get_cpu_data(cpu); 291 + if (!cpu_data) { 292 + pr_err("Error in acquiring _CPC/_PSD data for CPU%d.\n", cpu); 293 + return -ENODEV; 294 + } 295 + caps = &cpu_data->perf_caps; 296 + policy->driver_data = cpu_data; 268 297 269 298 /* 270 299 * Set min to lowest nonlinear perf to avoid any efficiency penalty (see ··· 330 287 /* Nothing to be done - we'll have a policy for each CPU */ 331 288 break; 332 289 case CPUFREQ_SHARED_TYPE_ANY: 333 - /* All CPUs in the domain will share a policy */ 290 + /* 291 + * All CPUs in the domain will share a policy and all cpufreq 292 + * operations will use a single cppc_cpudata structure stored 293 + * in policy->driver_data. 294 + */ 334 295 cpumask_copy(policy->cpus, cpu_data->shared_cpu_map); 335 - 336 - for_each_cpu(i, policy->cpus) { 337 - if (unlikely(i == cpu)) 338 - continue; 339 - 340 - memcpy(&all_cpu_data[i]->perf_caps, caps, 341 - sizeof(cpu_data->perf_caps)); 342 - } 343 296 break; 344 297 default: 345 298 pr_debug("Unsupported CPU co-ord type: %d\n", 346 299 policy->shared_type); 347 300 return -EFAULT; 348 301 } 349 - 350 - cpu_data->cur_policy = policy; 351 302 352 303 /* 353 304 * If 'highest_perf' is greater than 'nominal_perf', we assume CPU Boost ··· 397 360 static unsigned int cppc_cpufreq_get_rate(unsigned int cpu) 398 361 { 399 362 struct cppc_perf_fb_ctrs fb_ctrs_t0 = {0}, fb_ctrs_t1 = {0}; 400 - struct cppc_cpudata *cpu_data = all_cpu_data[cpu]; 363 + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); 364 + struct cppc_cpudata *cpu_data = policy->driver_data; 401 365 int ret; 366 + 367 + cpufreq_cpu_put(policy); 402 368 403 369 ret = cppc_get_perf_ctrs(cpu, &fb_ctrs_t0); 404 370 if (ret) ··· 418 378 419 379 static int cppc_cpufreq_set_boost(struct cpufreq_policy *policy, int state) 420 380 { 421 - struct cppc_cpudata *cpu_data = all_cpu_data[policy->cpu]; 381 + struct cppc_cpudata *cpu_data = policy->driver_data; 422 382 struct cppc_perf_caps *caps = &cpu_data->perf_caps; 423 383 int ret; 424 384 ··· 444 404 445 405 static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) 446 406 { 447 - unsigned int cpu = policy->cpu; 407 + struct cppc_cpudata *cpu_data = policy->driver_data; 448 408 449 - return cpufreq_show_cpus(all_cpu_data[cpu]->shared_cpu_map, buf); 409 + return cpufreq_show_cpus(cpu_data->shared_cpu_map, buf); 450 410 } 451 411 cpufreq_freq_attr_ro(freqdomain_cpus); 452 412 ··· 475 435 */ 476 436 static unsigned int hisi_cppc_cpufreq_get_rate(unsigned int cpu) 477 437 { 478 - struct cppc_cpudata *cpu_data = all_cpu_data[cpu]; 438 + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); 439 + struct cppc_cpudata *cpu_data = policy->driver_data; 479 440 u64 desired_perf; 480 441 int ret; 442 + 443 + cpufreq_cpu_put(policy); 481 444 482 445 ret = cppc_get_desired_perf(cpu, &desired_perf); 483 446 if (ret < 0) ··· 514 471 515 472 static int __init cppc_cpufreq_init(void) 516 473 { 517 - struct cppc_cpudata *cpu_data; 518 - int i, ret = 0; 519 - 520 - if (acpi_disabled) 474 + if ((acpi_disabled) || !acpi_cpc_valid()) 521 475 return -ENODEV; 522 476 523 - all_cpu_data = kcalloc(num_possible_cpus(), sizeof(void *), 524 - GFP_KERNEL); 525 - if (!all_cpu_data) 526 - return -ENOMEM; 527 - 528 - for_each_possible_cpu(i) { 529 - all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL); 530 - if (!all_cpu_data[i]) 531 - goto out; 532 - 533 - cpu_data = all_cpu_data[i]; 534 - if (!zalloc_cpumask_var(&cpu_data->shared_cpu_map, GFP_KERNEL)) 535 - goto out; 536 - } 537 - 538 - ret = acpi_get_psd_map(all_cpu_data); 539 - if (ret) { 540 - pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n"); 541 - goto out; 542 - } 477 + INIT_LIST_HEAD(&cpu_data_list); 543 478 544 479 cppc_check_hisi_workaround(); 545 480 546 - ret = cpufreq_register_driver(&cppc_cpufreq_driver); 547 - if (ret) 548 - goto out; 481 + return cpufreq_register_driver(&cppc_cpufreq_driver); 482 + } 549 483 550 - return ret; 484 + static inline void free_cpu_data(void) 485 + { 486 + struct cppc_cpudata *iter, *tmp; 551 487 552 - out: 553 - for_each_possible_cpu(i) { 554 - cpu_data = all_cpu_data[i]; 555 - if (!cpu_data) 556 - break; 557 - free_cpumask_var(cpu_data->shared_cpu_map); 558 - kfree(cpu_data); 488 + list_for_each_entry_safe(iter, tmp, &cpu_data_list, node) { 489 + free_cpumask_var(iter->shared_cpu_map); 490 + list_del(&iter->node); 491 + kfree(iter); 559 492 } 560 493 561 - kfree(all_cpu_data); 562 - return -ENODEV; 563 494 } 564 495 565 496 static void __exit cppc_cpufreq_exit(void) 566 497 { 567 - struct cppc_cpudata *cpu_data; 568 - int i; 569 - 570 498 cpufreq_unregister_driver(&cppc_cpufreq_driver); 571 499 572 - for_each_possible_cpu(i) { 573 - cpu_data = all_cpu_data[i]; 574 - free_cpumask_var(cpu_data->shared_cpu_map); 575 - kfree(cpu_data); 576 - } 577 - 578 - kfree(all_cpu_data); 500 + free_cpu_data(); 579 501 } 580 502 581 503 module_exit(cppc_cpufreq_exit);
+3 -3
include/acpi/cppc_acpi.h
··· 124 124 125 125 /* Per CPU container for runtime CPPC management. */ 126 126 struct cppc_cpudata { 127 - int cpu; 127 + struct list_head node; 128 128 struct cppc_perf_caps perf_caps; 129 129 struct cppc_perf_ctrls perf_ctrls; 130 130 struct cppc_perf_fb_ctrs perf_fb_ctrs; 131 - struct cpufreq_policy *cur_policy; 132 131 unsigned int shared_type; 133 132 cpumask_var_t shared_cpu_map; 134 133 }; ··· 136 137 extern int cppc_get_perf_ctrs(int cpu, struct cppc_perf_fb_ctrs *perf_fb_ctrs); 137 138 extern int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls); 138 139 extern int cppc_get_perf_caps(int cpu, struct cppc_perf_caps *caps); 139 - extern int acpi_get_psd_map(struct cppc_cpudata **); 140 + extern bool acpi_cpc_valid(void); 141 + extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data); 140 142 extern unsigned int cppc_get_transition_latency(int cpu); 141 143 extern bool cpc_ffh_supported(void); 142 144 extern int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val);