perf/x86: Fix shared register mutual exclusion enforcement
[firefly-linux-kernel-4.4.55.git] / arch / x86 / kernel / cpu / perf_event.c
index afc2413ba00c53c58e3714d0f31b325e05a973db..9e581c5cf6d0db026e524a1dd881abe6cc61e7bb 100644 (file)
@@ -726,6 +726,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
 {
        struct event_constraint *c;
        unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
+       struct perf_event *e;
        int i, wmin, wmax, num = 0;
        struct hw_perf_event *hwc;
 
@@ -769,14 +770,32 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
                num = perf_assign_events(cpuc->event_list, n, wmin,
                                         wmax, assign);
 
+       /*
+        * Mark the event as committed, so we do not put_constraint()
+        * in case new events are added and fail scheduling.
+        */
+       if (!num && assign) {
+               for (i = 0; i < n; i++) {
+                       e = cpuc->event_list[i];
+                       e->hw.flags |= PERF_X86_EVENT_COMMITTED;
+               }
+       }
        /*
         * scheduling failed or is just a simulation,
         * free resources if necessary
         */
        if (!assign || num) {
                for (i = 0; i < n; i++) {
+                       e = cpuc->event_list[i];
+                       /*
+                        * do not put_constraint() on comitted events,
+                        * because they are good to go
+                        */
+                       if ((e->hw.flags & PERF_X86_EVENT_COMMITTED))
+                               continue;
+
                        if (x86_pmu.put_event_constraints)
-                               x86_pmu.put_event_constraints(cpuc, cpuc->event_list[i]);
+                               x86_pmu.put_event_constraints(cpuc, e);
                }
        }
        return num ? -EINVAL : 0;
@@ -1155,6 +1174,11 @@ static void x86_pmu_del(struct perf_event *event, int flags)
        struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
        int i;
 
+       /*
+        * event is descheduled
+        */
+       event->hw.flags &= ~PERF_X86_EVENT_COMMITTED;
+
        /*
         * If we're called during a txn, we don't need to do anything.
         * The events never got scheduled and ->cancel_txn will truncate