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/x86 | |
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/x86')
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 106 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_ds.c | 2 |
3 files changed, 63 insertions, 47 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 79705ac45019..dd6fec710677 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -583,7 +583,7 @@ static void x86_pmu_disable_all(void) | |||
583 | } | 583 | } |
584 | } | 584 | } |
585 | 585 | ||
586 | static void x86_pmu_pmu_disable(struct pmu *pmu) | 586 | static void x86_pmu_disable(struct pmu *pmu) |
587 | { | 587 | { |
588 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 588 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
589 | 589 | ||
@@ -800,10 +800,10 @@ static inline int match_prev_assignment(struct hw_perf_event *hwc, | |||
800 | hwc->last_tag == cpuc->tags[i]; | 800 | hwc->last_tag == cpuc->tags[i]; |
801 | } | 801 | } |
802 | 802 | ||
803 | static int x86_pmu_start(struct perf_event *event); | 803 | static void x86_pmu_start(struct perf_event *event, int flags); |
804 | static void x86_pmu_stop(struct perf_event *event); | 804 | static void x86_pmu_stop(struct perf_event *event, int flags); |
805 | 805 | ||
806 | static void x86_pmu_pmu_enable(struct pmu *pmu) | 806 | static void x86_pmu_enable(struct pmu *pmu) |
807 | { | 807 | { |
808 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 808 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
809 | struct perf_event *event; | 809 | struct perf_event *event; |
@@ -839,7 +839,14 @@ static void x86_pmu_pmu_enable(struct pmu *pmu) | |||
839 | match_prev_assignment(hwc, cpuc, i)) | 839 | match_prev_assignment(hwc, cpuc, i)) |
840 | continue; | 840 | continue; |
841 | 841 | ||
842 | x86_pmu_stop(event); | 842 | /* |
843 | * Ensure we don't accidentally enable a stopped | ||
844 | * counter simply because we rescheduled. | ||
845 | */ | ||
846 | if (hwc->state & PERF_HES_STOPPED) | ||
847 | hwc->state |= PERF_HES_ARCH; | ||
848 | |||
849 | x86_pmu_stop(event, PERF_EF_UPDATE); | ||
843 | } | 850 | } |
844 | 851 | ||
845 | for (i = 0; i < cpuc->n_events; i++) { | 852 | for (i = 0; i < cpuc->n_events; i++) { |
@@ -851,7 +858,10 @@ static void x86_pmu_pmu_enable(struct pmu *pmu) | |||
851 | else if (i < n_running) | 858 | else if (i < n_running) |
852 | continue; | 859 | continue; |
853 | 860 | ||
854 | x86_pmu_start(event); | 861 | if (hwc->state & PERF_HES_ARCH) |
862 | continue; | ||
863 | |||
864 | x86_pmu_start(event, PERF_EF_RELOAD); | ||
855 | } | 865 | } |
856 | cpuc->n_added = 0; | 866 | cpuc->n_added = 0; |
857 | perf_events_lapic_init(); | 867 | perf_events_lapic_init(); |
@@ -952,15 +962,12 @@ static void x86_pmu_enable_event(struct perf_event *event) | |||
952 | } | 962 | } |
953 | 963 | ||
954 | /* | 964 | /* |
955 | * activate a single event | 965 | * Add a single event to the PMU. |
956 | * | 966 | * |
957 | * The event is added to the group of enabled events | 967 | * The event is added to the group of enabled events |
958 | * but only if it can be scehduled with existing events. | 968 | * but only if it can be scehduled with existing events. |
959 | * | ||
960 | * Called with PMU disabled. If successful and return value 1, | ||
961 | * then guaranteed to call perf_enable() and hw_perf_enable() | ||
962 | */ | 969 | */ |
963 | static int x86_pmu_enable(struct perf_event *event) | 970 | static int x86_pmu_add(struct perf_event *event, int flags) |
964 | { | 971 | { |
965 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 972 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
966 | struct hw_perf_event *hwc; | 973 | struct hw_perf_event *hwc; |
@@ -975,10 +982,14 @@ static int x86_pmu_enable(struct perf_event *event) | |||
975 | if (ret < 0) | 982 | if (ret < 0) |
976 | goto out; | 983 | goto out; |
977 | 984 | ||
985 | hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; | ||
986 | if (!(flags & PERF_EF_START)) | ||
987 | hwc->state |= PERF_HES_ARCH; | ||
988 | |||
978 | /* | 989 | /* |
979 | * If group events scheduling transaction was started, | 990 | * If group events scheduling transaction was started, |
980 | * skip the schedulability test here, it will be peformed | 991 | * skip the schedulability test here, it will be peformed |
981 | * at commit time(->commit_txn) as a whole | 992 | * at commit time (->commit_txn) as a whole |
982 | */ | 993 | */ |
983 | if (cpuc->group_flag & PERF_EVENT_TXN) | 994 | if (cpuc->group_flag & PERF_EVENT_TXN) |
984 | goto done_collect; | 995 | goto done_collect; |
@@ -1003,27 +1014,28 @@ out: | |||
1003 | return ret; | 1014 | return ret; |
1004 | } | 1015 | } |
1005 | 1016 | ||
1006 | static int x86_pmu_start(struct perf_event *event) | 1017 | static void x86_pmu_start(struct perf_event *event, int flags) |
1007 | { | 1018 | { |
1008 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 1019 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
1009 | int idx = event->hw.idx; | 1020 | int idx = event->hw.idx; |
1010 | 1021 | ||
1011 | if (idx == -1) | 1022 | if (WARN_ON_ONCE(!(event->hw.state & PERF_HES_STOPPED))) |
1012 | return -EAGAIN; | 1023 | return; |
1024 | |||
1025 | if (WARN_ON_ONCE(idx == -1)) | ||
1026 | return; | ||
1027 | |||
1028 | if (flags & PERF_EF_RELOAD) { | ||
1029 | WARN_ON_ONCE(!(event->hw.state & PERF_HES_UPTODATE)); | ||
1030 | x86_perf_event_set_period(event); | ||
1031 | } | ||
1032 | |||
1033 | event->hw.state = 0; | ||
1013 | 1034 | ||
1014 | x86_perf_event_set_period(event); | ||
1015 | cpuc->events[idx] = event; | 1035 | cpuc->events[idx] = event; |
1016 | __set_bit(idx, cpuc->active_mask); | 1036 | __set_bit(idx, cpuc->active_mask); |
1017 | x86_pmu.enable(event); | 1037 | x86_pmu.enable(event); |
1018 | perf_event_update_userpage(event); | 1038 | perf_event_update_userpage(event); |
1019 | |||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | static void x86_pmu_unthrottle(struct perf_event *event) | ||
1024 | { | ||
1025 | int ret = x86_pmu_start(event); | ||
1026 | WARN_ON_ONCE(ret); | ||
1027 | } | 1039 | } |
1028 | 1040 | ||
1029 | void perf_event_print_debug(void) | 1041 | void perf_event_print_debug(void) |
@@ -1080,27 +1092,29 @@ void perf_event_print_debug(void) | |||
1080 | local_irq_restore(flags); | 1092 | local_irq_restore(flags); |
1081 | } | 1093 | } |
1082 | 1094 | ||
1083 | static void x86_pmu_stop(struct perf_event *event) | 1095 | static void x86_pmu_stop(struct perf_event *event, int flags) |
1084 | { | 1096 | { |
1085 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 1097 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
1086 | struct hw_perf_event *hwc = &event->hw; | 1098 | struct hw_perf_event *hwc = &event->hw; |
1087 | int idx = hwc->idx; | ||
1088 | |||
1089 | if (!__test_and_clear_bit(idx, cpuc->active_mask)) | ||
1090 | return; | ||
1091 | |||
1092 | x86_pmu.disable(event); | ||
1093 | 1099 | ||
1094 | /* | 1100 | if (__test_and_clear_bit(hwc->idx, cpuc->active_mask)) { |
1095 | * Drain the remaining delta count out of a event | 1101 | x86_pmu.disable(event); |
1096 | * that we are disabling: | 1102 | cpuc->events[hwc->idx] = NULL; |
1097 | */ | 1103 | WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); |
1098 | x86_perf_event_update(event); | 1104 | hwc->state |= PERF_HES_STOPPED; |
1105 | } | ||
1099 | 1106 | ||
1100 | cpuc->events[idx] = NULL; | 1107 | if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) { |
1108 | /* | ||
1109 | * Drain the remaining delta count out of a event | ||
1110 | * that we are disabling: | ||
1111 | */ | ||
1112 | x86_perf_event_update(event); | ||
1113 | hwc->state |= PERF_HES_UPTODATE; | ||
1114 | } | ||
1101 | } | 1115 | } |
1102 | 1116 | ||
1103 | static void x86_pmu_disable(struct perf_event *event) | 1117 | static void x86_pmu_del(struct perf_event *event, int flags) |
1104 | { | 1118 | { |
1105 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 1119 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
1106 | int i; | 1120 | int i; |
@@ -1113,7 +1127,7 @@ static void x86_pmu_disable(struct perf_event *event) | |||
1113 | if (cpuc->group_flag & PERF_EVENT_TXN) | 1127 | if (cpuc->group_flag & PERF_EVENT_TXN) |
1114 | return; | 1128 | return; |
1115 | 1129 | ||
1116 | x86_pmu_stop(event); | 1130 | x86_pmu_stop(event, PERF_EF_UPDATE); |
1117 | 1131 | ||
1118 | for (i = 0; i < cpuc->n_events; i++) { | 1132 | for (i = 0; i < cpuc->n_events; i++) { |
1119 | if (event == cpuc->event_list[i]) { | 1133 | if (event == cpuc->event_list[i]) { |
@@ -1165,7 +1179,7 @@ static int x86_pmu_handle_irq(struct pt_regs *regs) | |||
1165 | continue; | 1179 | continue; |
1166 | 1180 | ||
1167 | if (perf_event_overflow(event, 1, &data, regs)) | 1181 | if (perf_event_overflow(event, 1, &data, regs)) |
1168 | x86_pmu_stop(event); | 1182 | x86_pmu_stop(event, 0); |
1169 | } | 1183 | } |
1170 | 1184 | ||
1171 | if (handled) | 1185 | if (handled) |
@@ -1605,15 +1619,17 @@ int x86_pmu_event_init(struct perf_event *event) | |||
1605 | } | 1619 | } |
1606 | 1620 | ||
1607 | static struct pmu pmu = { | 1621 | static struct pmu pmu = { |
1608 | .pmu_enable = x86_pmu_pmu_enable, | 1622 | .pmu_enable = x86_pmu_enable, |
1609 | .pmu_disable = x86_pmu_pmu_disable, | 1623 | .pmu_disable = x86_pmu_disable, |
1624 | |||
1610 | .event_init = x86_pmu_event_init, | 1625 | .event_init = x86_pmu_event_init, |
1611 | .enable = x86_pmu_enable, | 1626 | |
1612 | .disable = x86_pmu_disable, | 1627 | .add = x86_pmu_add, |
1628 | .del = x86_pmu_del, | ||
1613 | .start = x86_pmu_start, | 1629 | .start = x86_pmu_start, |
1614 | .stop = x86_pmu_stop, | 1630 | .stop = x86_pmu_stop, |
1615 | .read = x86_pmu_read, | 1631 | .read = x86_pmu_read, |
1616 | .unthrottle = x86_pmu_unthrottle, | 1632 | |
1617 | .start_txn = x86_pmu_start_txn, | 1633 | .start_txn = x86_pmu_start_txn, |
1618 | .cancel_txn = x86_pmu_cancel_txn, | 1634 | .cancel_txn = x86_pmu_cancel_txn, |
1619 | .commit_txn = x86_pmu_commit_txn, | 1635 | .commit_txn = x86_pmu_commit_txn, |
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index ee05c90012d2..82395f2378ec 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c | |||
@@ -763,7 +763,7 @@ again: | |||
763 | data.period = event->hw.last_period; | 763 | data.period = event->hw.last_period; |
764 | 764 | ||
765 | if (perf_event_overflow(event, 1, &data, regs)) | 765 | if (perf_event_overflow(event, 1, &data, regs)) |
766 | x86_pmu_stop(event); | 766 | x86_pmu_stop(event, 0); |
767 | } | 767 | } |
768 | 768 | ||
769 | /* | 769 | /* |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 18018d1311cd..9893a2f77b7a 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c | |||
@@ -491,7 +491,7 @@ static void __intel_pmu_pebs_event(struct perf_event *event, | |||
491 | regs.flags &= ~PERF_EFLAGS_EXACT; | 491 | regs.flags &= ~PERF_EFLAGS_EXACT; |
492 | 492 | ||
493 | if (perf_event_overflow(event, 1, &data, ®s)) | 493 | if (perf_event_overflow(event, 1, &data, ®s)) |
494 | x86_pmu_stop(event); | 494 | x86_pmu_stop(event, 0); |
495 | } | 495 | } |
496 | 496 | ||
497 | static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) | 497 | static void intel_pmu_drain_pebs_core(struct pt_regs *iregs) |