Commit 91f3469a authored by Athira Rajeev's avatar Athira Rajeev Committed by Michael Ellerman

powerpc/perf: Include PMCs as part of per-cpu cpuhw_events struct

To support capturing of PMC's as part of extended registers, the
value of SPR's PMC1 to PMC6 has to be saved in the starting of PMI
interrupt handler. This is needed since we are resetting the
overflown PMC before creating sample and hence directly reading
SPRN_PMCx in 'perf_reg_value' will be capturing the modified value.

To solve this, add a per-cpu array as part of structure cpu_hw_events
and use this array to capture PMC values in the perf interrupt handler.
Patch also re-factor's the interrupt handler code to use this per-cpu
array instead of current local array.
Signed-off-by: default avatarAthira Rajeev <atrajeev@linux.vnet.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/1612335337-1888-2-git-send-email-atrajeev@linux.vnet.ibm.com
parent 266d8f75
...@@ -54,6 +54,9 @@ struct cpu_hw_events { ...@@ -54,6 +54,9 @@ struct cpu_hw_events {
struct perf_branch_stack bhrb_stack; struct perf_branch_stack bhrb_stack;
struct perf_branch_entry bhrb_entries[BHRB_MAX_ENTRIES]; struct perf_branch_entry bhrb_entries[BHRB_MAX_ENTRIES];
u64 ic_init; u64 ic_init;
/* Store the PMC values */
unsigned long pmcs[MAX_HWEVENTS];
}; };
static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
...@@ -2264,7 +2267,6 @@ static void __perf_event_interrupt(struct pt_regs *regs) ...@@ -2264,7 +2267,6 @@ static void __perf_event_interrupt(struct pt_regs *regs)
int i, j; int i, j;
struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events); struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
struct perf_event *event; struct perf_event *event;
unsigned long val[8];
int found, active; int found, active;
if (cpuhw->n_limited) if (cpuhw->n_limited)
...@@ -2275,12 +2277,12 @@ static void __perf_event_interrupt(struct pt_regs *regs) ...@@ -2275,12 +2277,12 @@ static void __perf_event_interrupt(struct pt_regs *regs)
/* Read all the PMCs since we'll need them a bunch of times */ /* Read all the PMCs since we'll need them a bunch of times */
for (i = 0; i < ppmu->n_counter; ++i) for (i = 0; i < ppmu->n_counter; ++i)
val[i] = read_pmc(i + 1); cpuhw->pmcs[i] = read_pmc(i + 1);
/* Try to find what caused the IRQ */ /* Try to find what caused the IRQ */
found = 0; found = 0;
for (i = 0; i < ppmu->n_counter; ++i) { for (i = 0; i < ppmu->n_counter; ++i) {
if (!pmc_overflow(val[i])) if (!pmc_overflow(cpuhw->pmcs[i]))
continue; continue;
if (is_limited_pmc(i + 1)) if (is_limited_pmc(i + 1))
continue; /* these won't generate IRQs */ continue; /* these won't generate IRQs */
...@@ -2295,7 +2297,7 @@ static void __perf_event_interrupt(struct pt_regs *regs) ...@@ -2295,7 +2297,7 @@ static void __perf_event_interrupt(struct pt_regs *regs)
event = cpuhw->event[j]; event = cpuhw->event[j];
if (event->hw.idx == (i + 1)) { if (event->hw.idx == (i + 1)) {
active = 1; active = 1;
record_and_restart(event, val[i], regs); record_and_restart(event, cpuhw->pmcs[i], regs);
break; break;
} }
} }
...@@ -2309,11 +2311,11 @@ static void __perf_event_interrupt(struct pt_regs *regs) ...@@ -2309,11 +2311,11 @@ static void __perf_event_interrupt(struct pt_regs *regs)
event = cpuhw->event[i]; event = cpuhw->event[i];
if (!event->hw.idx || is_limited_pmc(event->hw.idx)) if (!event->hw.idx || is_limited_pmc(event->hw.idx))
continue; continue;
if (pmc_overflow_power7(val[event->hw.idx - 1])) { if (pmc_overflow_power7(cpuhw->pmcs[event->hw.idx - 1])) {
/* event has overflowed in a buggy way*/ /* event has overflowed in a buggy way*/
found = 1; found = 1;
record_and_restart(event, record_and_restart(event,
val[event->hw.idx - 1], cpuhw->pmcs[event->hw.idx - 1],
regs); regs);
} }
} }
...@@ -2329,6 +2331,10 @@ static void __perf_event_interrupt(struct pt_regs *regs) ...@@ -2329,6 +2331,10 @@ static void __perf_event_interrupt(struct pt_regs *regs)
* we get back out of this interrupt. * we get back out of this interrupt.
*/ */
write_mmcr0(cpuhw, cpuhw->mmcr.mmcr0); write_mmcr0(cpuhw, cpuhw->mmcr.mmcr0);
/* Clear the cpuhw->pmcs */
memset(&cpuhw->pmcs, 0, sizeof(cpuhw->pmcs));
} }
static void perf_event_interrupt(struct pt_regs *regs) static void perf_event_interrupt(struct pt_regs *regs)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment