Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 356387
b: refs/heads/master
c: bc09c21
h: refs/heads/master
i:
  356385: 31eb82c
  356383: 30e4430
v: v3
  • Loading branch information
Michael Neuling authored and Benjamin Herrenschmidt committed Jan 10, 2013
1 parent 400db54 commit 3785d21
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 30 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6a040ce72598159a74969a2d01ab0ba5ee6536b3
refs/heads/master: bc09c219b2e6f9436d06a1a3a10eff97faab371c
83 changes: 54 additions & 29 deletions trunk/arch/powerpc/perf/core-book3s.c
Original file line number Diff line number Diff line change
Expand Up @@ -1412,11 +1412,8 @@ unsigned long perf_instruction_pointer(struct pt_regs *regs)
return regs->nip;
}

static bool pmc_overflow(unsigned long val)
static bool pmc_overflow_power7(unsigned long val)
{
if ((int)val < 0)
return true;

/*
* Events on POWER7 can roll back if a speculative event doesn't
* eventually complete. Unfortunately in some rare cases they will
Expand All @@ -1428,7 +1425,15 @@ static bool pmc_overflow(unsigned long val)
* PMCs because a user might set a period of less than 256 and we
* don't want to mistakenly reset them.
*/
if (pvr_version_is(PVR_POWER7) && ((0x80000000 - val) <= 256))
if ((0x80000000 - val) <= 256)
return true;

return false;
}

static bool pmc_overflow(unsigned long val)
{
if ((int)val < 0)
return true;

return false;
Expand All @@ -1439,11 +1444,11 @@ static bool pmc_overflow(unsigned long val)
*/
static void perf_event_interrupt(struct pt_regs *regs)
{
int i;
int i, j;
struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
struct perf_event *event;
unsigned long val;
int found = 0;
unsigned long val[8];
int found, active;
int nmi;

if (cpuhw->n_limited)
Expand All @@ -1458,33 +1463,53 @@ static void perf_event_interrupt(struct pt_regs *regs)
else
irq_enter();

for (i = 0; i < cpuhw->n_events; ++i) {
event = cpuhw->event[i];
if (!event->hw.idx || is_limited_pmc(event->hw.idx))
/* Read all the PMCs since we'll need them a bunch of times */
for (i = 0; i < ppmu->n_counter; ++i)
val[i] = read_pmc(i + 1);

/* Try to find what caused the IRQ */
found = 0;
for (i = 0; i < ppmu->n_counter; ++i) {
if (!pmc_overflow(val[i]))
continue;
val = read_pmc(event->hw.idx);
if ((int)val < 0) {
/* event has overflowed */
found = 1;
record_and_restart(event, val, regs);
if (is_limited_pmc(i + 1))
continue; /* these won't generate IRQs */
/*
* We've found one that's overflowed. For active
* counters we need to log this. For inactive
* counters, we need to reset it anyway
*/
found = 1;
active = 0;
for (j = 0; j < cpuhw->n_events; ++j) {
event = cpuhw->event[j];
if (event->hw.idx == (i + 1)) {
active = 1;
record_and_restart(event, val[i], regs);
break;
}
}
if (!active)
/* reset non active counters that have overflowed */
write_pmc(i + 1, 0);
}

/*
* In case we didn't find and reset the event that caused
* the interrupt, scan all events and reset any that are
* negative, to avoid getting continual interrupts.
* Any that we processed in the previous loop will not be negative.
*/
if (!found) {
for (i = 0; i < ppmu->n_counter; ++i) {
if (is_limited_pmc(i + 1))
if (!found && pvr_version_is(PVR_POWER7)) {
/* check active counters for special buggy p7 overflow */
for (i = 0; i < cpuhw->n_events; ++i) {
event = cpuhw->event[i];
if (!event->hw.idx || is_limited_pmc(event->hw.idx))
continue;
val = read_pmc(i + 1);
if (pmc_overflow(val))
write_pmc(i + 1, 0);
if (pmc_overflow_power7(val[event->hw.idx - 1])) {
/* event has overflowed in a buggy way*/
found = 1;
record_and_restart(event,
val[event->hw.idx - 1],
regs);
}
}
}
if ((!found) && printk_ratelimit())
printk(KERN_WARNING "Can't find PMC that caused IRQ\n");

/*
* Reset MMCR0 to its normal value. This will set PMXE and
Expand Down

0 comments on commit 3785d21

Please sign in to comment.