aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Shishkin <alexander.shishkin@linux.intel.com>2015-06-09 06:03:26 -0400
committerIngo Molnar <mingo@kernel.org>2015-06-19 03:38:47 -0400
commit1b7b938f181742f899a2f31c280f79dabef6ddb6 (patch)
tree2b51a8b3e4f19792dbb12e931bcf05f29591ced1
parent6b099d9b040b0f3d0aec05b560d7caf879af5077 (diff)
perf/x86/intel: Fix PMI handling for Intel PT
Intel PT is a separate PMU and it is not using any of the x86_pmu code paths, which means in particular that the active_events counter remains intact when new PT events are created. However, PT uses the generic x86_pmu PMI handler for its PMI handling needs. The problem here is that the latter checks active_events and in case of it being zero, exits without calling the actual x86_pmu.handle_nmi(), which results in unknown NMI errors and massive data loss for PT. The effect is not visible if there are other perf events in the system at the same time that keep active_events counter non-zero, for instance if the NMI watchdog is running, so one needs to disable it to reproduce the problem. At the same time, the active_events counter besides doing what the name suggests also implicitly serves as a PMC hardware and DS area reference counter. This patch adds a separate reference counter for the PMC hardware, leaving active_events for actually counting the events and makes sure it also counts PT and BTS events. Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: acme@infradead.org Cc: adrian.hunter@intel.com Link: http://lkml.kernel.org/r/87k2v92t0s.fsf@ashishki-desk.ger.corp.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/kernel/cpu/perf_event.c27
1 files changed, 23 insertions, 4 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index aa4e3a74e541..6be186ccef46 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -135,6 +135,7 @@ static int x86_pmu_extra_regs(u64 config, struct perf_event *event)
135} 135}
136 136
137static atomic_t active_events; 137static atomic_t active_events;
138static atomic_t pmc_refcount;
138static DEFINE_MUTEX(pmc_reserve_mutex); 139static DEFINE_MUTEX(pmc_reserve_mutex);
139 140
140#ifdef CONFIG_X86_LOCAL_APIC 141#ifdef CONFIG_X86_LOCAL_APIC
@@ -271,6 +272,7 @@ msr_fail:
271static void hw_perf_event_destroy(struct perf_event *event) 272static void hw_perf_event_destroy(struct perf_event *event)
272{ 273{
273 x86_release_hardware(); 274 x86_release_hardware();
275 atomic_dec(&active_events);
274} 276}
275 277
276void hw_perf_lbr_event_destroy(struct perf_event *event) 278void hw_perf_lbr_event_destroy(struct perf_event *event)
@@ -324,16 +326,16 @@ int x86_reserve_hardware(void)
324{ 326{
325 int err = 0; 327 int err = 0;
326 328
327 if (!atomic_inc_not_zero(&active_events)) { 329 if (!atomic_inc_not_zero(&pmc_refcount)) {
328 mutex_lock(&pmc_reserve_mutex); 330 mutex_lock(&pmc_reserve_mutex);
329 if (atomic_read(&active_events) == 0) { 331 if (atomic_read(&pmc_refcount) == 0) {
330 if (!reserve_pmc_hardware()) 332 if (!reserve_pmc_hardware())
331 err = -EBUSY; 333 err = -EBUSY;
332 else 334 else
333 reserve_ds_buffers(); 335 reserve_ds_buffers();
334 } 336 }
335 if (!err) 337 if (!err)
336 atomic_inc(&active_events); 338 atomic_inc(&pmc_refcount);
337 mutex_unlock(&pmc_reserve_mutex); 339 mutex_unlock(&pmc_reserve_mutex);
338 } 340 }
339 341
@@ -342,7 +344,7 @@ int x86_reserve_hardware(void)
342 344
343void x86_release_hardware(void) 345void x86_release_hardware(void)
344{ 346{
345 if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { 347 if (atomic_dec_and_mutex_lock(&pmc_refcount, &pmc_reserve_mutex)) {
346 release_pmc_hardware(); 348 release_pmc_hardware();
347 release_ds_buffers(); 349 release_ds_buffers();
348 mutex_unlock(&pmc_reserve_mutex); 350 mutex_unlock(&pmc_reserve_mutex);
@@ -371,12 +373,24 @@ int x86_add_exclusive(unsigned int what)
371 373
372out: 374out:
373 mutex_unlock(&pmc_reserve_mutex); 375 mutex_unlock(&pmc_reserve_mutex);
376
377 /*
378 * Assuming that all exclusive events will share the PMI handler
379 * (which checks active_events for whether there is work to do),
380 * we can bump active_events counter right here, except for
381 * x86_lbr_exclusive_lbr events that go through x86_pmu_event_init()
382 * path, which already bumps active_events for them.
383 */
384 if (!ret && what != x86_lbr_exclusive_lbr)
385 atomic_inc(&active_events);
386
374 return ret; 387 return ret;
375} 388}
376 389
377void x86_del_exclusive(unsigned int what) 390void x86_del_exclusive(unsigned int what)
378{ 391{
379 atomic_dec(&x86_pmu.lbr_exclusive[what]); 392 atomic_dec(&x86_pmu.lbr_exclusive[what]);
393 atomic_dec(&active_events);
380} 394}
381 395
382int x86_setup_perfctr(struct perf_event *event) 396int x86_setup_perfctr(struct perf_event *event)
@@ -557,6 +571,7 @@ static int __x86_pmu_event_init(struct perf_event *event)
557 if (err) 571 if (err)
558 return err; 572 return err;
559 573
574 atomic_inc(&active_events);
560 event->destroy = hw_perf_event_destroy; 575 event->destroy = hw_perf_event_destroy;
561 576
562 event->hw.idx = -1; 577 event->hw.idx = -1;
@@ -1429,6 +1444,10 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs)
1429 u64 finish_clock; 1444 u64 finish_clock;
1430 int ret; 1445 int ret;
1431 1446
1447 /*
1448 * All PMUs/events that share this PMI handler should make sure to
1449 * increment active_events for their events.
1450 */
1432 if (!atomic_read(&active_events)) 1451 if (!atomic_read(&active_events))
1433 return NMI_DONE; 1452 return NMI_DONE;
1434 1453