aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2012-04-02 14:19:16 -0400
committerIngo Molnar <mingo@kernel.org>2012-05-09 09:23:16 -0400
commitc9574fe0bdb9ac9a2698e02a712088ce8431e9f8 (patch)
treeaaa5ad58269c78937ffac9ec3fc8ceffd4f55ff7 /arch
parent7caaf4d8241feecafb87919402b0a6dbb1b71d9e (diff)
perf/x86-ibs: Implement workaround for IBS erratum #420
When disabling ibs there might be the case where hardware continuously generates interrupts. This is described in erratum #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 <robert.richter@amd.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1333390758-10893-11-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c62
1 files changed, 39 insertions, 23 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index cb51a3e55870..b14e71127c82 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -291,20 +291,36 @@ static u64 get_ibs_op_count(u64 config)
291 291
292static void 292static void
293perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event, 293perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event,
294 u64 config) 294 u64 *config)
295{ 295{
296 u64 count = perf_ibs->get_count(config); 296 u64 count = perf_ibs->get_count(*config);
297 297
298 while (!perf_event_try_update(event, count, 20)) { 298 while (!perf_event_try_update(event, count, 20)) {
299 rdmsrl(event->hw.config_base, config); 299 rdmsrl(event->hw.config_base, *config);
300 count = perf_ibs->get_count(config); 300 count = perf_ibs->get_count(*config);
301 } 301 }
302} 302}
303 303
304/* Note: The enable mask must be encoded in the config argument. */ 304static inline void perf_ibs_enable_event(struct perf_ibs *perf_ibs,
305static inline void perf_ibs_enable_event(struct hw_perf_event *hwc, u64 config) 305 struct hw_perf_event *hwc, u64 config)
306{ 306{
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 */
317static 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);
308} 324}
309 325
310/* 326/*
@@ -318,7 +334,7 @@ static void perf_ibs_start(struct perf_event *event, int flags)
318 struct hw_perf_event *hwc = &event->hw; 334 struct hw_perf_event *hwc = &event->hw;
319 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); 335 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
320 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); 336 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
321 u64 config; 337 u64 period;
322 338
323 if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) 339 if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
324 return; 340 return;
@@ -326,10 +342,9 @@ static void perf_ibs_start(struct perf_event *event, int flags)
326 WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); 342 WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
327 hwc->state = 0; 343 hwc->state = 0;
328 344
329 perf_ibs_set_period(perf_ibs, hwc, &config); 345 perf_ibs_set_period(perf_ibs, hwc, &period);
330 config = (config >> 4) | perf_ibs->enable_mask;
331 set_bit(IBS_STARTED, pcpu->state); 346 set_bit(IBS_STARTED, pcpu->state);
332 perf_ibs_enable_event(hwc, config); 347 perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
333 348
334 perf_event_update_userpage(event); 349 perf_event_update_userpage(event);
335} 350}
@@ -339,7 +354,7 @@ static void perf_ibs_stop(struct perf_event *event, int flags)
339 struct hw_perf_event *hwc = &event->hw; 354 struct hw_perf_event *hwc = &event->hw;
340 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); 355 struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu);
341 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); 356 struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu);
342 u64 val; 357 u64 config;
343 int stopping; 358 int stopping;
344 359
345 stopping = test_and_clear_bit(IBS_STARTED, pcpu->state); 360 stopping = test_and_clear_bit(IBS_STARTED, pcpu->state);
@@ -347,12 +362,11 @@ static void perf_ibs_stop(struct perf_event *event, int flags)
347 if (!stopping && (hwc->state & PERF_HES_UPTODATE)) 362 if (!stopping && (hwc->state & PERF_HES_UPTODATE))
348 return; 363 return;
349 364
350 rdmsrl(hwc->config_base, val); 365 rdmsrl(hwc->config_base, config);
351 366
352 if (stopping) { 367 if (stopping) {
353 set_bit(IBS_STOPPING, pcpu->state); 368 set_bit(IBS_STOPPING, pcpu->state);
354 val &= ~perf_ibs->enable_mask; 369 perf_ibs_disable_event(perf_ibs, hwc, config);
355 wrmsrl(hwc->config_base, val);
356 WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); 370 WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
357 hwc->state |= PERF_HES_STOPPED; 371 hwc->state |= PERF_HES_STOPPED;
358 } 372 }
@@ -360,7 +374,7 @@ static void perf_ibs_stop(struct perf_event *event, int flags)
360 if (hwc->state & PERF_HES_UPTODATE) 374 if (hwc->state & PERF_HES_UPTODATE)
361 return; 375 return;
362 376
363 perf_ibs_event_update(perf_ibs, event, val); 377 perf_ibs_event_update(perf_ibs, event, &config);
364 hwc->state |= PERF_HES_UPTODATE; 378 hwc->state |= PERF_HES_UPTODATE;
365} 379}
366 380
@@ -456,7 +470,7 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
456 struct perf_ibs_data ibs_data; 470 struct perf_ibs_data ibs_data;
457 int offset, size, check_rip, offset_max, throttle = 0; 471 int offset, size, check_rip, offset_max, throttle = 0;
458 unsigned int msr; 472 unsigned int msr;
459 u64 *buf, config; 473 u64 *buf, *config, period;
460 474
461 if (!test_bit(IBS_STARTED, pcpu->state)) { 475 if (!test_bit(IBS_STARTED, pcpu->state)) {
462 /* Catch spurious interrupts after stopping IBS: */ 476 /* 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)
477 * supported in all cpus. As this triggered an interrupt, we 491 * supported in all cpus. As this triggered an interrupt, we
478 * set the current count to the max count. 492 * set the current count to the max count.
479 */ 493 */
480 config = ibs_data.regs[0]; 494 config = &ibs_data.regs[0];
481 if (perf_ibs == &perf_ibs_op && !(ibs_caps & IBS_CAPS_RDWROPCNT)) { 495 if (perf_ibs == &perf_ibs_op && !(ibs_caps & IBS_CAPS_RDWROPCNT)) {
482 config &= ~IBS_OP_CUR_CNT; 496 *config &= ~IBS_OP_CUR_CNT;
483 config |= (config & IBS_OP_MAX_CNT) << 36; 497 *config |= (*config & IBS_OP_MAX_CNT) << 36;
484 } 498 }
485 499
486 perf_ibs_event_update(perf_ibs, event, config); 500 perf_ibs_event_update(perf_ibs, event, config);
487 perf_sample_data_init(&data, 0, hwc->last_period); 501 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))
489 goto out; /* no sw counter overflow */ 503 goto out; /* no sw counter overflow */
490 504
491 ibs_data.caps = ibs_caps; 505 ibs_data.caps = ibs_caps;
@@ -523,8 +537,10 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs)
523 537
524 throttle = perf_event_overflow(event, &data, &regs); 538 throttle = perf_event_overflow(event, &data, &regs);
525out: 539out:
526 config = (config >> 4) | (throttle ? 0 : perf_ibs->enable_mask); 540 if (throttle)
527 perf_ibs_enable_event(hwc, config); 541 perf_ibs_disable_event(perf_ibs, hwc, *config);
542 else
543 perf_ibs_enable_event(perf_ibs, hwc, period >> 4);
528 544
529 perf_event_update_userpage(event); 545 perf_event_update_userpage(event);
530 546