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/x86 | |
| 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/x86')
| -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; |
