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

drivers: cci: reject groups spanning multiple HW PMUs

The perf core implicitly rejects events spanning multiple HW PMUs, as in
these cases the event->ctx will differ. However this validation is
performed after pmu::event_init() is called in perf_init_event(), and
thus pmu::event_init() may be called with a group leader from a
different HW PMU.

The CCI PMU driver does not take this fact into account, and assumes
that the any other hardware event belongs to the CCI. There are two
issues with it :

1) It is wrong and we should reject such groups.
2) Validation allocates an temporary idx for this non-cci event, which leads
to wrong calculation of the counter availability, and eventually lesser
number of events in the group.

This patch updates the CCI PMU driver to first test for and reject
events from other PMUs, which is the right thing to do.

Cc: Will Deacon <will.deacon@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Peter Ziljstra (Intel) <peterz@infradead.org>
Signed-off-by: Suzuki K. Poulose <suzuki.poulose@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>

authored by

Suzuki K. Poulose and committed by
Will Deacon
b1862199 06e5801b

+14 -5
+14 -5
drivers/bus/arm-cci.c
··· 660 660 } 661 661 662 662 static int 663 - validate_event(struct cci_pmu_hw_events *hw_events, 664 - struct perf_event *event) 663 + validate_event(struct pmu *cci_pmu, 664 + struct cci_pmu_hw_events *hw_events, 665 + struct perf_event *event) 665 666 { 666 667 if (is_software_event(event)) 667 668 return 1; 669 + 670 + /* 671 + * Reject groups spanning multiple HW PMUs (e.g. CPU + CCI). The 672 + * core perf code won't check that the pmu->ctx == leader->ctx 673 + * until after pmu->event_init(event). 674 + */ 675 + if (event->pmu != cci_pmu) 676 + return 0; 668 677 669 678 if (event->state < PERF_EVENT_STATE_OFF) 670 679 return 1; ··· 696 687 .used_mask = CPU_BITS_NONE, 697 688 }; 698 689 699 - if (!validate_event(&fake_pmu, leader)) 690 + if (!validate_event(event->pmu, &fake_pmu, leader)) 700 691 return -EINVAL; 701 692 702 693 list_for_each_entry(sibling, &leader->sibling_list, group_entry) { 703 - if (!validate_event(&fake_pmu, sibling)) 694 + if (!validate_event(event->pmu, &fake_pmu, sibling)) 704 695 return -EINVAL; 705 696 } 706 697 707 - if (!validate_event(&fake_pmu, event)) 698 + if (!validate_event(event->pmu, &fake_pmu, event)) 708 699 return -EINVAL; 709 700 710 701 return 0;