diff options
Diffstat (limited to 'drivers/perf/arm_pmu.c')
-rw-r--r-- | drivers/perf/arm_pmu.c | 138 |
1 files changed, 61 insertions, 77 deletions
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 7bc5eee96b31..f63db346c219 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c | |||
@@ -17,7 +17,6 @@ | |||
17 | #include <linux/export.h> | 17 | #include <linux/export.h> |
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/perf/arm_pmu.h> | 19 | #include <linux/perf/arm_pmu.h> |
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
22 | #include <linux/sched/clock.h> | 21 | #include <linux/sched/clock.h> |
23 | #include <linux/spinlock.h> | 22 | #include <linux/spinlock.h> |
@@ -26,6 +25,9 @@ | |||
26 | 25 | ||
27 | #include <asm/irq_regs.h> | 26 | #include <asm/irq_regs.h> |
28 | 27 | ||
28 | static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu); | ||
29 | static DEFINE_PER_CPU(int, cpu_irq); | ||
30 | |||
29 | static int | 31 | static int |
30 | armpmu_map_cache_event(const unsigned (*cache_map) | 32 | armpmu_map_cache_event(const unsigned (*cache_map) |
31 | [PERF_COUNT_HW_CACHE_MAX] | 33 | [PERF_COUNT_HW_CACHE_MAX] |
@@ -320,17 +322,9 @@ validate_group(struct perf_event *event) | |||
320 | return 0; | 322 | return 0; |
321 | } | 323 | } |
322 | 324 | ||
323 | static struct arm_pmu_platdata *armpmu_get_platdata(struct arm_pmu *armpmu) | ||
324 | { | ||
325 | struct platform_device *pdev = armpmu->plat_device; | ||
326 | |||
327 | return pdev ? dev_get_platdata(&pdev->dev) : NULL; | ||
328 | } | ||
329 | |||
330 | static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) | 325 | static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) |
331 | { | 326 | { |
332 | struct arm_pmu *armpmu; | 327 | struct arm_pmu *armpmu; |
333 | struct arm_pmu_platdata *plat; | ||
334 | int ret; | 328 | int ret; |
335 | u64 start_clock, finish_clock; | 329 | u64 start_clock, finish_clock; |
336 | 330 | ||
@@ -341,14 +335,11 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev) | |||
341 | * dereference. | 335 | * dereference. |
342 | */ | 336 | */ |
343 | armpmu = *(void **)dev; | 337 | armpmu = *(void **)dev; |
344 | 338 | if (WARN_ON_ONCE(!armpmu)) | |
345 | plat = armpmu_get_platdata(armpmu); | 339 | return IRQ_NONE; |
346 | 340 | ||
347 | start_clock = sched_clock(); | 341 | start_clock = sched_clock(); |
348 | if (plat && plat->handle_irq) | 342 | ret = armpmu->handle_irq(irq, armpmu); |
349 | ret = plat->handle_irq(irq, armpmu, armpmu->handle_irq); | ||
350 | else | ||
351 | ret = armpmu->handle_irq(irq, armpmu); | ||
352 | finish_clock = sched_clock(); | 343 | finish_clock = sched_clock(); |
353 | 344 | ||
354 | perf_sample_event_took(finish_clock - start_clock); | 345 | perf_sample_event_took(finish_clock - start_clock); |
@@ -531,54 +522,41 @@ int perf_num_counters(void) | |||
531 | } | 522 | } |
532 | EXPORT_SYMBOL_GPL(perf_num_counters); | 523 | EXPORT_SYMBOL_GPL(perf_num_counters); |
533 | 524 | ||
534 | void armpmu_free_irq(struct arm_pmu *armpmu, int cpu) | 525 | static int armpmu_count_irq_users(const int irq) |
535 | { | 526 | { |
536 | struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; | 527 | int cpu, count = 0; |
537 | int irq = per_cpu(hw_events->irq, cpu); | ||
538 | 528 | ||
539 | if (!cpumask_test_and_clear_cpu(cpu, &armpmu->active_irqs)) | 529 | for_each_possible_cpu(cpu) { |
540 | return; | 530 | if (per_cpu(cpu_irq, cpu) == irq) |
541 | 531 | count++; | |
542 | if (irq_is_percpu_devid(irq)) { | ||
543 | free_percpu_irq(irq, &hw_events->percpu_pmu); | ||
544 | cpumask_clear(&armpmu->active_irqs); | ||
545 | return; | ||
546 | } | 532 | } |
547 | 533 | ||
548 | free_irq(irq, per_cpu_ptr(&hw_events->percpu_pmu, cpu)); | 534 | return count; |
549 | } | 535 | } |
550 | 536 | ||
551 | void armpmu_free_irqs(struct arm_pmu *armpmu) | 537 | void armpmu_free_irq(int irq, int cpu) |
552 | { | 538 | { |
553 | int cpu; | 539 | if (per_cpu(cpu_irq, cpu) == 0) |
540 | return; | ||
541 | if (WARN_ON(irq != per_cpu(cpu_irq, cpu))) | ||
542 | return; | ||
543 | |||
544 | if (!irq_is_percpu_devid(irq)) | ||
545 | free_irq(irq, per_cpu_ptr(&cpu_armpmu, cpu)); | ||
546 | else if (armpmu_count_irq_users(irq) == 1) | ||
547 | free_percpu_irq(irq, &cpu_armpmu); | ||
554 | 548 | ||
555 | for_each_cpu(cpu, &armpmu->supported_cpus) | 549 | per_cpu(cpu_irq, cpu) = 0; |
556 | armpmu_free_irq(armpmu, cpu); | ||
557 | } | 550 | } |
558 | 551 | ||
559 | int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) | 552 | int armpmu_request_irq(int irq, int cpu) |
560 | { | 553 | { |
561 | int err = 0; | 554 | int err = 0; |
562 | struct pmu_hw_events __percpu *hw_events = armpmu->hw_events; | ||
563 | const irq_handler_t handler = armpmu_dispatch_irq; | 555 | const irq_handler_t handler = armpmu_dispatch_irq; |
564 | int irq = per_cpu(hw_events->irq, cpu); | ||
565 | if (!irq) | 556 | if (!irq) |
566 | return 0; | 557 | return 0; |
567 | 558 | ||
568 | if (irq_is_percpu_devid(irq) && cpumask_empty(&armpmu->active_irqs)) { | 559 | if (!irq_is_percpu_devid(irq)) { |
569 | err = request_percpu_irq(irq, handler, "arm-pmu", | ||
570 | &hw_events->percpu_pmu); | ||
571 | } else if (irq_is_percpu_devid(irq)) { | ||
572 | int other_cpu = cpumask_first(&armpmu->active_irqs); | ||
573 | int other_irq = per_cpu(hw_events->irq, other_cpu); | ||
574 | |||
575 | if (irq != other_irq) { | ||
576 | pr_warn("mismatched PPIs detected.\n"); | ||
577 | err = -EINVAL; | ||
578 | goto err_out; | ||
579 | } | ||
580 | } else { | ||
581 | struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu); | ||
582 | unsigned long irq_flags; | 560 | unsigned long irq_flags; |
583 | 561 | ||
584 | err = irq_force_affinity(irq, cpumask_of(cpu)); | 562 | err = irq_force_affinity(irq, cpumask_of(cpu)); |
@@ -589,22 +567,22 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) | |||
589 | goto err_out; | 567 | goto err_out; |
590 | } | 568 | } |
591 | 569 | ||
592 | if (platdata && platdata->irq_flags) { | 570 | irq_flags = IRQF_PERCPU | |
593 | irq_flags = platdata->irq_flags; | 571 | IRQF_NOBALANCING | |
594 | } else { | 572 | IRQF_NO_THREAD; |
595 | irq_flags = IRQF_PERCPU | | ||
596 | IRQF_NOBALANCING | | ||
597 | IRQF_NO_THREAD; | ||
598 | } | ||
599 | 573 | ||
574 | irq_set_status_flags(irq, IRQ_NOAUTOEN); | ||
600 | err = request_irq(irq, handler, irq_flags, "arm-pmu", | 575 | err = request_irq(irq, handler, irq_flags, "arm-pmu", |
601 | per_cpu_ptr(&hw_events->percpu_pmu, cpu)); | 576 | per_cpu_ptr(&cpu_armpmu, cpu)); |
577 | } else if (armpmu_count_irq_users(irq) == 0) { | ||
578 | err = request_percpu_irq(irq, handler, "arm-pmu", | ||
579 | &cpu_armpmu); | ||
602 | } | 580 | } |
603 | 581 | ||
604 | if (err) | 582 | if (err) |
605 | goto err_out; | 583 | goto err_out; |
606 | 584 | ||
607 | cpumask_set_cpu(cpu, &armpmu->active_irqs); | 585 | per_cpu(cpu_irq, cpu) = irq; |
608 | return 0; | 586 | return 0; |
609 | 587 | ||
610 | err_out: | 588 | err_out: |
@@ -612,19 +590,6 @@ err_out: | |||
612 | return err; | 590 | return err; |
613 | } | 591 | } |
614 | 592 | ||
615 | int armpmu_request_irqs(struct arm_pmu *armpmu) | ||
616 | { | ||
617 | int cpu, err; | ||
618 | |||
619 | for_each_cpu(cpu, &armpmu->supported_cpus) { | ||
620 | err = armpmu_request_irq(armpmu, cpu); | ||
621 | if (err) | ||
622 | break; | ||
623 | } | ||
624 | |||
625 | return err; | ||
626 | } | ||
627 | |||
628 | static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu) | 593 | static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu) |
629 | { | 594 | { |
630 | struct pmu_hw_events __percpu *hw_events = pmu->hw_events; | 595 | struct pmu_hw_events __percpu *hw_events = pmu->hw_events; |
@@ -647,12 +612,14 @@ static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node) | |||
647 | if (pmu->reset) | 612 | if (pmu->reset) |
648 | pmu->reset(pmu); | 613 | pmu->reset(pmu); |
649 | 614 | ||
615 | per_cpu(cpu_armpmu, cpu) = pmu; | ||
616 | |||
650 | irq = armpmu_get_cpu_irq(pmu, cpu); | 617 | irq = armpmu_get_cpu_irq(pmu, cpu); |
651 | if (irq) { | 618 | if (irq) { |
652 | if (irq_is_percpu_devid(irq)) { | 619 | if (irq_is_percpu_devid(irq)) |
653 | enable_percpu_irq(irq, IRQ_TYPE_NONE); | 620 | enable_percpu_irq(irq, IRQ_TYPE_NONE); |
654 | return 0; | 621 | else |
655 | } | 622 | enable_irq(irq); |
656 | } | 623 | } |
657 | 624 | ||
658 | return 0; | 625 | return 0; |
@@ -667,8 +634,14 @@ static int arm_perf_teardown_cpu(unsigned int cpu, struct hlist_node *node) | |||
667 | return 0; | 634 | return 0; |
668 | 635 | ||
669 | irq = armpmu_get_cpu_irq(pmu, cpu); | 636 | irq = armpmu_get_cpu_irq(pmu, cpu); |
670 | if (irq && irq_is_percpu_devid(irq)) | 637 | if (irq) { |
671 | disable_percpu_irq(irq); | 638 | if (irq_is_percpu_devid(irq)) |
639 | disable_percpu_irq(irq); | ||
640 | else | ||
641 | disable_irq_nosync(irq); | ||
642 | } | ||
643 | |||
644 | per_cpu(cpu_armpmu, cpu) = NULL; | ||
672 | 645 | ||
673 | return 0; | 646 | return 0; |
674 | } | 647 | } |
@@ -800,18 +773,18 @@ static void cpu_pmu_destroy(struct arm_pmu *cpu_pmu) | |||
800 | &cpu_pmu->node); | 773 | &cpu_pmu->node); |
801 | } | 774 | } |
802 | 775 | ||
803 | struct arm_pmu *armpmu_alloc(void) | 776 | static struct arm_pmu *__armpmu_alloc(gfp_t flags) |
804 | { | 777 | { |
805 | struct arm_pmu *pmu; | 778 | struct arm_pmu *pmu; |
806 | int cpu; | 779 | int cpu; |
807 | 780 | ||
808 | pmu = kzalloc(sizeof(*pmu), GFP_KERNEL); | 781 | pmu = kzalloc(sizeof(*pmu), flags); |
809 | if (!pmu) { | 782 | if (!pmu) { |
810 | pr_info("failed to allocate PMU device!\n"); | 783 | pr_info("failed to allocate PMU device!\n"); |
811 | goto out; | 784 | goto out; |
812 | } | 785 | } |
813 | 786 | ||
814 | pmu->hw_events = alloc_percpu(struct pmu_hw_events); | 787 | pmu->hw_events = alloc_percpu_gfp(struct pmu_hw_events, flags); |
815 | if (!pmu->hw_events) { | 788 | if (!pmu->hw_events) { |
816 | pr_info("failed to allocate per-cpu PMU data.\n"); | 789 | pr_info("failed to allocate per-cpu PMU data.\n"); |
817 | goto out_free_pmu; | 790 | goto out_free_pmu; |
@@ -857,6 +830,17 @@ out: | |||
857 | return NULL; | 830 | return NULL; |
858 | } | 831 | } |
859 | 832 | ||
833 | struct arm_pmu *armpmu_alloc(void) | ||
834 | { | ||
835 | return __armpmu_alloc(GFP_KERNEL); | ||
836 | } | ||
837 | |||
838 | struct arm_pmu *armpmu_alloc_atomic(void) | ||
839 | { | ||
840 | return __armpmu_alloc(GFP_ATOMIC); | ||
841 | } | ||
842 | |||
843 | |||
860 | void armpmu_free(struct arm_pmu *pmu) | 844 | void armpmu_free(struct arm_pmu *pmu) |
861 | { | 845 | { |
862 | free_percpu(pmu->hw_events); | 846 | free_percpu(pmu->hw_events); |