|
@@ -56,6 +56,10 @@ enum {
|
|
|
|
|
|
#define LBR_FROM_FLAG_MISPRED (1ULL << 63)
|
|
|
|
|
|
+#define for_each_branch_sample_type(x) \
|
|
|
+ for ((x) = PERF_SAMPLE_BRANCH_USER; \
|
|
|
+ (x) < PERF_SAMPLE_BRANCH_MAX; (x) <<= 1)
|
|
|
+
|
|
|
/*
|
|
|
* We only support LBR implementations that have FREEZE_LBRS_ON_PMI
|
|
|
* otherwise it becomes near impossible to get a reliable stack.
|
|
@@ -64,6 +68,10 @@ enum {
|
|
|
static void __intel_pmu_lbr_enable(void)
|
|
|
{
|
|
|
u64 debugctl;
|
|
|
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
|
|
|
+
|
|
|
+ if (cpuc->lbr_sel)
|
|
|
+ wrmsrl(MSR_LBR_SELECT, cpuc->lbr_sel->config);
|
|
|
|
|
|
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl);
|
|
|
debugctl |= (DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI);
|
|
@@ -119,7 +127,6 @@ void intel_pmu_lbr_enable(struct perf_event *event)
|
|
|
* Reset the LBR stack if we changed task context to
|
|
|
* avoid data leaks.
|
|
|
*/
|
|
|
-
|
|
|
if (event->ctx->task && cpuc->lbr_context != event->ctx) {
|
|
|
intel_pmu_lbr_reset();
|
|
|
cpuc->lbr_context = event->ctx;
|
|
@@ -138,8 +145,11 @@ void intel_pmu_lbr_disable(struct perf_event *event)
|
|
|
cpuc->lbr_users--;
|
|
|
WARN_ON_ONCE(cpuc->lbr_users < 0);
|
|
|
|
|
|
- if (cpuc->enabled && !cpuc->lbr_users)
|
|
|
+ if (cpuc->enabled && !cpuc->lbr_users) {
|
|
|
__intel_pmu_lbr_disable();
|
|
|
+ /* avoid stale pointer */
|
|
|
+ cpuc->lbr_context = NULL;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void intel_pmu_lbr_enable_all(void)
|
|
@@ -158,6 +168,9 @@ void intel_pmu_lbr_disable_all(void)
|
|
|
__intel_pmu_lbr_disable();
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * TOS = most recently recorded branch
|
|
|
+ */
|
|
|
static inline u64 intel_pmu_lbr_tos(void)
|
|
|
{
|
|
|
u64 tos;
|
|
@@ -241,6 +254,75 @@ void intel_pmu_lbr_read(void)
|
|
|
intel_pmu_lbr_read_64(cpuc);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * setup the HW LBR filter
|
|
|
+ * Used only when available, may not be enough to disambiguate
|
|
|
+ * all branches, may need the help of the SW filter
|
|
|
+ */
|
|
|
+static int intel_pmu_setup_hw_lbr_filter(struct perf_event *event)
|
|
|
+{
|
|
|
+ struct hw_perf_event_extra *reg;
|
|
|
+ u64 br_type = event->attr.branch_sample_type;
|
|
|
+ u64 mask = 0, m;
|
|
|
+ u64 v;
|
|
|
+
|
|
|
+ for_each_branch_sample_type(m) {
|
|
|
+ if (!(br_type & m))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ v = x86_pmu.lbr_sel_map[m];
|
|
|
+ if (v == LBR_NOT_SUPP)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ mask |= v;
|
|
|
+
|
|
|
+ if (m == PERF_SAMPLE_BRANCH_ANY)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ reg = &event->hw.branch_reg;
|
|
|
+ reg->idx = EXTRA_REG_LBR;
|
|
|
+
|
|
|
+ /* LBR_SELECT operates in suppress mode so invert mask */
|
|
|
+ reg->config = ~mask & x86_pmu.lbr_sel_mask;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * all the bits supported on some flavor of x86LBR
|
|
|
+ * we ignore BRANCH_HV because it is not supported
|
|
|
+ */
|
|
|
+#define PERF_SAMPLE_BRANCH_X86_ALL \
|
|
|
+ (PERF_SAMPLE_BRANCH_ANY |\
|
|
|
+ PERF_SAMPLE_BRANCH_USER |\
|
|
|
+ PERF_SAMPLE_BRANCH_KERNEL)
|
|
|
+
|
|
|
+int intel_pmu_setup_lbr_filter(struct perf_event *event)
|
|
|
+{
|
|
|
+ u64 br_type = event->attr.branch_sample_type;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * no LBR on this PMU
|
|
|
+ */
|
|
|
+ if (!x86_pmu.lbr_nr)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * if no LBR HW filter, users can only
|
|
|
+ * capture all branches
|
|
|
+ */
|
|
|
+ if (!x86_pmu.lbr_sel_map) {
|
|
|
+ if (br_type != PERF_SAMPLE_BRANCH_X86_ALL)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * we ignore branch priv levels we do not
|
|
|
+ * know about: BRANCH_HV
|
|
|
+ */
|
|
|
+
|
|
|
+ return intel_pmu_setup_hw_lbr_filter(event);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Map interface branch filters onto LBR filters
|
|
|
*/
|