From: Andi Kleen <a...@linux.intel.com>

Add LBR filtering for branch in transaction, branch not in transaction
or transaction abort. This is exposed as new sample types.

v2: Rename ABORT to ABORTTX
v3: Use table instead of if
Signed-off-by: Andi Kleen <a...@linux.intel.com>
---
 arch/x86/kernel/cpu/perf_event_intel_lbr.c |   58 ++++++++++++++++++++++------
 include/uapi/linux/perf_event.h            |    5 ++-
 2 files changed, 50 insertions(+), 13 deletions(-)

diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c 
b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
index ad5af13..0be69fd 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
@@ -16,6 +16,14 @@ enum {
        LBR_FORMAT_MAX_KNOWN    = LBR_FORMAT_EIP_FLAGS2,
 };
 
+static enum {
+       LBR_EIP_FLAGS           = 1,
+       LBR_TSX                 = 2,
+} lbr_desc[LBR_FORMAT_MAX_KNOWN + 1] = {
+       [LBR_FORMAT_EIP_FLAGS]  = LBR_EIP_FLAGS,
+       [LBR_FORMAT_EIP_FLAGS2] = LBR_EIP_FLAGS | LBR_TSX,
+};
+
 /*
  * Intel LBR_SELECT bits
  * Intel Vol3a, April 2011, Section 16.7 Table 16-10
@@ -85,9 +93,13 @@ enum {
        X86_BR_JMP      = 1 << 9, /* jump */
        X86_BR_IRQ      = 1 << 10,/* hw interrupt or trap or fault */
        X86_BR_IND_CALL = 1 << 11,/* indirect calls */
+       X86_BR_ABORT    = 1 << 12,/* transaction abort */
+       X86_BR_INTX     = 1 << 13,/* in transaction */
+       X86_BR_NOTX     = 1 << 14,/* not in transaction */
 };
 
 #define X86_BR_PLM (X86_BR_USER | X86_BR_KERNEL)
+#define X86_BR_ANYTX (X86_BR_NOTX | X86_BR_INTX)
 
 #define X86_BR_ANY       \
        (X86_BR_CALL    |\
@@ -99,6 +111,7 @@ enum {
         X86_BR_JCC     |\
         X86_BR_JMP      |\
         X86_BR_IRQ      |\
+        X86_BR_ABORT    |\
         X86_BR_IND_CALL)
 
 #define X86_BR_ALL (X86_BR_PLM | X86_BR_ANY)
@@ -275,22 +288,23 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events 
*cpuc)
        for (i = 0; i < x86_pmu.lbr_nr; i++) {
                unsigned long lbr_idx = (tos - i) & mask;
                u64 from, to, mis = 0, pred = 0, intx = 0, abort = 0;
+               int skip = 0;
+               int lbr_flags = lbr_desc[lbr_format];
 
                rdmsrl(x86_pmu.lbr_from + lbr_idx, from);
                rdmsrl(x86_pmu.lbr_to   + lbr_idx, to);
 
-               if (lbr_format == LBR_FORMAT_EIP_FLAGS ||
-                   lbr_format == LBR_FORMAT_EIP_FLAGS2) {
+               if (lbr_flags & LBR_EIP_FLAGS) {
                        mis = !!(from & LBR_FROM_FLAG_MISPRED);
                        pred = !mis;
-                       if (lbr_format == LBR_FORMAT_EIP_FLAGS)
-                               from = (u64)((((s64)from) << 1) >> 1);
-                       else if (lbr_format == LBR_FORMAT_EIP_FLAGS2) {
-                               intx = !!(from & LBR_FROM_FLAG_INTX);
-                               abort = !!(from & LBR_FROM_FLAG_ABORT);
-                               from = (u64)((((s64)from) << 3) >> 3);
-                       }
+                       skip = 1;
+               }
+               if (lbr_flags & LBR_TSX) {
+                       intx = !!(from & LBR_FROM_FLAG_INTX);
+                       abort = !!(from & LBR_FROM_FLAG_ABORT);
+                       skip = 3;
                }
+               from = (u64)((((s64)from) << skip) >> skip);
 
                cpuc->lbr_entries[i].from       = from;
                cpuc->lbr_entries[i].to         = to;
@@ -347,6 +361,16 @@ static void intel_pmu_setup_sw_lbr_filter(struct 
perf_event *event)
 
        if (br_type & PERF_SAMPLE_BRANCH_IND_CALL)
                mask |= X86_BR_IND_CALL;
+
+       if (br_type & PERF_SAMPLE_BRANCH_ABORTTX)
+               mask |= X86_BR_ABORT;
+
+       if (br_type & PERF_SAMPLE_BRANCH_INTX)
+               mask |= X86_BR_INTX;
+
+       if (br_type & PERF_SAMPLE_BRANCH_NOTX)
+               mask |= X86_BR_NOTX;
+
        /*
         * stash actual user request into reg, it may
         * be used by fixup code for some CPU
@@ -393,7 +417,8 @@ int intel_pmu_setup_lbr_filter(struct perf_event *event)
        /*
         * no LBR on this PMU
         */
-       if (!x86_pmu.lbr_nr || x86_pmu.intel_cap.lbr_format > 
LBR_FORMAT_MAX_KNOWN)
+       if (!x86_pmu.lbr_nr ||
+           x86_pmu.intel_cap.lbr_format > LBR_FORMAT_MAX_KNOWN)
                return -EOPNOTSUPP;
 
        /*
@@ -421,7 +446,7 @@ int intel_pmu_setup_lbr_filter(struct perf_event *event)
  * decoded (e.g., text page not present), then X86_BR_NONE is
  * returned.
  */
-static int branch_type(unsigned long from, unsigned long to)
+static int branch_type(unsigned long from, unsigned long to, int abort)
 {
        struct insn insn;
        void *addr;
@@ -441,6 +466,9 @@ static int branch_type(unsigned long from, unsigned long to)
        if (from == 0 || to == 0)
                return X86_BR_NONE;
 
+       if (abort)
+               return X86_BR_ABORT | to_plm;
+
        if (from_plm == X86_BR_USER) {
                /*
                 * can happen if measuring at the user level only
@@ -577,7 +605,13 @@ intel_pmu_lbr_filter(struct cpu_hw_events *cpuc)
                from = cpuc->lbr_entries[i].from;
                to = cpuc->lbr_entries[i].to;
 
-               type = branch_type(from, to);
+               type = branch_type(from, to, cpuc->lbr_entries[i].abort);
+               if (type != X86_BR_NONE && (br_sel & X86_BR_ANYTX)) {
+                       if (cpuc->lbr_entries[i].intx)
+                               type |= X86_BR_INTX;
+                       else
+                               type |= X86_BR_NOTX;
+               }
 
                /* if type does not correspond, then discard */
                if (type == X86_BR_NONE || (br_sel & type) != type) {
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h
index 9fa9c62..41b25f0 100644
--- a/include/uapi/linux/perf_event.h
+++ b/include/uapi/linux/perf_event.h
@@ -155,8 +155,11 @@ enum perf_branch_sample_type {
        PERF_SAMPLE_BRANCH_ANY_CALL     = 1U << 4, /* any call branch */
        PERF_SAMPLE_BRANCH_ANY_RETURN   = 1U << 5, /* any return branch */
        PERF_SAMPLE_BRANCH_IND_CALL     = 1U << 6, /* indirect calls */
+       PERF_SAMPLE_BRANCH_ABORTTX      = 1U << 7, /* transaction aborts */
+       PERF_SAMPLE_BRANCH_INTX         = 1U << 8, /* in transaction (flag) */
+       PERF_SAMPLE_BRANCH_NOTX         = 1U << 9, /* not in transaction (flag) 
*/
 
-       PERF_SAMPLE_BRANCH_MAX          = 1U << 7, /* non-ABI */
+       PERF_SAMPLE_BRANCH_MAX          = 1U << 10, /* non-ABI */
 };
 
 #define PERF_SAMPLE_BRANCH_PLM_ALL \
-- 
1.7.7.6

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to