diff options
author | Alexander Shishkin <alexander.shishkin@linux.intel.com> | 2015-06-11 08:13:56 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-06-29 15:35:28 -0400 |
commit | 55c9e52cf3b056109ab84de8d2a5b4b6e0a4cefa (patch) | |
tree | f68472f73281582d17f459356f4b10e9ad3ecdb2 /arch | |
parent | 0432ca1ad0d399d40db32c4364143b1d741e18c5 (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')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 52 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.h | 4 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_bts.c | 9 |
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 | ||
271 | static void hw_perf_event_destroy(struct perf_event *event) | 271 | static 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 | ||
280 | void hw_perf_lbr_event_destroy(struct perf_event *event) | 276 | void 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 | ||
323 | int 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 | |||
343 | void 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 | ||
704 | void x86_del_exclusive(unsigned int what); | 704 | void x86_del_exclusive(unsigned int what); |
705 | 705 | ||
706 | int x86_reserve_hardware(void); | ||
707 | |||
708 | void x86_release_hardware(void); | ||
709 | |||
706 | void hw_perf_lbr_event_destroy(struct perf_event *event); | 710 | void hw_perf_lbr_event_destroy(struct perf_event *event); |
707 | 711 | ||
708 | int x86_setup_perfctr(struct perf_event *event); | 712 | int 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 | ||
484 | static void bts_event_destroy(struct perf_event *event) | 484 | static 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 | ||
489 | static int bts_event_init(struct perf_event *event) | 490 | static 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; |