aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorAlexander Shishkin <alexander.shishkin@linux.intel.com>2015-06-11 08:13:56 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-06-29 15:35:28 -0400
commit55c9e52cf3b056109ab84de8d2a5b4b6e0a4cefa (patch)
treef68472f73281582d17f459356f4b10e9ad3ecdb2 /arch/x86
parent0432ca1ad0d399d40db32c4364143b1d741e18c5 (diff)
perf/x86/intel/bts: Fix DS area sharing with x86_pmu events
commit 6b099d9b040b0f3d0aec05b560d7caf879af5077 upstream. Currently, the intel_bts driver relies on the DS area allocated by the x86_pmu code in its event_init() path, which is a bug: creating a BTS event while no x86_pmu events are present results in a NULL pointer dereference. The same DS area is also used by PEBS sampling, which makes it quite a bit trickier to have a separate one for intel_bts' purposes. This patch makes intel_bts driver use the same DS allocation and reference counting code as x86_pmu to make sure it is always present when either intel_bts or x86_pmu need it. 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/1434024837-9916-2-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/kernel/cpu/perf_event.c52
-rw-r--r--arch/x86/kernel/cpu/perf_event.h4
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_bts.c9
3 files changed, 46 insertions, 19 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 4f7001f28936..aa4e3a74e541 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -270,11 +270,7 @@ msr_fail:
270 270
271static void hw_perf_event_destroy(struct perf_event *event) 271static void hw_perf_event_destroy(struct perf_event *event)
272{ 272{
273 if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { 273 x86_release_hardware();
274 release_pmc_hardware();
275 release_ds_buffers();
276 mutex_unlock(&pmc_reserve_mutex);
277 }
278} 274}
279 275
280void hw_perf_lbr_event_destroy(struct perf_event *event) 276void hw_perf_lbr_event_destroy(struct perf_event *event)
@@ -324,6 +320,35 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
324 return x86_pmu_extra_regs(val, event); 320 return x86_pmu_extra_regs(val, event);
325} 321}
326 322
323int x86_reserve_hardware(void)
324{
325 int err = 0;
326
327 if (!atomic_inc_not_zero(&active_events)) {
328 mutex_lock(&pmc_reserve_mutex);
329 if (atomic_read(&active_events) == 0) {
330 if (!reserve_pmc_hardware())
331 err = -EBUSY;
332 else
333 reserve_ds_buffers();
334 }
335 if (!err)
336 atomic_inc(&active_events);
337 mutex_unlock(&pmc_reserve_mutex);
338 }
339
340 return err;
341}
342
343void x86_release_hardware(void)
344{
345 if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) {
346 release_pmc_hardware();
347 release_ds_buffers();
348 mutex_unlock(&pmc_reserve_mutex);
349 }
350}
351
327/* 352/*
328 * Check if we can create event of a certain type (that no conflicting events 353 * Check if we can create event of a certain type (that no conflicting events
329 * are present). 354 * are present).
@@ -336,9 +361,10 @@ int x86_add_exclusive(unsigned int what)
336 return 0; 361 return 0;
337 362
338 mutex_lock(&pmc_reserve_mutex); 363 mutex_lock(&pmc_reserve_mutex);
339 for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++) 364 for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++) {
340 if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i])) 365 if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
341 goto out; 366 goto out;
367 }
342 368
343 atomic_inc(&x86_pmu.lbr_exclusive[what]); 369 atomic_inc(&x86_pmu.lbr_exclusive[what]);
344 ret = 0; 370 ret = 0;
@@ -527,19 +553,7 @@ static int __x86_pmu_event_init(struct perf_event *event)
527 if (!x86_pmu_initialized()) 553 if (!x86_pmu_initialized())
528 return -ENODEV; 554 return -ENODEV;
529 555
530 err = 0; 556 err = x86_reserve_hardware();
531 if (!atomic_inc_not_zero(&active_events)) {
532 mutex_lock(&pmc_reserve_mutex);
533 if (atomic_read(&active_events) == 0) {
534 if (!reserve_pmc_hardware())
535 err = -EBUSY;
536 else
537 reserve_ds_buffers();
538 }
539 if (!err)
540 atomic_inc(&active_events);
541 mutex_unlock(&pmc_reserve_mutex);
542 }
543 if (err) 557 if (err)
544 return err; 558 return err;
545 559
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index ef78516850fb..f068695eaca0 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -703,6 +703,10 @@ int x86_add_exclusive(unsigned int what);
703 703
704void x86_del_exclusive(unsigned int what); 704void x86_del_exclusive(unsigned int what);
705 705
706int x86_reserve_hardware(void);
707
708void x86_release_hardware(void);
709
706void hw_perf_lbr_event_destroy(struct perf_event *event); 710void hw_perf_lbr_event_destroy(struct perf_event *event);
707 711
708int x86_setup_perfctr(struct perf_event *event); 712int x86_setup_perfctr(struct perf_event *event);
diff --git a/arch/x86/kernel/cpu/perf_event_intel_bts.c b/arch/x86/kernel/cpu/perf_event_intel_bts.c
index ac1f0c55f379..7795f3f8b1d5 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_bts.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_bts.c
@@ -483,17 +483,26 @@ static int bts_event_add(struct perf_event *event, int mode)
483 483
484static void bts_event_destroy(struct perf_event *event) 484static void bts_event_destroy(struct perf_event *event)
485{ 485{
486 x86_release_hardware();
486 x86_del_exclusive(x86_lbr_exclusive_bts); 487 x86_del_exclusive(x86_lbr_exclusive_bts);
487} 488}
488 489
489static int bts_event_init(struct perf_event *event) 490static int bts_event_init(struct perf_event *event)
490{ 491{
492 int ret;
493
491 if (event->attr.type != bts_pmu.type) 494 if (event->attr.type != bts_pmu.type)
492 return -ENOENT; 495 return -ENOENT;
493 496
494 if (x86_add_exclusive(x86_lbr_exclusive_bts)) 497 if (x86_add_exclusive(x86_lbr_exclusive_bts))
495 return -EBUSY; 498 return -EBUSY;
496 499
500 ret = x86_reserve_hardware();
501 if (ret) {
502 x86_del_exclusive(x86_lbr_exclusive_bts);
503 return ret;
504 }
505
497 event->destroy = bts_event_destroy; 506 event->destroy = bts_event_destroy;
498 507
499 return 0; 508 return 0;