aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/perf_event.c
diff options
context:
space:
mode:
authorStephane Eranian <eranian@google.com>2010-05-25 10:23:10 -0400
committerIngo Molnar <mingo@elte.hu>2010-05-31 02:46:10 -0400
commit90151c35b19633e0cab5a6c80f1ba4a51e7c913b (patch)
tree448c86520eef5b9dc0f06c59a8a96abfd4096fab /kernel/perf_event.c
parent2e97942fe57864588774f173cf4cd7bb68968b76 (diff)
perf_events: Fix event scheduling issues introduced by transactional API
The transactional API patch between the generic and model-specific code introduced several important bugs with event scheduling, at least on X86. If you had pinned events, e.g., watchdog, and were over-committing the PMU, you would get bogus counts. The bug was showing up on Intel CPU because events would move around more often that on AMD. But the problem also existed on AMD, though harder to expose. The issues were: - group_sched_in() was missing a cancel_txn() in the error path - cpuc->n_added was not properly maintained, leading to missing actions in hw_perf_enable(), i.e., n_running being 0. You cannot update n_added until you know the transaction has succeeded. In case of failed transaction n_added was not adjusted back. - in case of failed transactions, event_sched_out() was called and eventually invoked x86_disable_event() to touch the HW reg. But with transactions, on X86, event_sched_in() does not touch HW registers, it simply collects events into a list. Thus, you could end up calling x86_disable_event() on a counter which did not correspond to the current event when idx != -1. The patch modifies the generic and X86 code to avoid all those problems. First, we keep track of the number of events added last. In case the transaction fails, we substract them from n_added. This approach is necessary (as opposed to delaying updates to n_added) because not all event updates use the transaction API, e.g., single events. Second, we encapsulate the event_sched_in() and event_sched_out() in group_sched_in() inside the transaction. That makes the operations symmetrical and you can also detect that you are inside a transaction and skip the HW reg access by checking cpuc->group_flag. With this patch, you can now overcommit the PMU even with pinned system-wide events present and still get valid counts. Signed-off-by: Stephane Eranian <eranian@google.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> LKML-Reference: <1274796225.5882.1389.camel@twins> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/perf_event.c')
-rw-r--r--kernel/perf_event.c11
1 files changed, 7 insertions, 4 deletions
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index 10a1aee2309e..42a0e9191af5 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -687,8 +687,11 @@ group_sched_in(struct perf_event *group_event,
687 if (txn) 687 if (txn)
688 pmu->start_txn(pmu); 688 pmu->start_txn(pmu);
689 689
690 if (event_sched_in(group_event, cpuctx, ctx)) 690 if (event_sched_in(group_event, cpuctx, ctx)) {
691 if (txn)
692 pmu->cancel_txn(pmu);
691 return -EAGAIN; 693 return -EAGAIN;
694 }
692 695
693 /* 696 /*
694 * Schedule in siblings as one group (if any): 697 * Schedule in siblings as one group (if any):
@@ -710,9 +713,6 @@ group_sched_in(struct perf_event *group_event,
710 } 713 }
711 714
712group_error: 715group_error:
713 if (txn)
714 pmu->cancel_txn(pmu);
715
716 /* 716 /*
717 * Groups can be scheduled in as one unit only, so undo any 717 * Groups can be scheduled in as one unit only, so undo any
718 * partial group before returning: 718 * partial group before returning:
@@ -724,6 +724,9 @@ group_error:
724 } 724 }
725 event_sched_out(group_event, cpuctx, ctx); 725 event_sched_out(group_event, cpuctx, ctx);
726 726
727 if (txn)
728 pmu->cancel_txn(pmu);
729
727 return -EAGAIN; 730 return -EAGAIN;
728} 731}
729 732