Skip to content

Commit 7685665

Browse files
sandip4nPeter Zijlstra
authored andcommitted
perf/x86/amd/core: Add PerfMonV2 overflow handling
If AMD Performance Monitoring Version 2 (PerfMonV2) is supported, use a new scheme to process Core PMC overflows in the NMI handler using the new global control and status registers. This will be bypassed on unsupported hardware (x86_pmu.version < 2). In x86_pmu_handle_irq(), overflows are detected by testing the contents of the PERF_CTR register for each active PMC in a loop. The new scheme instead inspects the overflow bits of the global status register. The Performance Counter Global Status (PerfCntrGlobalStatus) register has overflow (PerfCntrOvfl) bits for each PMC. This is, however, a read-only MSR. To acknowledge that overflows have been processed, the NMI handler must clear the bits by writing to the PerfCntrGlobalStatusClr register. In x86_pmu_handle_irq(), PMCs counting the same event that are started and stopped at the same time record slightly different counts due to delays in between reads from the PERF_CTR registers. This is fixed by stopping and starting the PMCs at the same before and with a single write to the Performance Counter Global Control (PerfCntrGlobalCtl) upon entering and before exiting the NMI handler. Signed-off-by: Sandipan Das <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/f20b7e4da0b0a83bdbe05857f354146623bc63ab.1650515382.git.sandipan.das@amd.com
1 parent 9622e67 commit 7685665

File tree

1 file changed

+133
-11
lines changed

1 file changed

+133
-11
lines changed

arch/x86/events/amd/core.c

Lines changed: 133 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/delay.h>
99
#include <linux/jiffies.h>
1010
#include <asm/apicdef.h>
11+
#include <asm/apic.h>
1112
#include <asm/nmi.h>
1213

1314
#include "../perf_event.h"
@@ -669,6 +670,45 @@ static inline void amd_pmu_set_global_ctl(u64 ctl)
669670
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, ctl);
670671
}
671672

673+
static inline u64 amd_pmu_get_global_status(void)
674+
{
675+
u64 status;
676+
677+
/* PerfCntrGlobalStatus is read-only */
678+
rdmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, status);
679+
680+
return status & amd_pmu_global_cntr_mask;
681+
}
682+
683+
static inline void amd_pmu_ack_global_status(u64 status)
684+
{
685+
/*
686+
* PerfCntrGlobalStatus is read-only but an overflow acknowledgment
687+
* mechanism exists; writing 1 to a bit in PerfCntrGlobalStatusClr
688+
* clears the same bit in PerfCntrGlobalStatus
689+
*/
690+
691+
/* Only allow modifications to PerfCntrGlobalStatus.PerfCntrOvfl */
692+
status &= amd_pmu_global_cntr_mask;
693+
wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, status);
694+
}
695+
696+
static bool amd_pmu_test_overflow_topbit(int idx)
697+
{
698+
u64 counter;
699+
700+
rdmsrl(x86_pmu_event_addr(idx), counter);
701+
702+
return !(counter & BIT_ULL(x86_pmu.cntval_bits - 1));
703+
}
704+
705+
static bool amd_pmu_test_overflow_status(int idx)
706+
{
707+
return amd_pmu_get_global_status() & BIT_ULL(idx);
708+
}
709+
710+
DEFINE_STATIC_CALL(amd_pmu_test_overflow, amd_pmu_test_overflow_topbit);
711+
672712
/*
673713
* When a PMC counter overflows, an NMI is used to process the event and
674714
* reset the counter. NMI latency can result in the counter being updated
@@ -681,16 +721,14 @@ static inline void amd_pmu_set_global_ctl(u64 ctl)
681721
static void amd_pmu_wait_on_overflow(int idx)
682722
{
683723
unsigned int i;
684-
u64 counter;
685724

686725
/*
687726
* Wait for the counter to be reset if it has overflowed. This loop
688727
* should exit very, very quickly, but just in case, don't wait
689728
* forever...
690729
*/
691730
for (i = 0; i < OVERFLOW_WAIT_COUNT; i++) {
692-
rdmsrl(x86_pmu_event_addr(idx), counter);
693-
if (counter & (1ULL << (x86_pmu.cntval_bits - 1)))
731+
if (!static_call(amd_pmu_test_overflow)(idx))
694732
break;
695733

696734
/* Might be in IRQ context, so can't sleep */
@@ -830,6 +868,24 @@ static void amd_pmu_del_event(struct perf_event *event)
830868
* handled a counter. When an un-handled NMI is received, it will be claimed
831869
* only if arriving within that window.
832870
*/
871+
static inline int amd_pmu_adjust_nmi_window(int handled)
872+
{
873+
/*
874+
* If a counter was handled, record a timestamp such that un-handled
875+
* NMIs will be claimed if arriving within that window.
876+
*/
877+
if (handled) {
878+
this_cpu_write(perf_nmi_tstamp, jiffies + perf_nmi_window);
879+
880+
return handled;
881+
}
882+
883+
if (time_after(jiffies, this_cpu_read(perf_nmi_tstamp)))
884+
return NMI_DONE;
885+
886+
return NMI_HANDLED;
887+
}
888+
833889
static int amd_pmu_handle_irq(struct pt_regs *regs)
834890
{
835891
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
@@ -857,20 +913,84 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
857913
if (pmu_enabled)
858914
amd_pmu_enable_all(0);
859915

916+
return amd_pmu_adjust_nmi_window(handled);
917+
}
918+
919+
static int amd_pmu_v2_handle_irq(struct pt_regs *regs)
920+
{
921+
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
922+
struct perf_sample_data data;
923+
struct hw_perf_event *hwc;
924+
struct perf_event *event;
925+
int handled = 0, idx;
926+
u64 status, mask;
927+
bool pmu_enabled;
928+
860929
/*
861-
* If a counter was handled, record a timestamp such that un-handled
862-
* NMIs will be claimed if arriving within that window.
930+
* Save the PMU state as it needs to be restored when leaving the
931+
* handler
863932
*/
864-
if (handled) {
865-
this_cpu_write(perf_nmi_tstamp, jiffies + perf_nmi_window);
933+
pmu_enabled = cpuc->enabled;
934+
cpuc->enabled = 0;
866935

867-
return handled;
936+
/* Stop counting */
937+
amd_pmu_v2_disable_all();
938+
939+
status = amd_pmu_get_global_status();
940+
941+
/* Check if any overflows are pending */
942+
if (!status)
943+
goto done;
944+
945+
for (idx = 0; idx < x86_pmu.num_counters; idx++) {
946+
if (!test_bit(idx, cpuc->active_mask))
947+
continue;
948+
949+
event = cpuc->events[idx];
950+
hwc = &event->hw;
951+
x86_perf_event_update(event);
952+
mask = BIT_ULL(idx);
953+
954+
if (!(status & mask))
955+
continue;
956+
957+
/* Event overflow */
958+
handled++;
959+
perf_sample_data_init(&data, 0, hwc->last_period);
960+
961+
if (!x86_perf_event_set_period(event))
962+
continue;
963+
964+
if (perf_event_overflow(event, &data, regs))
965+
x86_pmu_stop(event, 0);
966+
967+
status &= ~mask;
868968
}
869969

870-
if (time_after(jiffies, this_cpu_read(perf_nmi_tstamp)))
871-
return NMI_DONE;
970+
/*
971+
* It should never be the case that some overflows are not handled as
972+
* the corresponding PMCs are expected to be inactive according to the
973+
* active_mask
974+
*/
975+
WARN_ON(status > 0);
872976

873-
return NMI_HANDLED;
977+
/* Clear overflow bits */
978+
amd_pmu_ack_global_status(~status);
979+
980+
/*
981+
* Unmasking the LVTPC is not required as the Mask (M) bit of the LVT
982+
* PMI entry is not set by the local APIC when a PMC overflow occurs
983+
*/
984+
inc_irq_stat(apic_perf_irqs);
985+
986+
done:
987+
cpuc->enabled = pmu_enabled;
988+
989+
/* Resume counting only if PMU is active */
990+
if (pmu_enabled)
991+
amd_pmu_v2_enable_all(0);
992+
993+
return amd_pmu_adjust_nmi_window(handled);
874994
}
875995

876996
static struct event_constraint *
@@ -1256,6 +1376,8 @@ static int __init amd_core_pmu_init(void)
12561376
x86_pmu.enable_all = amd_pmu_v2_enable_all;
12571377
x86_pmu.disable_all = amd_pmu_v2_disable_all;
12581378
x86_pmu.enable = amd_pmu_v2_enable_event;
1379+
x86_pmu.handle_irq = amd_pmu_v2_handle_irq;
1380+
static_call_update(amd_pmu_test_overflow, amd_pmu_test_overflow_status);
12591381
}
12601382

12611383
/*

0 commit comments

Comments
 (0)