diff options
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.c | 47 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_event.h | 8 |
2 files changed, 38 insertions, 17 deletions
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 79f9f848bee4..ae407f7226c8 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -892,7 +892,6 @@ static void x86_pmu_enable(struct pmu *pmu) | |||
892 | * hw_perf_group_sched_in() or x86_pmu_enable() | 892 | * hw_perf_group_sched_in() or x86_pmu_enable() |
893 | * | 893 | * |
894 | * step1: save events moving to new counters | 894 | * step1: save events moving to new counters |
895 | * step2: reprogram moved events into new counters | ||
896 | */ | 895 | */ |
897 | for (i = 0; i < n_running; i++) { | 896 | for (i = 0; i < n_running; i++) { |
898 | event = cpuc->event_list[i]; | 897 | event = cpuc->event_list[i]; |
@@ -918,6 +917,9 @@ static void x86_pmu_enable(struct pmu *pmu) | |||
918 | x86_pmu_stop(event, PERF_EF_UPDATE); | 917 | x86_pmu_stop(event, PERF_EF_UPDATE); |
919 | } | 918 | } |
920 | 919 | ||
920 | /* | ||
921 | * step2: reprogram moved events into new counters | ||
922 | */ | ||
921 | for (i = 0; i < cpuc->n_events; i++) { | 923 | for (i = 0; i < cpuc->n_events; i++) { |
922 | event = cpuc->event_list[i]; | 924 | event = cpuc->event_list[i]; |
923 | hwc = &event->hw; | 925 | hwc = &event->hw; |
@@ -1043,7 +1045,7 @@ static int x86_pmu_add(struct perf_event *event, int flags) | |||
1043 | /* | 1045 | /* |
1044 | * If group events scheduling transaction was started, | 1046 | * If group events scheduling transaction was started, |
1045 | * skip the schedulability test here, it will be performed | 1047 | * skip the schedulability test here, it will be performed |
1046 | * at commit time (->commit_txn) as a whole | 1048 | * at commit time (->commit_txn) as a whole. |
1047 | */ | 1049 | */ |
1048 | if (cpuc->group_flag & PERF_EVENT_TXN) | 1050 | if (cpuc->group_flag & PERF_EVENT_TXN) |
1049 | goto done_collect; | 1051 | goto done_collect; |
@@ -1058,6 +1060,10 @@ static int x86_pmu_add(struct perf_event *event, int flags) | |||
1058 | memcpy(cpuc->assign, assign, n*sizeof(int)); | 1060 | memcpy(cpuc->assign, assign, n*sizeof(int)); |
1059 | 1061 | ||
1060 | done_collect: | 1062 | done_collect: |
1063 | /* | ||
1064 | * Commit the collect_events() state. See x86_pmu_del() and | ||
1065 | * x86_pmu_*_txn(). | ||
1066 | */ | ||
1061 | cpuc->n_events = n; | 1067 | cpuc->n_events = n; |
1062 | cpuc->n_added += n - n0; | 1068 | cpuc->n_added += n - n0; |
1063 | cpuc->n_txn += n - n0; | 1069 | cpuc->n_txn += n - n0; |
@@ -1183,28 +1189,38 @@ static void x86_pmu_del(struct perf_event *event, int flags) | |||
1183 | * If we're called during a txn, we don't need to do anything. | 1189 | * If we're called during a txn, we don't need to do anything. |
1184 | * The events never got scheduled and ->cancel_txn will truncate | 1190 | * The events never got scheduled and ->cancel_txn will truncate |
1185 | * the event_list. | 1191 | * the event_list. |
1192 | * | ||
1193 | * XXX assumes any ->del() called during a TXN will only be on | ||
1194 | * an event added during that same TXN. | ||
1186 | */ | 1195 | */ |
1187 | if (cpuc->group_flag & PERF_EVENT_TXN) | 1196 | if (cpuc->group_flag & PERF_EVENT_TXN) |
1188 | return; | 1197 | return; |
1189 | 1198 | ||
1199 | /* | ||
1200 | * Not a TXN, therefore cleanup properly. | ||
1201 | */ | ||
1190 | x86_pmu_stop(event, PERF_EF_UPDATE); | 1202 | x86_pmu_stop(event, PERF_EF_UPDATE); |
1191 | 1203 | ||
1192 | for (i = 0; i < cpuc->n_events; i++) { | 1204 | for (i = 0; i < cpuc->n_events; i++) { |
1193 | if (event == cpuc->event_list[i]) { | 1205 | if (event == cpuc->event_list[i]) |
1206 | break; | ||
1207 | } | ||
1194 | 1208 | ||
1195 | if (i >= cpuc->n_events - cpuc->n_added) | 1209 | if (WARN_ON_ONCE(i == cpuc->n_events)) /* called ->del() without ->add() ? */ |
1196 | --cpuc->n_added; | 1210 | return; |
1197 | 1211 | ||
1198 | if (x86_pmu.put_event_constraints) | 1212 | /* If we have a newly added event; make sure to decrease n_added. */ |
1199 | x86_pmu.put_event_constraints(cpuc, event); | 1213 | if (i >= cpuc->n_events - cpuc->n_added) |
1214 | --cpuc->n_added; | ||
1200 | 1215 | ||
1201 | while (++i < cpuc->n_events) | 1216 | if (x86_pmu.put_event_constraints) |
1202 | cpuc->event_list[i-1] = cpuc->event_list[i]; | 1217 | x86_pmu.put_event_constraints(cpuc, event); |
1218 | |||
1219 | /* Delete the array entry. */ | ||
1220 | while (++i < cpuc->n_events) | ||
1221 | cpuc->event_list[i-1] = cpuc->event_list[i]; | ||
1222 | --cpuc->n_events; | ||
1203 | 1223 | ||
1204 | --cpuc->n_events; | ||
1205 | break; | ||
1206 | } | ||
1207 | } | ||
1208 | perf_event_update_userpage(event); | 1224 | perf_event_update_userpage(event); |
1209 | } | 1225 | } |
1210 | 1226 | ||
@@ -1598,7 +1614,8 @@ static void x86_pmu_cancel_txn(struct pmu *pmu) | |||
1598 | { | 1614 | { |
1599 | __this_cpu_and(cpu_hw_events.group_flag, ~PERF_EVENT_TXN); | 1615 | __this_cpu_and(cpu_hw_events.group_flag, ~PERF_EVENT_TXN); |
1600 | /* | 1616 | /* |
1601 | * Truncate the collected events. | 1617 | * Truncate collected array by the number of events added in this |
1618 | * transaction. See x86_pmu_add() and x86_pmu_*_txn(). | ||
1602 | */ | 1619 | */ |
1603 | __this_cpu_sub(cpu_hw_events.n_added, __this_cpu_read(cpu_hw_events.n_txn)); | 1620 | __this_cpu_sub(cpu_hw_events.n_added, __this_cpu_read(cpu_hw_events.n_txn)); |
1604 | __this_cpu_sub(cpu_hw_events.n_events, __this_cpu_read(cpu_hw_events.n_txn)); | 1621 | __this_cpu_sub(cpu_hw_events.n_events, __this_cpu_read(cpu_hw_events.n_txn)); |
@@ -1609,6 +1626,8 @@ static void x86_pmu_cancel_txn(struct pmu *pmu) | |||
1609 | * Commit group events scheduling transaction | 1626 | * Commit group events scheduling transaction |
1610 | * Perform the group schedulability test as a whole | 1627 | * Perform the group schedulability test as a whole |
1611 | * Return 0 if success | 1628 | * Return 0 if success |
1629 | * | ||
1630 | * Does not cancel the transaction on failure; expects the caller to do this. | ||
1612 | */ | 1631 | */ |
1613 | static int x86_pmu_commit_txn(struct pmu *pmu) | 1632 | static int x86_pmu_commit_txn(struct pmu *pmu) |
1614 | { | 1633 | { |
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 4972c244d0bc..3b2f9bdd974b 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h | |||
@@ -130,9 +130,11 @@ struct cpu_hw_events { | |||
130 | unsigned long running[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 130 | unsigned long running[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
131 | int enabled; | 131 | int enabled; |
132 | 132 | ||
133 | int n_events; | 133 | int n_events; /* the # of events in the below arrays */ |
134 | int n_added; | 134 | int n_added; /* the # last events in the below arrays; |
135 | int n_txn; | 135 | they've never been enabled yet */ |
136 | int n_txn; /* the # last events in the below arrays; | ||
137 | added in the current transaction */ | ||
136 | int assign[X86_PMC_IDX_MAX]; /* event to counter assignment */ | 138 | int assign[X86_PMC_IDX_MAX]; /* event to counter assignment */ |
137 | u64 tags[X86_PMC_IDX_MAX]; | 139 | u64 tags[X86_PMC_IDX_MAX]; |
138 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ | 140 | struct perf_event *event_list[X86_PMC_IDX_MAX]; /* in enabled order */ |