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

powerpc/perf: Add core IMC PMU support

Add support to register Core In-Memory Collection PMU counters.
Patch adds core IMC specific data structures, along with memory
init functions and CPU hotplug support.

Signed-off-by: Anju T Sudhakar <anju@linux.vnet.ibm.com>
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Anju T Sudhakar and committed by
Michael Ellerman
39a846db 885dcd70

+300 -4
+299 -4
arch/powerpc/perf/imc-pmu.c
··· 31 31 struct imc_pmu_ref *nest_imc_refc; 32 32 static int nest_pmus; 33 33 34 + /* Core IMC data structures and variables */ 35 + 36 + static cpumask_t core_imc_cpumask; 37 + struct imc_pmu_ref *core_imc_refc; 38 + static struct imc_pmu *core_imc_pmu; 39 + 34 40 struct imc_pmu *imc_event_to_pmu(struct perf_event *event) 35 41 { 36 42 return container_of(event->pmu, struct imc_pmu, pmu); ··· 68 62 struct imc_pmu *imc_pmu = container_of(pmu, struct imc_pmu, pmu); 69 63 cpumask_t *active_mask; 70 64 71 - /* Subsequenct patch will add more pmu types here */ 72 65 switch(imc_pmu->domain){ 73 66 case IMC_DOMAIN_NEST: 74 67 active_mask = &nest_imc_cpumask; 68 + break; 69 + case IMC_DOMAIN_CORE: 70 + active_mask = &core_imc_cpumask; 75 71 break; 76 72 default: 77 73 return 0; ··· 494 486 return 0; 495 487 } 496 488 489 + /* 490 + * core_imc_mem_init : Initializes memory for the current core. 491 + * 492 + * Uses alloc_pages_node() and uses the returned address as an argument to 493 + * an opal call to configure the pdbar. The address sent as an argument is 494 + * converted to physical address before the opal call is made. This is the 495 + * base address at which the core imc counters are populated. 496 + */ 497 + static int core_imc_mem_init(int cpu, int size) 498 + { 499 + int phys_id, rc = 0, core_id = (cpu / threads_per_core); 500 + struct imc_mem_info *mem_info; 501 + 502 + /* 503 + * alloc_pages_node() will allocate memory for core in the 504 + * local node only. 505 + */ 506 + phys_id = topology_physical_package_id(cpu); 507 + mem_info = &core_imc_pmu->mem_info[core_id]; 508 + mem_info->id = core_id; 509 + 510 + /* We need only vbase for core counters */ 511 + mem_info->vbase = page_address(alloc_pages_node(phys_id, 512 + GFP_KERNEL | __GFP_ZERO | __GFP_THISNODE, 513 + get_order(size))); 514 + if (!mem_info->vbase) 515 + return -ENOMEM; 516 + 517 + /* Init the mutex */ 518 + core_imc_refc[core_id].id = core_id; 519 + mutex_init(&core_imc_refc[core_id].lock); 520 + 521 + rc = opal_imc_counters_init(OPAL_IMC_COUNTERS_CORE, 522 + __pa((void *)mem_info->vbase), 523 + get_hard_smp_processor_id(cpu)); 524 + if (rc) { 525 + free_pages((u64)mem_info->vbase, get_order(size)); 526 + mem_info->vbase = NULL; 527 + } 528 + 529 + return rc; 530 + } 531 + 532 + static bool is_core_imc_mem_inited(int cpu) 533 + { 534 + struct imc_mem_info *mem_info; 535 + int core_id = (cpu / threads_per_core); 536 + 537 + mem_info = &core_imc_pmu->mem_info[core_id]; 538 + if (!mem_info->vbase) 539 + return false; 540 + 541 + return true; 542 + } 543 + 544 + static int ppc_core_imc_cpu_online(unsigned int cpu) 545 + { 546 + const struct cpumask *l_cpumask; 547 + static struct cpumask tmp_mask; 548 + int ret = 0; 549 + 550 + /* Get the cpumask for this core */ 551 + l_cpumask = cpu_sibling_mask(cpu); 552 + 553 + /* If a cpu for this core is already set, then, don't do anything */ 554 + if (cpumask_and(&tmp_mask, l_cpumask, &core_imc_cpumask)) 555 + return 0; 556 + 557 + if (!is_core_imc_mem_inited(cpu)) { 558 + ret = core_imc_mem_init(cpu, core_imc_pmu->counter_mem_size); 559 + if (ret) { 560 + pr_info("core_imc memory allocation for cpu %d failed\n", cpu); 561 + return ret; 562 + } 563 + } 564 + 565 + /* set the cpu in the mask */ 566 + cpumask_set_cpu(cpu, &core_imc_cpumask); 567 + return 0; 568 + } 569 + 570 + static int ppc_core_imc_cpu_offline(unsigned int cpu) 571 + { 572 + unsigned int ncpu, core_id; 573 + struct imc_pmu_ref *ref; 574 + 575 + /* 576 + * clear this cpu out of the mask, if not present in the mask, 577 + * don't bother doing anything. 578 + */ 579 + if (!cpumask_test_and_clear_cpu(cpu, &core_imc_cpumask)) 580 + return 0; 581 + 582 + /* Find any online cpu in that core except the current "cpu" */ 583 + ncpu = cpumask_any_but(cpu_sibling_mask(cpu), cpu); 584 + 585 + if (ncpu >= 0 && ncpu < nr_cpu_ids) { 586 + cpumask_set_cpu(ncpu, &core_imc_cpumask); 587 + perf_pmu_migrate_context(&core_imc_pmu->pmu, cpu, ncpu); 588 + } else { 589 + /* 590 + * If this is the last cpu in this core then, skip taking refernce 591 + * count mutex lock for this core and directly zero "refc" for 592 + * this core. 593 + */ 594 + opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE, 595 + get_hard_smp_processor_id(cpu)); 596 + core_id = cpu / threads_per_core; 597 + ref = &core_imc_refc[core_id]; 598 + if (!ref) 599 + return -EINVAL; 600 + 601 + ref->refc = 0; 602 + } 603 + return 0; 604 + } 605 + 606 + static int core_imc_pmu_cpumask_init(void) 607 + { 608 + return cpuhp_setup_state(CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE, 609 + "perf/powerpc/imc_core:online", 610 + ppc_core_imc_cpu_online, 611 + ppc_core_imc_cpu_offline); 612 + } 613 + 614 + static void core_imc_counters_release(struct perf_event *event) 615 + { 616 + int rc, core_id; 617 + struct imc_pmu_ref *ref; 618 + 619 + if (event->cpu < 0) 620 + return; 621 + /* 622 + * See if we need to disable the IMC PMU. 623 + * If no events are currently in use, then we have to take a 624 + * mutex to ensure that we don't race with another task doing 625 + * enable or disable the core counters. 626 + */ 627 + core_id = event->cpu / threads_per_core; 628 + 629 + /* Take the mutex lock and decrement the refernce count for this core */ 630 + ref = &core_imc_refc[core_id]; 631 + if (!ref) 632 + return; 633 + 634 + mutex_lock(&ref->lock); 635 + ref->refc--; 636 + if (ref->refc == 0) { 637 + rc = opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE, 638 + get_hard_smp_processor_id(event->cpu)); 639 + if (rc) { 640 + mutex_unlock(&ref->lock); 641 + pr_err("IMC: Unable to stop the counters for core %d\n", core_id); 642 + return; 643 + } 644 + } else if (ref->refc < 0) { 645 + WARN(1, "core-imc: Invalid event reference count\n"); 646 + ref->refc = 0; 647 + } 648 + mutex_unlock(&ref->lock); 649 + } 650 + 651 + static int core_imc_event_init(struct perf_event *event) 652 + { 653 + int core_id, rc; 654 + u64 config = event->attr.config; 655 + struct imc_mem_info *pcmi; 656 + struct imc_pmu *pmu; 657 + struct imc_pmu_ref *ref; 658 + 659 + if (event->attr.type != event->pmu->type) 660 + return -ENOENT; 661 + 662 + /* Sampling not supported */ 663 + if (event->hw.sample_period) 664 + return -EINVAL; 665 + 666 + /* unsupported modes and filters */ 667 + if (event->attr.exclude_user || 668 + event->attr.exclude_kernel || 669 + event->attr.exclude_hv || 670 + event->attr.exclude_idle || 671 + event->attr.exclude_host || 672 + event->attr.exclude_guest) 673 + return -EINVAL; 674 + 675 + if (event->cpu < 0) 676 + return -EINVAL; 677 + 678 + event->hw.idx = -1; 679 + pmu = imc_event_to_pmu(event); 680 + 681 + /* Sanity check for config (event offset) */ 682 + if (((config & IMC_EVENT_OFFSET_MASK) > pmu->counter_mem_size)) 683 + return -EINVAL; 684 + 685 + if (!is_core_imc_mem_inited(event->cpu)) 686 + return -ENODEV; 687 + 688 + core_id = event->cpu / threads_per_core; 689 + pcmi = &core_imc_pmu->mem_info[core_id]; 690 + if ((!pcmi->vbase)) 691 + return -ENODEV; 692 + 693 + /* Get the core_imc mutex for this core */ 694 + ref = &core_imc_refc[core_id]; 695 + if (!ref) 696 + return -EINVAL; 697 + 698 + /* 699 + * Core pmu units are enabled only when it is used. 700 + * See if this is triggered for the first time. 701 + * If yes, take the mutex lock and enable the core counters. 702 + * If not, just increment the count in core_imc_refc struct. 703 + */ 704 + mutex_lock(&ref->lock); 705 + if (ref->refc == 0) { 706 + rc = opal_imc_counters_start(OPAL_IMC_COUNTERS_CORE, 707 + get_hard_smp_processor_id(event->cpu)); 708 + if (rc) { 709 + mutex_unlock(&ref->lock); 710 + pr_err("core-imc: Unable to start the counters for core %d\n", 711 + core_id); 712 + return rc; 713 + } 714 + } 715 + ++ref->refc; 716 + mutex_unlock(&ref->lock); 717 + 718 + event->hw.event_base = (u64)pcmi->vbase + (config & IMC_EVENT_OFFSET_MASK); 719 + event->destroy = core_imc_counters_release; 720 + return 0; 721 + } 722 + 497 723 static u64 * get_event_base_addr(struct perf_event *event) 498 724 { 499 725 /* ··· 806 564 pmu->pmu.attr_groups = pmu->attr_groups; 807 565 pmu->attr_groups[IMC_FORMAT_ATTR] = &imc_format_group; 808 566 809 - /* Subsequenct patch will add more pmu types here */ 810 567 switch (pmu->domain) { 811 568 case IMC_DOMAIN_NEST: 812 569 pmu->pmu.event_init = nest_imc_event_init; 570 + pmu->attr_groups[IMC_CPUMASK_ATTR] = &imc_pmu_cpumask_attr_group; 571 + break; 572 + case IMC_DOMAIN_CORE: 573 + pmu->pmu.event_init = core_imc_event_init; 813 574 pmu->attr_groups[IMC_CPUMASK_ATTR] = &imc_pmu_cpumask_attr_group; 814 575 break; 815 576 default: ··· 866 621 return 0; 867 622 } 868 623 624 + static void cleanup_all_core_imc_memory(void) 625 + { 626 + int i, nr_cores = num_present_cpus() / threads_per_core; 627 + struct imc_mem_info *ptr = core_imc_pmu->mem_info; 628 + int size = core_imc_pmu->counter_mem_size; 629 + 630 + /* mem_info will never be NULL */ 631 + for (i = 0; i < nr_cores; i++) { 632 + if (ptr[i].vbase) 633 + free_pages((u64)ptr->vbase, get_order(size)); 634 + } 635 + 636 + kfree(ptr); 637 + kfree(core_imc_refc); 638 + } 639 + 869 640 /* 870 641 * Common function to unregister cpu hotplug callback and 871 642 * free the memory. ··· 902 641 mutex_unlock(&nest_init_lock); 903 642 } 904 643 644 + /* Free core_imc memory */ 645 + if (pmu_ptr->domain == IMC_DOMAIN_CORE) { 646 + cpuhp_remove_state(CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE); 647 + cleanup_all_core_imc_memory(); 648 + } 649 + 905 650 /* Only free the attr_groups which are dynamically allocated */ 906 651 kfree(pmu_ptr->attr_groups[IMC_EVENT_ATTR]->attrs); 907 652 kfree(pmu_ptr->attr_groups[IMC_EVENT_ATTR]); ··· 923 656 int pmu_index) 924 657 { 925 658 const char *s; 659 + int nr_cores; 926 660 927 661 if (of_property_read_string(parent, "name", &s)) 928 662 return -ENODEV; 929 663 930 - /* Subsequenct patch will add more pmu types here */ 931 664 switch (pmu_ptr->domain) { 932 665 case IMC_DOMAIN_NEST: 933 666 /* Update the pmu name */ ··· 937 670 938 671 /* Needed for hotplug/migration */ 939 672 per_nest_pmu_arr[pmu_index] = pmu_ptr; 673 + break; 674 + case IMC_DOMAIN_CORE: 675 + /* Update the pmu name */ 676 + pmu_ptr->pmu.name = kasprintf(GFP_KERNEL, "%s%s", s, "_imc"); 677 + if (!pmu_ptr->pmu.name) 678 + return -ENOMEM; 679 + 680 + nr_cores = num_present_cpus() / threads_per_core; 681 + pmu_ptr->mem_info = kcalloc(nr_cores, sizeof(struct imc_mem_info), 682 + GFP_KERNEL); 683 + 684 + if (!pmu_ptr->mem_info) 685 + return -ENOMEM; 686 + 687 + core_imc_refc = kcalloc(nr_cores, sizeof(struct imc_pmu_ref), 688 + GFP_KERNEL); 689 + 690 + if (!core_imc_refc) 691 + return -ENOMEM; 692 + 693 + core_imc_pmu = pmu_ptr; 940 694 break; 941 695 default: 942 696 return -EINVAL; ··· 984 696 if (ret) 985 697 goto err_free; 986 698 987 - /* Subsequenct patch will add more pmu types here */ 988 699 switch (pmu_ptr->domain) { 989 700 case IMC_DOMAIN_NEST: 990 701 /* ··· 1008 721 } 1009 722 nest_pmus++; 1010 723 mutex_unlock(&nest_init_lock); 724 + break; 725 + case IMC_DOMAIN_CORE: 726 + ret = core_imc_pmu_cpumask_init(); 727 + if (ret) { 728 + cleanup_all_core_imc_memory(); 729 + return ret; 730 + } 731 + 1011 732 break; 1012 733 default: 1013 734 return -1; /* Unknown domain */
+1
include/linux/cpuhotplug.h
··· 140 140 CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE, 141 141 CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE, 142 142 CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE, 143 + CPUHP_AP_PERF_POWERPC_CORE_IMC_ONLINE, 143 144 CPUHP_AP_WORKQUEUE_ONLINE, 144 145 CPUHP_AP_RCUTREE_ONLINE, 145 146 CPUHP_AP_ONLINE_DYN,