Skip to content

Commit d8226d8

Browse files
robherringwilldeacon
authored andcommitted
perf: arm_pmuv3: Add support for Armv9.4 PMU instruction counter
Armv9.4/8.9 PMU adds optional support for a fixed instruction counter similar to the fixed cycle counter. Support for the feature is indicated in the ID_AA64DFR1_EL1 register PMICNTR field. The counter is not accessible in AArch32. Existing userspace using direct counter access won't know how to handle the fixed instruction counter, so we have to avoid using the counter when user access is requested. Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Rob Herring (Arm) <robh@kernel.org> Tested-by: James Clark <james.clark@linaro.org> Link: https://lore.kernel.org/r/20240731-arm-pmu-3-9-icntr-v3-7-280a8d7ff465@kernel.org Signed-off-by: Will Deacon <will@kernel.org>
1 parent 2f62701 commit d8226d8

File tree

7 files changed

+114
-6
lines changed

7 files changed

+114
-6
lines changed

arch/arm/include/asm/arm_pmuv3.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ static inline u32 read_pmuver(void)
127127
return (dfr0 >> 24) & 0xf;
128128
}
129129

130+
static inline bool pmuv3_has_icntr(void)
131+
{
132+
/* FEAT_PMUv3_ICNTR not accessible for 32-bit */
133+
return false;
134+
}
135+
130136
static inline void write_pmcr(u32 val)
131137
{
132138
write_sysreg(val, PMCR);
@@ -152,6 +158,13 @@ static inline u64 read_pmccntr(void)
152158
return read_sysreg(PMCCNTR);
153159
}
154160

161+
static inline void write_pmicntr(u64 val) {}
162+
163+
static inline u64 read_pmicntr(void)
164+
{
165+
return 0;
166+
}
167+
155168
static inline void write_pmcntenset(u32 val)
156169
{
157170
write_sysreg(val, PMCNTENSET);
@@ -177,6 +190,13 @@ static inline void write_pmccfiltr(u32 val)
177190
write_sysreg(val, PMCCFILTR);
178191
}
179192

193+
static inline void write_pmicfiltr(u64 val) {}
194+
195+
static inline u64 read_pmicfiltr(void)
196+
{
197+
return 0;
198+
}
199+
180200
static inline void write_pmovsclr(u32 val)
181201
{
182202
write_sysreg(val, PMOVSR);

arch/arm64/include/asm/arm_pmuv3.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ static inline u32 read_pmuver(void)
5454
ID_AA64DFR0_EL1_PMUVer_SHIFT);
5555
}
5656

57+
static inline bool pmuv3_has_icntr(void)
58+
{
59+
u64 dfr1 = read_sysreg(id_aa64dfr1_el1);
60+
61+
return !!cpuid_feature_extract_unsigned_field(dfr1,
62+
ID_AA64DFR1_EL1_PMICNTR_SHIFT);
63+
}
64+
5765
static inline void write_pmcr(u64 val)
5866
{
5967
write_sysreg(val, pmcr_el0);
@@ -79,6 +87,16 @@ static inline u64 read_pmccntr(void)
7987
return read_sysreg(pmccntr_el0);
8088
}
8189

90+
static inline void write_pmicntr(u64 val)
91+
{
92+
write_sysreg_s(val, SYS_PMICNTR_EL0);
93+
}
94+
95+
static inline u64 read_pmicntr(void)
96+
{
97+
return read_sysreg_s(SYS_PMICNTR_EL0);
98+
}
99+
82100
static inline void write_pmcntenset(u64 val)
83101
{
84102
write_sysreg(val, pmcntenset_el0);
@@ -109,6 +127,16 @@ static inline u64 read_pmccfiltr(void)
109127
return read_sysreg(pmccfiltr_el0);
110128
}
111129

130+
static inline void write_pmicfiltr(u64 val)
131+
{
132+
write_sysreg_s(val, SYS_PMICFILTR_EL0);
133+
}
134+
135+
static inline u64 read_pmicfiltr(void)
136+
{
137+
return read_sysreg_s(SYS_PMICFILTR_EL0);
138+
}
139+
112140
static inline void write_pmovsclr(u64 val)
113141
{
114142
write_sysreg(val, pmovsclr_el0);

arch/arm64/kvm/pmu.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,28 @@ void kvm_clr_pmu_events(u64 clr)
6666

6767
/*
6868
* Read a value direct from PMEVTYPER<idx> where idx is 0-30
69-
* or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
69+
* or PMxCFILTR_EL0 where idx is 31-32.
7070
*/
7171
static u64 kvm_vcpu_pmu_read_evtype_direct(int idx)
7272
{
7373
if (idx == ARMV8_PMU_CYCLE_IDX)
7474
return read_pmccfiltr();
75+
else if (idx == ARMV8_PMU_INSTR_IDX)
76+
return read_pmicfiltr();
7577

7678
return read_pmevtypern(idx);
7779
}
7880

7981
/*
8082
* Write a value direct to PMEVTYPER<idx> where idx is 0-30
81-
* or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31).
83+
* or PMxCFILTR_EL0 where idx is 31-32.
8284
*/
8385
static void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val)
8486
{
8587
if (idx == ARMV8_PMU_CYCLE_IDX)
8688
write_pmccfiltr(val);
89+
else if (idx == ARMV8_PMU_INSTR_IDX)
90+
write_pmicfiltr(val);
8791
else
8892
write_pmevtypern(idx, val);
8993
}

arch/arm64/tools/sysreg

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,6 +2029,31 @@ Sysreg FAR_EL1 3 0 6 0 0
20292029
Field 63:0 ADDR
20302030
EndSysreg
20312031

2032+
Sysreg PMICNTR_EL0 3 3 9 4 0
2033+
Field 63:0 ICNT
2034+
EndSysreg
2035+
2036+
Sysreg PMICFILTR_EL0 3 3 9 6 0
2037+
Res0 63:59
2038+
Field 58 SYNC
2039+
Field 57:56 VS
2040+
Res0 55:32
2041+
Field 31 P
2042+
Field 30 U
2043+
Field 29 NSK
2044+
Field 28 NSU
2045+
Field 27 NSH
2046+
Field 26 M
2047+
Res0 25
2048+
Field 24 SH
2049+
Field 23 T
2050+
Field 22 RLK
2051+
Field 21 RLU
2052+
Field 20 RLH
2053+
Res0 19:16
2054+
Field 15:0 evtCount
2055+
EndSysreg
2056+
20322057
Sysreg PMSCR_EL1 3 0 9 9 0
20332058
Res0 63:8
20342059
Field 7:6 PCT

drivers/perf/arm_pmuv3.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ static u64 armv8pmu_read_counter(struct perf_event *event)
571571

572572
if (idx == ARMV8_PMU_CYCLE_IDX)
573573
value = read_pmccntr();
574+
else if (idx == ARMV8_PMU_INSTR_IDX)
575+
value = read_pmicntr();
574576
else
575577
value = armv8pmu_read_hw_counter(event);
576578

@@ -604,6 +606,8 @@ static void armv8pmu_write_counter(struct perf_event *event, u64 value)
604606

605607
if (idx == ARMV8_PMU_CYCLE_IDX)
606608
write_pmccntr(value);
609+
else if (idx == ARMV8_PMU_INSTR_IDX)
610+
write_pmicntr(value);
607611
else
608612
armv8pmu_write_hw_counter(event, value);
609613
}
@@ -641,6 +645,8 @@ static void armv8pmu_write_event_type(struct perf_event *event)
641645
} else {
642646
if (idx == ARMV8_PMU_CYCLE_IDX)
643647
write_pmccfiltr(hwc->config_base);
648+
else if (idx == ARMV8_PMU_INSTR_IDX)
649+
write_pmicfiltr(hwc->config_base);
644650
else
645651
armv8pmu_write_evtype(idx, hwc->config_base);
646652
}
@@ -769,6 +775,8 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
769775
ARMPMU_MAX_HWEVENTS) {
770776
if (i == ARMV8_PMU_CYCLE_IDX)
771777
write_pmccntr(0);
778+
else if (i == ARMV8_PMU_INSTR_IDX)
779+
write_pmicntr(0);
772780
else
773781
armv8pmu_write_evcntr(i, 0);
774782
}
@@ -936,6 +944,19 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
936944
return -EAGAIN;
937945
}
938946

947+
/*
948+
* Always prefer to place a instruction counter into the instruction counter,
949+
* but don't expose the instruction counter to userspace access as userspace
950+
* may not know how to handle it.
951+
*/
952+
if ((evtype == ARMV8_PMUV3_PERFCTR_INST_RETIRED) &&
953+
!armv8pmu_event_get_threshold(&event->attr) &&
954+
test_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask) &&
955+
!armv8pmu_event_want_user_access(event)) {
956+
if (!test_and_set_bit(ARMV8_PMU_INSTR_IDX, cpuc->used_mask))
957+
return ARMV8_PMU_INSTR_IDX;
958+
}
959+
939960
/*
940961
* Otherwise use events counters
941962
*/
@@ -1193,6 +1214,10 @@ static void __armv8pmu_probe_pmu(void *info)
11931214
/* Add the CPU cycles counter */
11941215
set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask);
11951216

1217+
/* Add the CPU instructions counter */
1218+
if (pmuv3_has_icntr())
1219+
set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask);
1220+
11961221
pmceid[0] = pmceid_raw[0] = read_pmceid0();
11971222
pmceid[1] = pmceid_raw[1] = read_pmceid1();
11981223

include/linux/perf/arm_pmu.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@
1717
#ifdef CONFIG_ARM_PMU
1818

1919
/*
20-
* The ARMv7 CPU PMU supports up to 32 event counters.
20+
* The Armv7 and Armv8.8 or less CPU PMU supports up to 32 event counters.
21+
* The Armv8.9/9.4 CPU PMU supports up to 33 event counters.
2122
*/
23+
#ifdef CONFIG_ARM
2224
#define ARMPMU_MAX_HWEVENTS 32
23-
25+
#else
26+
#define ARMPMU_MAX_HWEVENTS 33
27+
#endif
2428
/*
2529
* ARM PMU hw_event flags
2630
*/

include/linux/perf/arm_pmuv3.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
#define ARMV8_PMU_MAX_GENERAL_COUNTERS 31
1010
#define ARMV8_PMU_CYCLE_IDX 31
11-
11+
#define ARMV8_PMU_INSTR_IDX 32 /* Not accessible from AArch32 */
1212

1313
/*
1414
* Common architectural and microarchitectural event numbers.
@@ -228,8 +228,10 @@
228228
*/
229229
#define ARMV8_PMU_OVSR_P GENMASK(30, 0)
230230
#define ARMV8_PMU_OVSR_C BIT(31)
231+
#define ARMV8_PMU_OVSR_F BIT_ULL(32) /* arm64 only */
231232
/* Mask for writable bits is both P and C fields */
232-
#define ARMV8_PMU_OVERFLOWED_MASK (ARMV8_PMU_OVSR_P | ARMV8_PMU_OVSR_C)
233+
#define ARMV8_PMU_OVERFLOWED_MASK (ARMV8_PMU_OVSR_P | ARMV8_PMU_OVSR_C | \
234+
ARMV8_PMU_OVSR_F)
233235

234236
/*
235237
* PMXEVTYPER: Event selection reg

0 commit comments

Comments
 (0)