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/powerpc/kernel/perf_event.c | |
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/powerpc/kernel/perf_event.c')
-rw-r--r-- | arch/powerpc/kernel/perf_event.c | 105 |
1 files changed, 68 insertions, 37 deletions
diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c index deb84bbcb0e6..9cb4924b6c07 100644 --- a/arch/powerpc/kernel/perf_event.c +++ b/arch/powerpc/kernel/perf_event.c | |||
@@ -402,6 +402,9 @@ static void power_pmu_read(struct perf_event *event) | |||
402 | { | 402 | { |
403 | s64 val, delta, prev; | 403 | s64 val, delta, prev; |
404 | 404 | ||
405 | if (event->hw.state & PERF_HES_STOPPED) | ||
406 | return; | ||
407 | |||
405 | if (!event->hw.idx) | 408 | if (!event->hw.idx) |
406 | return; | 409 | return; |
407 | /* | 410 | /* |
@@ -517,7 +520,7 @@ static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) | |||
517 | * Disable all events to prevent PMU interrupts and to allow | 520 | * Disable all events to prevent PMU interrupts and to allow |
518 | * events to be added or removed. | 521 | * events to be added or removed. |
519 | */ | 522 | */ |
520 | static void power_pmu_pmu_disable(struct pmu *pmu) | 523 | static void power_pmu_disable(struct pmu *pmu) |
521 | { | 524 | { |
522 | struct cpu_hw_events *cpuhw; | 525 | struct cpu_hw_events *cpuhw; |
523 | unsigned long flags; | 526 | unsigned long flags; |
@@ -565,7 +568,7 @@ static void power_pmu_pmu_disable(struct pmu *pmu) | |||
565 | * If we were previously disabled and events were added, then | 568 | * If we were previously disabled and events were added, then |
566 | * put the new config on the PMU. | 569 | * put the new config on the PMU. |
567 | */ | 570 | */ |
568 | static void power_pmu_pmu_enable(struct pmu *pmu) | 571 | static void power_pmu_enable(struct pmu *pmu) |
569 | { | 572 | { |
570 | struct perf_event *event; | 573 | struct perf_event *event; |
571 | struct cpu_hw_events *cpuhw; | 574 | struct cpu_hw_events *cpuhw; |
@@ -672,6 +675,8 @@ static void power_pmu_pmu_enable(struct pmu *pmu) | |||
672 | } | 675 | } |
673 | local64_set(&event->hw.prev_count, val); | 676 | local64_set(&event->hw.prev_count, val); |
674 | event->hw.idx = idx; | 677 | event->hw.idx = idx; |
678 | if (event->hw.state & PERF_HES_STOPPED) | ||
679 | val = 0; | ||
675 | write_pmc(idx, val); | 680 | write_pmc(idx, val); |
676 | perf_event_update_userpage(event); | 681 | perf_event_update_userpage(event); |
677 | } | 682 | } |
@@ -727,7 +732,7 @@ static int collect_events(struct perf_event *group, int max_count, | |||
727 | * re-enable the PMU in order to get hw_perf_enable to do the | 732 | * re-enable the PMU in order to get hw_perf_enable to do the |
728 | * actual work of reconfiguring the PMU. | 733 | * actual work of reconfiguring the PMU. |
729 | */ | 734 | */ |
730 | static int power_pmu_enable(struct perf_event *event) | 735 | static int power_pmu_add(struct perf_event *event, int ef_flags) |
731 | { | 736 | { |
732 | struct cpu_hw_events *cpuhw; | 737 | struct cpu_hw_events *cpuhw; |
733 | unsigned long flags; | 738 | unsigned long flags; |
@@ -749,6 +754,9 @@ static int power_pmu_enable(struct perf_event *event) | |||
749 | cpuhw->events[n0] = event->hw.config; | 754 | cpuhw->events[n0] = event->hw.config; |
750 | cpuhw->flags[n0] = event->hw.event_base; | 755 | cpuhw->flags[n0] = event->hw.event_base; |
751 | 756 | ||
757 | if (!(ef_flags & PERF_EF_START)) | ||
758 | event->hw.state = PERF_HES_STOPPED | PERF_HES_UPTODATE; | ||
759 | |||
752 | /* | 760 | /* |
753 | * If group events scheduling transaction was started, | 761 | * If group events scheduling transaction was started, |
754 | * skip the schedulability test here, it will be peformed | 762 | * skip the schedulability test here, it will be peformed |
@@ -777,7 +785,7 @@ nocheck: | |||
777 | /* | 785 | /* |
778 | * Remove a event from the PMU. | 786 | * Remove a event from the PMU. |
779 | */ | 787 | */ |
780 | static void power_pmu_disable(struct perf_event *event) | 788 | static void power_pmu_del(struct perf_event *event, int ef_flags) |
781 | { | 789 | { |
782 | struct cpu_hw_events *cpuhw; | 790 | struct cpu_hw_events *cpuhw; |
783 | long i; | 791 | long i; |
@@ -826,27 +834,53 @@ static void power_pmu_disable(struct perf_event *event) | |||
826 | } | 834 | } |
827 | 835 | ||
828 | /* | 836 | /* |
829 | * Re-enable interrupts on a event after they were throttled | 837 | * POWER-PMU does not support disabling individual counters, hence |
830 | * because they were coming too fast. | 838 | * program their cycle counter to their max value and ignore the interrupts. |
831 | */ | 839 | */ |
832 | static void power_pmu_unthrottle(struct perf_event *event) | 840 | |
841 | static void power_pmu_start(struct perf_event *event, int ef_flags) | ||
833 | { | 842 | { |
834 | s64 val, left; | ||
835 | unsigned long flags; | 843 | unsigned long flags; |
844 | s64 left; | ||
836 | 845 | ||
837 | if (!event->hw.idx || !event->hw.sample_period) | 846 | if (!event->hw.idx || !event->hw.sample_period) |
838 | return; | 847 | return; |
848 | |||
849 | if (!(event->hw.state & PERF_HES_STOPPED)) | ||
850 | return; | ||
851 | |||
852 | if (ef_flags & PERF_EF_RELOAD) | ||
853 | WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); | ||
854 | |||
855 | local_irq_save(flags); | ||
856 | perf_pmu_disable(event->pmu); | ||
857 | |||
858 | event->hw.state = 0; | ||
859 | left = local64_read(&event->hw.period_left); | ||
860 | write_pmc(event->hw.idx, left); | ||
861 | |||
862 | perf_event_update_userpage(event); | ||
863 | perf_pmu_enable(event->pmu); | ||
864 | local_irq_restore(flags); | ||
865 | } | ||
866 | |||
867 | static void power_pmu_stop(struct perf_event *event, int ef_flags) | ||
868 | { | ||
869 | unsigned long flags; | ||
870 | |||
871 | if (!event->hw.idx || !event->hw.sample_period) | ||
872 | return; | ||
873 | |||
874 | if (event->hw.state & PERF_HES_STOPPED) | ||
875 | return; | ||
876 | |||
839 | local_irq_save(flags); | 877 | local_irq_save(flags); |
840 | perf_pmu_disable(event->pmu); | 878 | perf_pmu_disable(event->pmu); |
879 | |||
841 | power_pmu_read(event); | 880 | power_pmu_read(event); |
842 | left = event->hw.sample_period; | 881 | event->hw.state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; |
843 | event->hw.last_period = left; | 882 | write_pmc(event->hw.idx, 0); |
844 | val = 0; | 883 | |
845 | if (left < 0x80000000L) | ||
846 | val = 0x80000000L - left; | ||
847 | write_pmc(event->hw.idx, val); | ||
848 | local64_set(&event->hw.prev_count, val); | ||
849 | local64_set(&event->hw.period_left, left); | ||
850 | perf_event_update_userpage(event); | 884 | perf_event_update_userpage(event); |
851 | perf_pmu_enable(event->pmu); | 885 | perf_pmu_enable(event->pmu); |
852 | local_irq_restore(flags); | 886 | local_irq_restore(flags); |
@@ -1131,13 +1165,14 @@ static int power_pmu_event_init(struct perf_event *event) | |||
1131 | } | 1165 | } |
1132 | 1166 | ||
1133 | struct pmu power_pmu = { | 1167 | struct pmu power_pmu = { |
1134 | .pmu_enable = power_pmu_pmu_enable, | 1168 | .pmu_enable = power_pmu_enable, |
1135 | .pmu_disable = power_pmu_pmu_disable, | 1169 | .pmu_disable = power_pmu_disable, |
1136 | .event_init = power_pmu_event_init, | 1170 | .event_init = power_pmu_event_init, |
1137 | .enable = power_pmu_enable, | 1171 | .add = power_pmu_add, |
1138 | .disable = power_pmu_disable, | 1172 | .del = power_pmu_del, |
1173 | .start = power_pmu_start, | ||
1174 | .stop = power_pmu_stop, | ||
1139 | .read = power_pmu_read, | 1175 | .read = power_pmu_read, |
1140 | .unthrottle = power_pmu_unthrottle, | ||
1141 | .start_txn = power_pmu_start_txn, | 1176 | .start_txn = power_pmu_start_txn, |
1142 | .cancel_txn = power_pmu_cancel_txn, | 1177 | .cancel_txn = power_pmu_cancel_txn, |
1143 | .commit_txn = power_pmu_commit_txn, | 1178 | .commit_txn = power_pmu_commit_txn, |
@@ -1155,6 +1190,11 @@ static void record_and_restart(struct perf_event *event, unsigned long val, | |||
1155 | s64 prev, delta, left; | 1190 | s64 prev, delta, left; |
1156 | int record = 0; | 1191 | int record = 0; |
1157 | 1192 | ||
1193 | if (event->hw.state & PERF_HES_STOPPED) { | ||
1194 | write_pmc(event->hw.idx, 0); | ||
1195 | return; | ||
1196 | } | ||
1197 | |||
1158 | /* we don't have to worry about interrupts here */ | 1198 | /* we don't have to worry about interrupts here */ |
1159 | prev = local64_read(&event->hw.prev_count); | 1199 | prev = local64_read(&event->hw.prev_count); |
1160 | delta = (val - prev) & 0xfffffffful; | 1200 | delta = (val - prev) & 0xfffffffful; |
@@ -1177,6 +1217,11 @@ static void record_and_restart(struct perf_event *event, unsigned long val, | |||
1177 | val = 0x80000000LL - left; | 1217 | val = 0x80000000LL - left; |
1178 | } | 1218 | } |
1179 | 1219 | ||
1220 | write_pmc(event->hw.idx, val); | ||
1221 | local64_set(&event->hw.prev_count, val); | ||
1222 | local64_set(&event->hw.period_left, left); | ||
1223 | perf_event_update_userpage(event); | ||
1224 | |||
1180 | /* | 1225 | /* |
1181 | * Finally record data if requested. | 1226 | * Finally record data if requested. |
1182 | */ | 1227 | */ |
@@ -1189,23 +1234,9 @@ static void record_and_restart(struct perf_event *event, unsigned long val, | |||
1189 | if (event->attr.sample_type & PERF_SAMPLE_ADDR) | 1234 | if (event->attr.sample_type & PERF_SAMPLE_ADDR) |
1190 | perf_get_data_addr(regs, &data.addr); | 1235 | perf_get_data_addr(regs, &data.addr); |
1191 | 1236 | ||
1192 | if (perf_event_overflow(event, nmi, &data, regs)) { | 1237 | if (perf_event_overflow(event, nmi, &data, regs)) |
1193 | /* | 1238 | power_pmu_stop(event, 0); |
1194 | * Interrupts are coming too fast - throttle them | ||
1195 | * by setting the event to 0, so it will be | ||
1196 | * at least 2^30 cycles until the next interrupt | ||
1197 | * (assuming each event counts at most 2 counts | ||
1198 | * per cycle). | ||
1199 | */ | ||
1200 | val = 0; | ||
1201 | left = ~0ULL >> 1; | ||
1202 | } | ||
1203 | } | 1239 | } |
1204 | |||
1205 | write_pmc(event->hw.idx, val); | ||
1206 | local64_set(&event->hw.prev_count, val); | ||
1207 | local64_set(&event->hw.period_left, left); | ||
1208 | perf_event_update_userpage(event); | ||
1209 | } | 1240 | } |
1210 | 1241 | ||
1211 | /* | 1242 | /* |