Skip to content

Commit c9574fe

Browse files
Robert RichterIngo Molnar
authored andcommitted
perf/x86-ibs: Implement workaround for IBS erratum torvalds#420
When disabling ibs there might be the case where hardware continuously generates interrupts. This is described in erratum torvalds#420 (Instruction- Based Sampling Engine May Generate Interrupt that Cannot Be Cleared). To avoid this we must clear the counter mask first and then clear the enable bit. This patch implements this. See Revision Guide for AMD Family 10h Processors, Publication #41322. Note: We now keep track of the last read ibs config value which is then used to disable ibs. To update the config value we pass now a pointer to the functions reading it. Signed-off-by: Robert Richter <[email protected]> Signed-off-by: Peter Zijlstra <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 7caaf4d commit c9574fe

File tree

1 file changed

+39
-23
lines changed

1 file changed

+39
-23
lines changed

arch/x86/kernel/cpu/perf_event_amd_ibs.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -291,20 +291,36 @@ static u64 get_ibs_op_count(u64 config)
291291

292292
static void
293293
perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event,
294-
u64 config)
294+
u64 *config)
295295
{
296-
u64 count = perf_ibs->get_count(config);
296+
u64 count = perf_ibs->get_count(*config);
297297

298298
while (!perf_event_try_update(event, count, 20)) {
299-
rdmsrl(event->hw.config_base, config);
300-
count = perf_ibs->get_count(config);
299+
rdmsrl(event->hw.config_base, *config);
300+
count = perf_ibs->get_count(*config);
301301
}
302302
}
303303

304-
/* Note: The enable mask must be encoded in the config argument. */
305-
static inline void perf_ibs_enable_event(struct hw_perf_event *hwc, u64 config)
304+
static inline void perf_ibs_enable_event(struct perf_ibs *perf_ibs,
305+
struct hw_perf_event *hwc, u64 config)
306306
{
307-
wrmsrl(hwc->config_base, hwc->config | config);
307+
wrmsrl(hwc->config_base, hwc->config | config | perf_ibs->enable_mask);
308+
}
309+
310+
/*
311+
* Erratum #420 Instruction-Based Sampling Engine May Generate
312+
* Interrupt that Cannot Be Cleared:
313+
*
314+
* Must clear counter mask first, then clear the enable bit. See
315+
* Revision Guide for AMD Family 10h Processors, Publication #41322.
316+
*/
317+
static inline void perf_ibs_disable_event(struct perf_ibs *perf_ibs,
318+
struct hw_perf_event *hwc, u64 config)
319+
{
320+
config &= ~perf_ibs->cnt_mask;
321+
wrmsrl(hwc->config_base, config);
322+
config &= ~perf_ibs->enable_mask;
323+
wrmsrl(hwc->config_base, config);
308324
}
309325

310326
/*
@@ -318,18 +334,17 @@ static void perf_ibs_start(struct perf_event *event, int flags)
318334
struct hw_perf_event *hwc = &event->hw;
319335
struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
320336
struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
321-
u64 config;
337+
u64 period;
322338

323339
if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
324340
return;
325341

326342
WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
327343
hwc->state = 0;
328344

329-
perf_ibs_set_period(perf_ibs, hwc, &config);
330-
config = (config >> 4) | perf_ibs->enable_mask;
345+
perf_ibs_set_period(perf_ibs, hwc, &period);
331346
set_bit(IBS_STARTED, pcpu->state);
332-
perf_ibs_enable_event(hwc, config);
347+
perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
333348

334349
perf_event_update_userpage(event);
335350
}
@@ -339,28 +354,27 @@ static void perf_ibs_stop(struct perf_event *event, int flags)
339354
struct hw_perf_event *hwc = &event->hw;
340355
struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
341356
struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
342-
u64 val;
357+
u64 config;
343358
int stopping;
344359

345360
stopping = test_and_clear_bit(IBS_STARTED, pcpu->state);
346361

347362
if (!stopping && (hwc->state & PERF_HES_UPTODATE))
348363
return;
349364

350-
rdmsrl(hwc->config_base, val);
365+
rdmsrl(hwc->config_base, config);
351366

352367
if (stopping) {
353368
set_bit(IBS_STOPPING, pcpu->state);
354-
val &= ~perf_ibs->enable_mask;
355-
wrmsrl(hwc->config_base, val);
369+
perf_ibs_disable_event(perf_ibs, hwc, config);
356370
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
357371
hwc->state |= PERF_HES_STOPPED;
358372
}
359373

360374
if (hwc->state & PERF_HES_UPTODATE)
361375
return;
362376

363-
perf_ibs_event_update(perf_ibs, event, val);
377+
perf_ibs_event_update(perf_ibs, event, &config);
364378
hwc->state |= PERF_HES_UPTODATE;
365379
}
366380

@@ -456,7 +470,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
456470
struct perf_ibs_data ibs_data;
457471
int offset, size, check_rip, offset_max, throttle = 0;
458472
unsigned int msr;
459-
u64 *buf, config;
473+
u64 *buf, *config, period;
460474

461475
if (!test_bit(IBS_STARTED, pcpu->state)) {
462476
/* Catch spurious interrupts after stopping IBS: */
@@ -477,15 +491,15 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
477491
* supported in all cpus. As this triggered an interrupt, we
478492
* set the current count to the max count.
479493
*/
480-
config = ibs_data.regs[0];
494+
config = &ibs_data.regs[0];
481495
if (perf_ibs == &perf_ibs_op && !(ibs_caps & IBS_CAPS_RDWROPCNT)) {
482-
config &= ~IBS_OP_CUR_CNT;
483-
config |= (config & IBS_OP_MAX_CNT) << 36;
496+
*config &= ~IBS_OP_CUR_CNT;
497+
*config |= (*config & IBS_OP_MAX_CNT) << 36;
484498
}
485499

486500
perf_ibs_event_update(perf_ibs, event, config);
487501
perf_sample_data_init(&data, 0, hwc->last_period);
488-
if (!perf_ibs_set_period(perf_ibs, hwc, &config))
502+
if (!perf_ibs_set_period(perf_ibs, hwc, &period))
489503
goto out; /* no sw counter overflow */
490504

491505
ibs_data.caps = ibs_caps;
@@ -523,8 +537,10 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
523537

524538
throttle = perf_event_overflow(event, &data, &regs);
525539
out:
526-
config = (config >> 4) | (throttle ? 0 : perf_ibs->enable_mask);
527-
perf_ibs_enable_event(hwc, config);
540+
if (throttle)
541+
perf_ibs_disable_event(perf_ibs, hwc, *config);
542+
else
543+
perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
528544

529545
perf_event_update_userpage(event);
530546

0 commit comments

Comments
 (0)