diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-03-02 13:52:12 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-10 07:23:31 -0500 |
commit | ca037701a025334e724e5c61b3b1082940c8b981 (patch) | |
tree | 12e3651ae6b35e9a5df4b49f9f571a01fc5a42a4 /arch/x86/kernel/cpu/perf_event_intel.c | |
parent | d4944a06666054707d23e11888e480af239e5abf (diff) |
perf, x86: Add PEBS infrastructure
This patch implements support for Intel Precise Event Based Sampling,
which is an alternative counter mode in which the counter triggers a
hardware assist to collect information on events. The hardware assist
takes a trap like snapshot of a subset of the machine registers.
This data is written to the Intel Debug-Store, which can be programmed
with a data threshold at which to raise a PMI.
With the PEBS hardware assist being trap like, the reported IP is always
one instruction after the actual instruction that triggered the event.
This implements a simple PEBS model that always takes a single PEBS event
at a time. This is done so that the interaction with the rest of the
system is as expected (freq adjust, period randomization, lbr,
callchains, etc.).
It adds an ABI element: perf_event_attr::precise, which indicates that we
wish to use this (constrained, but precise) mode.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@infradead.org>
Cc: paulus@samba.org
Cc: eranian@google.com
Cc: robert.richter@amd.com
Cc: fweisbec@gmail.com
LKML-Reference: <20100304140100.392111285@chello.nl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event_intel.c')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel.c | 150 |
1 files changed, 34 insertions, 116 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 84bfde64a337..11446412e4c7 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c | |||
@@ -470,42 +470,6 @@ static u64 intel_pmu_raw_event(u64 hw_event) | |||
470 | return hw_event & CORE_EVNTSEL_MASK; | 470 | return hw_event & CORE_EVNTSEL_MASK; |
471 | } | 471 | } |
472 | 472 | ||
473 | static void intel_pmu_enable_bts(u64 config) | ||
474 | { | ||
475 | unsigned long debugctlmsr; | ||
476 | |||
477 | debugctlmsr = get_debugctlmsr(); | ||
478 | |||
479 | debugctlmsr |= X86_DEBUGCTL_TR; | ||
480 | debugctlmsr |= X86_DEBUGCTL_BTS; | ||
481 | debugctlmsr |= X86_DEBUGCTL_BTINT; | ||
482 | |||
483 | if (!(config & ARCH_PERFMON_EVENTSEL_OS)) | ||
484 | debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; | ||
485 | |||
486 | if (!(config & ARCH_PERFMON_EVENTSEL_USR)) | ||
487 | debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; | ||
488 | |||
489 | update_debugctlmsr(debugctlmsr); | ||
490 | } | ||
491 | |||
492 | static void intel_pmu_disable_bts(void) | ||
493 | { | ||
494 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
495 | unsigned long debugctlmsr; | ||
496 | |||
497 | if (!cpuc->ds) | ||
498 | return; | ||
499 | |||
500 | debugctlmsr = get_debugctlmsr(); | ||
501 | |||
502 | debugctlmsr &= | ||
503 | ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | | ||
504 | X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); | ||
505 | |||
506 | update_debugctlmsr(debugctlmsr); | ||
507 | } | ||
508 | |||
509 | static void intel_pmu_disable_all(void) | 473 | static void intel_pmu_disable_all(void) |
510 | { | 474 | { |
511 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 475 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
@@ -514,6 +478,8 @@ static void intel_pmu_disable_all(void) | |||
514 | 478 | ||
515 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) | 479 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) |
516 | intel_pmu_disable_bts(); | 480 | intel_pmu_disable_bts(); |
481 | |||
482 | intel_pmu_pebs_disable_all(); | ||
517 | } | 483 | } |
518 | 484 | ||
519 | static void intel_pmu_enable_all(void) | 485 | static void intel_pmu_enable_all(void) |
@@ -531,6 +497,8 @@ static void intel_pmu_enable_all(void) | |||
531 | 497 | ||
532 | intel_pmu_enable_bts(event->hw.config); | 498 | intel_pmu_enable_bts(event->hw.config); |
533 | } | 499 | } |
500 | |||
501 | intel_pmu_pebs_enable_all(); | ||
534 | } | 502 | } |
535 | 503 | ||
536 | static inline u64 intel_pmu_get_status(void) | 504 | static inline u64 intel_pmu_get_status(void) |
@@ -547,8 +515,7 @@ static inline void intel_pmu_ack_status(u64 ack) | |||
547 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); | 515 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); |
548 | } | 516 | } |
549 | 517 | ||
550 | static inline void | 518 | static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) |
551 | intel_pmu_disable_fixed(struct hw_perf_event *hwc) | ||
552 | { | 519 | { |
553 | int idx = hwc->idx - X86_PMC_IDX_FIXED; | 520 | int idx = hwc->idx - X86_PMC_IDX_FIXED; |
554 | u64 ctrl_val, mask; | 521 | u64 ctrl_val, mask; |
@@ -560,68 +527,7 @@ intel_pmu_disable_fixed(struct hw_perf_event *hwc) | |||
560 | (void)checking_wrmsrl(hwc->config_base, ctrl_val); | 527 | (void)checking_wrmsrl(hwc->config_base, ctrl_val); |
561 | } | 528 | } |
562 | 529 | ||
563 | static void intel_pmu_drain_bts_buffer(void) | 530 | static void intel_pmu_disable_event(struct perf_event *event) |
564 | { | ||
565 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
566 | struct debug_store *ds = cpuc->ds; | ||
567 | struct bts_record { | ||
568 | u64 from; | ||
569 | u64 to; | ||
570 | u64 flags; | ||
571 | }; | ||
572 | struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; | ||
573 | struct bts_record *at, *top; | ||
574 | struct perf_output_handle handle; | ||
575 | struct perf_event_header header; | ||
576 | struct perf_sample_data data; | ||
577 | struct pt_regs regs; | ||
578 | |||
579 | if (!event) | ||
580 | return; | ||
581 | |||
582 | if (!ds) | ||
583 | return; | ||
584 | |||
585 | at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; | ||
586 | top = (struct bts_record *)(unsigned long)ds->bts_index; | ||
587 | |||
588 | if (top <= at) | ||
589 | return; | ||
590 | |||
591 | ds->bts_index = ds->bts_buffer_base; | ||
592 | |||
593 | perf_sample_data_init(&data, 0); | ||
594 | |||
595 | data.period = event->hw.last_period; | ||
596 | regs.ip = 0; | ||
597 | |||
598 | /* | ||
599 | * Prepare a generic sample, i.e. fill in the invariant fields. | ||
600 | * We will overwrite the from and to address before we output | ||
601 | * the sample. | ||
602 | */ | ||
603 | perf_prepare_sample(&header, &data, event, ®s); | ||
604 | |||
605 | if (perf_output_begin(&handle, event, | ||
606 | header.size * (top - at), 1, 1)) | ||
607 | return; | ||
608 | |||
609 | for (; at < top; at++) { | ||
610 | data.ip = at->from; | ||
611 | data.addr = at->to; | ||
612 | |||
613 | perf_output_sample(&handle, &header, &data, event); | ||
614 | } | ||
615 | |||
616 | perf_output_end(&handle); | ||
617 | |||
618 | /* There's new data available. */ | ||
619 | event->hw.interrupts++; | ||
620 | event->pending_kill = POLL_IN; | ||
621 | } | ||
622 | |||
623 | static inline void | ||
624 | intel_pmu_disable_event(struct perf_event *event) | ||
625 | { | 531 | { |
626 | struct hw_perf_event *hwc = &event->hw; | 532 | struct hw_perf_event *hwc = &event->hw; |
627 | 533 | ||
@@ -637,10 +543,12 @@ intel_pmu_disable_event(struct perf_event *event) | |||
637 | } | 543 | } |
638 | 544 | ||
639 | x86_pmu_disable_event(event); | 545 | x86_pmu_disable_event(event); |
546 | |||
547 | if (unlikely(event->attr.precise)) | ||
548 | intel_pmu_pebs_disable(hwc); | ||
640 | } | 549 | } |
641 | 550 | ||
642 | static inline void | 551 | static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) |
643 | intel_pmu_enable_fixed(struct hw_perf_event *hwc) | ||
644 | { | 552 | { |
645 | int idx = hwc->idx - X86_PMC_IDX_FIXED; | 553 | int idx = hwc->idx - X86_PMC_IDX_FIXED; |
646 | u64 ctrl_val, bits, mask; | 554 | u64 ctrl_val, bits, mask; |
@@ -689,6 +597,9 @@ static void intel_pmu_enable_event(struct perf_event *event) | |||
689 | return; | 597 | return; |
690 | } | 598 | } |
691 | 599 | ||
600 | if (unlikely(event->attr.precise)) | ||
601 | intel_pmu_pebs_enable(hwc); | ||
602 | |||
692 | __x86_pmu_enable_event(hwc); | 603 | __x86_pmu_enable_event(hwc); |
693 | } | 604 | } |
694 | 605 | ||
@@ -762,6 +673,13 @@ again: | |||
762 | 673 | ||
763 | inc_irq_stat(apic_perf_irqs); | 674 | inc_irq_stat(apic_perf_irqs); |
764 | ack = status; | 675 | ack = status; |
676 | |||
677 | /* | ||
678 | * PEBS overflow sets bit 62 in the global status register | ||
679 | */ | ||
680 | if (__test_and_clear_bit(62, (unsigned long *)&status)) | ||
681 | x86_pmu.drain_pebs(regs); | ||
682 | |||
765 | for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { | 683 | for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { |
766 | struct perf_event *event = cpuc->events[bit]; | 684 | struct perf_event *event = cpuc->events[bit]; |
767 | 685 | ||
@@ -791,22 +709,18 @@ done: | |||
791 | return 1; | 709 | return 1; |
792 | } | 710 | } |
793 | 711 | ||
794 | static struct event_constraint bts_constraint = | ||
795 | EVENT_CONSTRAINT(0, 1ULL << X86_PMC_IDX_FIXED_BTS, 0); | ||
796 | |||
797 | static struct event_constraint * | 712 | static struct event_constraint * |
798 | intel_special_constraints(struct perf_event *event) | 713 | intel_bts_constraints(struct perf_event *event) |
799 | { | 714 | { |
800 | unsigned int hw_event; | 715 | struct hw_perf_event *hwc = &event->hw; |
801 | 716 | unsigned int hw_event, bts_event; | |
802 | hw_event = event->hw.config & INTEL_ARCH_EVENT_MASK; | ||
803 | 717 | ||
804 | if (unlikely((hw_event == | 718 | hw_event = hwc->config & INTEL_ARCH_EVENT_MASK; |
805 | x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && | 719 | bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); |
806 | (event->hw.sample_period == 1))) { | ||
807 | 720 | ||
721 | if (unlikely(hw_event == bts_event && hwc->sample_period == 1)) | ||
808 | return &bts_constraint; | 722 | return &bts_constraint; |
809 | } | 723 | |
810 | return NULL; | 724 | return NULL; |
811 | } | 725 | } |
812 | 726 | ||
@@ -815,7 +729,11 @@ intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event | |||
815 | { | 729 | { |
816 | struct event_constraint *c; | 730 | struct event_constraint *c; |
817 | 731 | ||
818 | c = intel_special_constraints(event); | 732 | c = intel_bts_constraints(event); |
733 | if (c) | ||
734 | return c; | ||
735 | |||
736 | c = intel_pebs_constraints(event); | ||
819 | if (c) | 737 | if (c) |
820 | return c; | 738 | return c; |
821 | 739 | ||
@@ -864,8 +782,6 @@ static __initconst struct x86_pmu intel_pmu = { | |||
864 | * the generic event period: | 782 | * the generic event period: |
865 | */ | 783 | */ |
866 | .max_period = (1ULL << 31) - 1, | 784 | .max_period = (1ULL << 31) - 1, |
867 | .enable_bts = intel_pmu_enable_bts, | ||
868 | .disable_bts = intel_pmu_disable_bts, | ||
869 | .get_event_constraints = intel_get_event_constraints, | 785 | .get_event_constraints = intel_get_event_constraints, |
870 | 786 | ||
871 | .cpu_starting = init_debug_store_on_cpu, | 787 | .cpu_starting = init_debug_store_on_cpu, |
@@ -915,6 +831,8 @@ static __init int intel_pmu_init(void) | |||
915 | if (version > 1) | 831 | if (version > 1) |
916 | x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); | 832 | x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); |
917 | 833 | ||
834 | intel_ds_init(); | ||
835 | |||
918 | /* | 836 | /* |
919 | * Install the hw-cache-events table: | 837 | * Install the hw-cache-events table: |
920 | */ | 838 | */ |