diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2010-06-16 08:37:10 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-09-09 14:46:30 -0400 |
commit | a4eaf7f14675cb512d69f0c928055e73d0c6d252 (patch) | |
tree | e8a0f631fc28d4bd9becd2e9e2c71743c64ee3ec /arch/sparc | |
parent | fa407f35e0298d841e4088f95a7f9cf6e725c6d5 (diff) |
perf: Rework the PMU methods
Replace pmu::{enable,disable,start,stop,unthrottle} with
pmu::{add,del,start,stop}, all of which take a flags argument.
The new interface extends the capability to stop a counter while
keeping it scheduled on the PMU. We replace the throttled state with
the generic stopped state.
This also allows us to efficiently stop/start counters over certain
code paths (like IRQ handlers).
It also allows scheduling a counter without it starting, allowing for
a generic frozen state (useful for rotating stopped counters).
The stopped state is implemented in two different ways, depending on
how the architecture implemented the throttled state:
1) We disable the counter:
a) the pmu has per-counter enable bits, we flip that
b) we program a NOP event, preserving the counter state
2) We store the counter state and ignore all read/overflow events
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus <paulus@samba.org>
Cc: stephane eranian <eranian@googlemail.com>
Cc: Robert Richter <robert.richter@amd.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Cyrill Gorcunov <gorcunov@gmail.com>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Yanmin <yanmin_zhang@linux.intel.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: Michael Cree <mcree@orcon.net.nz>
LKML-Reference: <new-submission>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/kernel/perf_event.c | 109 |
1 files changed, 67 insertions, 42 deletions
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c index 37cae676536c..516be2314b54 100644 --- a/arch/sparc/kernel/perf_event.c +++ b/arch/sparc/kernel/perf_event.c | |||
@@ -658,13 +658,16 @@ static u64 maybe_change_configuration(struct cpu_hw_events *cpuc, u64 pcr) | |||
658 | 658 | ||
659 | enc = perf_event_get_enc(cpuc->events[i]); | 659 | enc = perf_event_get_enc(cpuc->events[i]); |
660 | pcr &= ~mask_for_index(idx); | 660 | pcr &= ~mask_for_index(idx); |
661 | pcr |= event_encoding(enc, idx); | 661 | if (hwc->state & PERF_HES_STOPPED) |
662 | pcr |= nop_for_index(idx); | ||
663 | else | ||
664 | pcr |= event_encoding(enc, idx); | ||
662 | } | 665 | } |
663 | out: | 666 | out: |
664 | return pcr; | 667 | return pcr; |
665 | } | 668 | } |
666 | 669 | ||
667 | static void sparc_pmu_pmu_enable(struct pmu *pmu) | 670 | static void sparc_pmu_enable(struct pmu *pmu) |
668 | { | 671 | { |
669 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 672 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
670 | u64 pcr; | 673 | u64 pcr; |
@@ -691,7 +694,7 @@ static void sparc_pmu_pmu_enable(struct pmu *pmu) | |||
691 | pcr_ops->write(cpuc->pcr); | 694 | pcr_ops->write(cpuc->pcr); |
692 | } | 695 | } |
693 | 696 | ||
694 | static void sparc_pmu_pmu_disable(struct pmu *pmu) | 697 | static void sparc_pmu_disable(struct pmu *pmu) |
695 | { | 698 | { |
696 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 699 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
697 | u64 val; | 700 | u64 val; |
@@ -710,10 +713,53 @@ static void sparc_pmu_pmu_disable(struct pmu *pmu) | |||
710 | pcr_ops->write(cpuc->pcr); | 713 | pcr_ops->write(cpuc->pcr); |
711 | } | 714 | } |
712 | 715 | ||
713 | static void sparc_pmu_disable(struct perf_event *event) | 716 | static int active_event_index(struct cpu_hw_events *cpuc, |
717 | struct perf_event *event) | ||
718 | { | ||
719 | int i; | ||
720 | |||
721 | for (i = 0; i < cpuc->n_events; i++) { | ||
722 | if (cpuc->event[i] == event) | ||
723 | break; | ||
724 | } | ||
725 | BUG_ON(i == cpuc->n_events); | ||
726 | return cpuc->current_idx[i]; | ||
727 | } | ||
728 | |||
729 | static void sparc_pmu_start(struct perf_event *event, int flags) | ||
730 | { | ||
731 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
732 | int idx = active_event_index(cpuc, event); | ||
733 | |||
734 | if (flags & PERF_EF_RELOAD) { | ||
735 | WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); | ||
736 | sparc_perf_event_set_period(event, &event->hw, idx); | ||
737 | } | ||
738 | |||
739 | event->hw.state = 0; | ||
740 | |||
741 | sparc_pmu_enable_event(cpuc, &event->hw, idx); | ||
742 | } | ||
743 | |||
744 | static void sparc_pmu_stop(struct perf_event *event, int flags) | ||
745 | { | ||
746 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
747 | int idx = active_event_index(cpuc, event); | ||
748 | |||
749 | if (!(event->hw.state & PERF_HES_STOPPED)) { | ||
750 | sparc_pmu_disable_event(cpuc, &event->hw, idx); | ||
751 | event->hw.state |= PERF_HES_STOPPED; | ||
752 | } | ||
753 | |||
754 | if (!(event->hw.state & PERF_HES_UPTODATE) && (flags & PERF_EF_UPDATE)) { | ||
755 | sparc_perf_event_update(event, &event->hw, idx); | ||
756 | event->hw.state |= PERF_HES_UPTODATE; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | static void sparc_pmu_del(struct perf_event *event, int _flags) | ||
714 | { | 761 | { |
715 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 762 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
716 | struct hw_perf_event *hwc = &event->hw; | ||
717 | unsigned long flags; | 763 | unsigned long flags; |
718 | int i; | 764 | int i; |
719 | 765 | ||
@@ -722,7 +768,10 @@ static void sparc_pmu_disable(struct perf_event *event) | |||
722 | 768 | ||
723 | for (i = 0; i < cpuc->n_events; i++) { | 769 | for (i = 0; i < cpuc->n_events; i++) { |
724 | if (event == cpuc->event[i]) { | 770 | if (event == cpuc->event[i]) { |
725 | int idx = cpuc->current_idx[i]; | 771 | /* Absorb the final count and turn off the |
772 | * event. | ||
773 | */ | ||
774 | sparc_pmu_stop(event, PERF_EF_UPDATE); | ||
726 | 775 | ||
727 | /* Shift remaining entries down into | 776 | /* Shift remaining entries down into |
728 | * the existing slot. | 777 | * the existing slot. |
@@ -734,13 +783,6 @@ static void sparc_pmu_disable(struct perf_event *event) | |||
734 | cpuc->current_idx[i]; | 783 | cpuc->current_idx[i]; |
735 | } | 784 | } |
736 | 785 | ||
737 | /* Absorb the final count and turn off the | ||
738 | * event. | ||
739 | */ | ||
740 | sparc_pmu_disable_event(cpuc, hwc, idx); | ||
741 | barrier(); | ||
742 | sparc_perf_event_update(event, hwc, idx); | ||
743 | |||
744 | perf_event_update_userpage(event); | 786 | perf_event_update_userpage(event); |
745 | 787 | ||
746 | cpuc->n_events--; | 788 | cpuc->n_events--; |
@@ -752,19 +794,6 @@ static void sparc_pmu_disable(struct perf_event *event) | |||
752 | local_irq_restore(flags); | 794 | local_irq_restore(flags); |
753 | } | 795 | } |
754 | 796 | ||
755 | static int active_event_index(struct cpu_hw_events *cpuc, | ||
756 | struct perf_event *event) | ||
757 | { | ||
758 | int i; | ||
759 | |||
760 | for (i = 0; i < cpuc->n_events; i++) { | ||
761 | if (cpuc->event[i] == event) | ||
762 | break; | ||
763 | } | ||
764 | BUG_ON(i == cpuc->n_events); | ||
765 | return cpuc->current_idx[i]; | ||
766 | } | ||
767 | |||
768 | static void sparc_pmu_read(struct perf_event *event) | 797 | static void sparc_pmu_read(struct perf_event *event) |
769 | { | 798 | { |
770 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 799 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
@@ -774,15 +803,6 @@ static void sparc_pmu_read(struct perf_event *event) | |||
774 | sparc_perf_event_update(event, hwc, idx); | 803 | sparc_perf_event_update(event, hwc, idx); |
775 | } | 804 | } |
776 | 805 | ||
777 | static void sparc_pmu_unthrottle(struct perf_event *event) | ||
778 | { | ||
779 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | ||
780 | int idx = active_event_index(cpuc, event); | ||
781 | struct hw_perf_event *hwc = &event->hw; | ||
782 | |||
783 | sparc_pmu_enable_event(cpuc, hwc, idx); | ||
784 | } | ||
785 | |||
786 | static atomic_t active_events = ATOMIC_INIT(0); | 806 | static atomic_t active_events = ATOMIC_INIT(0); |
787 | static DEFINE_MUTEX(pmc_grab_mutex); | 807 | static DEFINE_MUTEX(pmc_grab_mutex); |
788 | 808 | ||
@@ -984,7 +1004,7 @@ static int collect_events(struct perf_event *group, int max_count, | |||
984 | return n; | 1004 | return n; |
985 | } | 1005 | } |
986 | 1006 | ||
987 | static int sparc_pmu_enable(struct perf_event *event) | 1007 | static int sparc_pmu_add(struct perf_event *event, int ef_flags) |
988 | { | 1008 | { |
989 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 1009 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
990 | int n0, ret = -EAGAIN; | 1010 | int n0, ret = -EAGAIN; |
@@ -1001,6 +1021,10 @@ static int sparc_pmu_enable(struct perf_event *event) | |||
1001 | cpuc->events[n0] = event->hw.event_base; | 1021 | cpuc->events[n0] = event->hw.event_base; |
1002 | cpuc->current_idx[n0] = PIC_NO_INDEX; | 1022 | cpuc->current_idx[n0] = PIC_NO_INDEX; |
1003 | 1023 | ||
1024 | event->hw.state = PERF_HES_UPTODATE; | ||
1025 | if (!(ef_flags & PERF_EF_START)) | ||
1026 | event->hw.state |= PERF_HES_STOPPED; | ||
1027 | |||
1004 | /* | 1028 | /* |
1005 | * If group events scheduling transaction was started, | 1029 | * If group events scheduling transaction was started, |
1006 | * skip the schedulability test here, it will be peformed | 1030 | * skip the schedulability test here, it will be peformed |
@@ -1156,13 +1180,14 @@ static int sparc_pmu_commit_txn(struct pmu *pmu) | |||
1156 | } | 1180 | } |
1157 | 1181 | ||
1158 | static struct pmu pmu = { | 1182 | static struct pmu pmu = { |
1159 | .pmu_enable = sparc_pmu_pmu_enable, | 1183 | .pmu_enable = sparc_pmu_enable, |
1160 | .pmu_disable = sparc_pmu_pmu_disable, | 1184 | .pmu_disable = sparc_pmu_disable, |
1161 | .event_init = sparc_pmu_event_init, | 1185 | .event_init = sparc_pmu_event_init, |
1162 | .enable = sparc_pmu_enable, | 1186 | .add = sparc_pmu_add, |
1163 | .disable = sparc_pmu_disable, | 1187 | .del = sparc_pmu_del, |
1188 | .start = sparc_pmu_start, | ||
1189 | .stop = sparc_pmu_stop, | ||
1164 | .read = sparc_pmu_read, | 1190 | .read = sparc_pmu_read, |
1165 | .unthrottle = sparc_pmu_unthrottle, | ||
1166 | .start_txn = sparc_pmu_start_txn, | 1191 | .start_txn = sparc_pmu_start_txn, |
1167 | .cancel_txn = sparc_pmu_cancel_txn, | 1192 | .cancel_txn = sparc_pmu_cancel_txn, |
1168 | .commit_txn = sparc_pmu_commit_txn, | 1193 | .commit_txn = sparc_pmu_commit_txn, |
@@ -1243,7 +1268,7 @@ static int __kprobes perf_event_nmi_handler(struct notifier_block *self, | |||
1243 | continue; | 1268 | continue; |
1244 | 1269 | ||
1245 | if (perf_event_overflow(event, 1, &data, regs)) | 1270 | if (perf_event_overflow(event, 1, &data, regs)) |
1246 | sparc_pmu_disable_event(cpuc, hwc, idx); | 1271 | sparc_pmu_stop(event, 0); |
1247 | } | 1272 | } |
1248 | 1273 | ||
1249 | return NOTIFY_STOP; | 1274 | return NOTIFY_STOP; |