These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / s390 / kernel / perf_cpum_cf.c
index 56fdad4..929c147 100644 (file)
@@ -72,6 +72,7 @@ struct cpu_hw_events {
        atomic_t                ctr_set[CPUMF_CTR_SET_MAX];
        u64                     state, tx_state;
        unsigned int            flags;
+       unsigned int            txn_flags;
 };
 static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
        .ctr_set = {
@@ -82,6 +83,7 @@ static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
        },
        .state = 0,
        .flags = 0,
+       .txn_flags = 0,
 };
 
 static int get_counter_set(u64 event)
@@ -157,10 +159,14 @@ static int validate_ctr_auth(const struct hw_perf_event *hwc)
 
        cpuhw = &get_cpu_var(cpu_hw_events);
 
-       /* check authorization for cpu counter sets */
+       /* Check authorization for cpu counter sets.
+        * If the particular CPU counter set is not authorized,
+        * return with -ENOENT in order to fall back to other
+        * PMUs that might suffice the event request.
+        */
        ctrs_state = cpumf_state_ctl[hwc->config_base];
        if (!(ctrs_state & cpuhw->info.auth_ctl))
-               err = -EPERM;
+               err = -ENOENT;
 
        put_cpu_var(cpu_hw_events);
        return err;
@@ -534,9 +540,9 @@ static int cpumf_pmu_add(struct perf_event *event, int flags)
         * For group events transaction, the authorization check is
         * done in cpumf_pmu_commit_txn().
         */
-       if (!(cpuhw->flags & PERF_EVENT_TXN))
+       if (!(cpuhw->txn_flags & PERF_PMU_TXN_ADD))
                if (validate_ctr_auth(&event->hw))
-                       return -EPERM;
+                       return -ENOENT;
 
        ctr_set_enable(&cpuhw->state, event->hw.config_base);
        event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
@@ -572,13 +578,22 @@ static void cpumf_pmu_del(struct perf_event *event, int flags)
 /*
  * Start group events scheduling transaction.
  * Set flags to perform a single test at commit time.
+ *
+ * We only support PERF_PMU_TXN_ADD transactions. Save the
+ * transaction flags but otherwise ignore non-PERF_PMU_TXN_ADD
+ * transactions.
  */
-static void cpumf_pmu_start_txn(struct pmu *pmu)
+static void cpumf_pmu_start_txn(struct pmu *pmu, unsigned int txn_flags)
 {
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
+       WARN_ON_ONCE(cpuhw->txn_flags);         /* txn already in flight */
+
+       cpuhw->txn_flags = txn_flags;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        perf_pmu_disable(pmu);
-       cpuhw->flags |= PERF_EVENT_TXN;
        cpuhw->tx_state = cpuhw->state;
 }
 
@@ -589,11 +604,18 @@ static void cpumf_pmu_start_txn(struct pmu *pmu)
  */
 static void cpumf_pmu_cancel_txn(struct pmu *pmu)
 {
+       unsigned int txn_flags;
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       txn_flags = cpuhw->txn_flags;
+       cpuhw->txn_flags = 0;
+       if (txn_flags & ~PERF_PMU_TXN_ADD)
+               return;
+
        WARN_ON(cpuhw->tx_state != cpuhw->state);
 
-       cpuhw->flags &= ~PERF_EVENT_TXN;
        perf_pmu_enable(pmu);
 }
 
@@ -607,13 +629,20 @@ static int cpumf_pmu_commit_txn(struct pmu *pmu)
        struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
        u64 state;
 
+       WARN_ON_ONCE(!cpuhw->txn_flags);        /* no txn in flight */
+
+       if (cpuhw->txn_flags & ~PERF_PMU_TXN_ADD) {
+               cpuhw->txn_flags = 0;
+               return 0;
+       }
+
        /* check if the updated state can be scheduled */
        state = cpuhw->state & ~((1 << CPUMF_LCCTL_ENABLE_SHIFT) - 1);
        state >>= CPUMF_LCCTL_ENABLE_SHIFT;
        if ((state & cpuhw->info.auth_ctl) != state)
-               return -EPERM;
+               return -ENOENT;
 
-       cpuhw->flags &= ~PERF_EVENT_TXN;
+       cpuhw->txn_flags = 0;
        perf_pmu_enable(pmu);
        return 0;
 }