Skip to content

Commit 8673e02

Browse files
Andrew Murraywilldeacon
authored andcommitted
arm64: perf: Add support for ARMv8.5-PMU 64-bit counters
At present ARMv8 event counters are limited to 32-bits, though by using the CHAIN event it's possible to combine adjacent counters to achieve 64-bits. The perf config1:0 bit can be set to use such a configuration. With the introduction of ARMv8.5-PMU support, all event counters can now be used as 64-bit counters. Let's enable 64-bit event counters where support exists. Unless the user sets config1:0 we will adjust the counter value such that it overflows upon 32-bit overflow. This follows the same behaviour as the cycle counter which has always been (and remains) 64-bits. Signed-off-by: Andrew Murray <[email protected]> Reviewed-by: Suzuki K Poulose <[email protected]> [Mark: fix ID field names, compare with 8.5 value] Signed-off-by: Mark Rutland <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent c854188 commit 8673e02

File tree

4 files changed

+78
-17
lines changed

4 files changed

+78
-17
lines changed

arch/arm64/include/asm/perf_event.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,10 @@
176176
#define ARMV8_PMU_PMCR_X (1 << 4) /* Export to ETM */
177177
#define ARMV8_PMU_PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
178178
#define ARMV8_PMU_PMCR_LC (1 << 6) /* Overflow on 64 bit cycle counter */
179+
#define ARMV8_PMU_PMCR_LP (1 << 7) /* Long event counter enable */
179180
#define ARMV8_PMU_PMCR_N_SHIFT 11 /* Number of counters supported */
180181
#define ARMV8_PMU_PMCR_N_MASK 0x1f
181-
#define ARMV8_PMU_PMCR_MASK 0x7f /* Mask for writable bits */
182+
#define ARMV8_PMU_PMCR_MASK 0xff /* Mask for writable bits */
182183

183184
/*
184185
* PMOVSR: counters overflow flag status reg

arch/arm64/include/asm/sysreg.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,11 @@
702702
#define ID_AA64DFR0_TRACEVER_SHIFT 4
703703
#define ID_AA64DFR0_DEBUGVER_SHIFT 0
704704

705+
#define ID_AA64DFR0_PMUVER_8_0 0x1
705706
#define ID_AA64DFR0_PMUVER_8_1 0x4
707+
#define ID_AA64DFR0_PMUVER_8_4 0x5
708+
#define ID_AA64DFR0_PMUVER_8_5 0x6
709+
#define ID_AA64DFR0_PMUVER_IMP_DEF 0xf
706710

707711
#define ID_DFR0_PERFMON_SHIFT 24
708712

arch/arm64/kernel/perf_event.c

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ static struct attribute_group armv8_pmuv3_format_attr_group = {
285285
#define ARMV8_IDX_COUNTER_LAST(cpu_pmu) \
286286
(ARMV8_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1)
287287

288+
289+
/*
290+
* We unconditionally enable ARMv8.5-PMU long event counter support
291+
* (64-bit events) where supported. Indicate if this arm_pmu has long
292+
* event counter support.
293+
*/
294+
static bool armv8pmu_has_long_event(struct arm_pmu *cpu_pmu)
295+
{
296+
return (cpu_pmu->pmuver >= ID_AA64DFR0_PMUVER_8_5);
297+
}
298+
288299
/*
289300
* We must chain two programmable counters for 64 bit events,
290301
* except when we have allocated the 64bit cycle counter (for CPU
@@ -294,9 +305,11 @@ static struct attribute_group armv8_pmuv3_format_attr_group = {
294305
static inline bool armv8pmu_event_is_chained(struct perf_event *event)
295306
{
296307
int idx = event->hw.idx;
308+
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
297309

298310
return !WARN_ON(idx < 0) &&
299311
armv8pmu_event_is_64bit(event) &&
312+
!armv8pmu_has_long_event(cpu_pmu) &&
300313
(idx != ARMV8_IDX_CYCLE_COUNTER);
301314
}
302315

@@ -345,7 +358,7 @@ static inline void armv8pmu_select_counter(int idx)
345358
isb();
346359
}
347360

348-
static inline u32 armv8pmu_read_evcntr(int idx)
361+
static inline u64 armv8pmu_read_evcntr(int idx)
349362
{
350363
armv8pmu_select_counter(idx);
351364
return read_sysreg(pmxevcntr_el0);
@@ -362,6 +375,44 @@ static inline u64 armv8pmu_read_hw_counter(struct perf_event *event)
362375
return val;
363376
}
364377

378+
/*
379+
* The cycle counter is always a 64-bit counter. When ARMV8_PMU_PMCR_LP
380+
* is set the event counters also become 64-bit counters. Unless the
381+
* user has requested a long counter (attr.config1) then we want to
382+
* interrupt upon 32-bit overflow - we achieve this by applying a bias.
383+
*/
384+
static bool armv8pmu_event_needs_bias(struct perf_event *event)
385+
{
386+
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
387+
struct hw_perf_event *hwc = &event->hw;
388+
int idx = hwc->idx;
389+
390+
if (armv8pmu_event_is_64bit(event))
391+
return false;
392+
393+
if (armv8pmu_has_long_event(cpu_pmu) ||
394+
idx == ARMV8_IDX_CYCLE_COUNTER)
395+
return true;
396+
397+
return false;
398+
}
399+
400+
static u64 armv8pmu_bias_long_counter(struct perf_event *event, u64 value)
401+
{
402+
if (armv8pmu_event_needs_bias(event))
403+
value |= GENMASK(63, 32);
404+
405+
return value;
406+
}
407+
408+
static u64 armv8pmu_unbias_long_counter(struct perf_event *event, u64 value)
409+
{
410+
if (armv8pmu_event_needs_bias(event))
411+
value &= ~GENMASK(63, 32);
412+
413+
return value;
414+
}
415+
365416
static u64 armv8pmu_read_counter(struct perf_event *event)
366417
{
367418
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
@@ -377,10 +428,10 @@ static u64 armv8pmu_read_counter(struct perf_event *event)
377428
else
378429
value = armv8pmu_read_hw_counter(event);
379430

380-
return value;
431+
return armv8pmu_unbias_long_counter(event, value);
381432
}
382433

383-
static inline void armv8pmu_write_evcntr(int idx, u32 value)
434+
static inline void armv8pmu_write_evcntr(int idx, u64 value)
384435
{
385436
armv8pmu_select_counter(idx);
386437
write_sysreg(value, pmxevcntr_el0);
@@ -405,20 +456,14 @@ static void armv8pmu_write_counter(struct perf_event *event, u64 value)
405456
struct hw_perf_event *hwc = &event->hw;
406457
int idx = hwc->idx;
407458

459+
value = armv8pmu_bias_long_counter(event, value);
460+
408461
if (!armv8pmu_counter_valid(cpu_pmu, idx))
409462
pr_err("CPU%u writing wrong counter %d\n",
410463
smp_processor_id(), idx);
411-
else if (idx == ARMV8_IDX_CYCLE_COUNTER) {
412-
/*
413-
* The cycles counter is really a 64-bit counter.
414-
* When treating it as a 32-bit counter, we only count
415-
* the lower 32 bits, and set the upper 32-bits so that
416-
* we get an interrupt upon 32-bit overflow.
417-
*/
418-
if (!armv8pmu_event_is_64bit(event))
419-
value |= 0xffffffff00000000ULL;
464+
else if (idx == ARMV8_IDX_CYCLE_COUNTER)
420465
write_sysreg(value, pmccntr_el0);
421-
} else
466+
else
422467
armv8pmu_write_hw_counter(event, value);
423468
}
424469

@@ -731,7 +776,8 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
731776
/*
732777
* Otherwise use events counters
733778
*/
734-
if (armv8pmu_event_is_64bit(event))
779+
if (armv8pmu_event_is_64bit(event) &&
780+
!armv8pmu_has_long_event(cpu_pmu))
735781
return armv8pmu_get_chain_idx(cpuc, cpu_pmu);
736782
else
737783
return armv8pmu_get_single_idx(cpuc, cpu_pmu);
@@ -802,6 +848,9 @@ static int armv8pmu_filter_match(struct perf_event *event)
802848

803849
static void armv8pmu_reset(void *info)
804850
{
851+
struct arm_pmu *cpu_pmu = (struct arm_pmu *)info;
852+
u32 pmcr;
853+
805854
/* The counter and interrupt enable registers are unknown at reset. */
806855
armv8pmu_disable_counter(U32_MAX);
807856
armv8pmu_disable_intens(U32_MAX);
@@ -813,8 +862,13 @@ static void armv8pmu_reset(void *info)
813862
* Initialize & Reset PMNC. Request overflow interrupt for
814863
* 64 bit cycle counter but cheat in armv8pmu_write_counter().
815864
*/
816-
armv8pmu_pmcr_write(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C |
817-
ARMV8_PMU_PMCR_LC);
865+
pmcr = ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C | ARMV8_PMU_PMCR_LC;
866+
867+
/* Enable long event counter support where available */
868+
if (armv8pmu_has_long_event(cpu_pmu))
869+
pmcr |= ARMV8_PMU_PMCR_LP;
870+
871+
armv8pmu_pmcr_write(pmcr);
818872
}
819873

820874
static int __armv8_pmuv3_map_event(struct perf_event *event,
@@ -897,6 +951,7 @@ static void __armv8pmu_probe_pmu(void *info)
897951
if (pmuver == 0xf || pmuver == 0)
898952
return;
899953

954+
cpu_pmu->pmuver = pmuver;
900955
probe->present = true;
901956

902957
/* Read the nb of CNTx counters supported from PMNC */

include/linux/perf/arm_pmu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ struct arm_pmu {
8080
struct pmu pmu;
8181
cpumask_t supported_cpus;
8282
char *name;
83+
int pmuver;
8384
irqreturn_t (*handle_irq)(struct arm_pmu *pmu);
8485
void (*enable)(struct perf_event *event);
8586
void (*disable)(struct perf_event *event);

0 commit comments

Comments
 (0)