diff options
Diffstat (limited to 'arch/x86/kernel/cpu/perf_event_intel_lbr.c')
| -rw-r--r-- | arch/x86/kernel/cpu/perf_event_intel_lbr.c | 69 | 
1 files changed, 54 insertions, 15 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c index d978353c939b..d5be06a5005e 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c +++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c @@ -12,6 +12,16 @@ enum {  	LBR_FORMAT_LIP		= 0x01,  	LBR_FORMAT_EIP		= 0x02,  	LBR_FORMAT_EIP_FLAGS	= 0x03, +	LBR_FORMAT_EIP_FLAGS2	= 0x04, +	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,  };  /* @@ -56,6 +66,8 @@ enum {  	 LBR_FAR)  #define LBR_FROM_FLAG_MISPRED  (1ULL << 63) +#define LBR_FROM_FLAG_IN_TX    (1ULL << 62) +#define LBR_FROM_FLAG_ABORT    (1ULL << 61)  #define for_each_branch_sample_type(x) \  	for ((x) = PERF_SAMPLE_BRANCH_USER; \ @@ -81,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_IN_TX    = 1 << 13,/* in transaction */ +	X86_BR_NO_TX    = 1 << 14,/* not in transaction */  };  #define X86_BR_PLM (X86_BR_USER | X86_BR_KERNEL) +#define X86_BR_ANYTX (X86_BR_NO_TX | X86_BR_IN_TX)  #define X86_BR_ANY       \  	(X86_BR_CALL    |\ @@ -95,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) @@ -270,21 +287,31 @@ 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; +		u64 from, to, mis = 0, pred = 0, in_tx = 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) { +		if (lbr_flags & LBR_EIP_FLAGS) {  			mis = !!(from & LBR_FROM_FLAG_MISPRED);  			pred = !mis; -			from = (u64)((((s64)from) << 1) >> 1); +			skip = 1; +		} +		if (lbr_flags & LBR_TSX) { +			in_tx = !!(from & LBR_FROM_FLAG_IN_TX); +			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;  		cpuc->lbr_entries[i].mispred	= mis;  		cpuc->lbr_entries[i].predicted	= pred; +		cpuc->lbr_entries[i].in_tx	= in_tx; +		cpuc->lbr_entries[i].abort	= abort;  		cpuc->lbr_entries[i].reserved	= 0;  	}  	cpuc->lbr_stack.nr = i; @@ -310,7 +337,7 @@ void intel_pmu_lbr_read(void)   * - in case there is no HW filter   * - in case the HW filter has errata or limitations   */ -static int intel_pmu_setup_sw_lbr_filter(struct perf_event *event) +static void intel_pmu_setup_sw_lbr_filter(struct perf_event *event)  {  	u64 br_type = event->attr.branch_sample_type;  	int mask = 0; @@ -318,11 +345,8 @@ static int intel_pmu_setup_sw_lbr_filter(struct perf_event *event)  	if (br_type & PERF_SAMPLE_BRANCH_USER)  		mask |= X86_BR_USER; -	if (br_type & PERF_SAMPLE_BRANCH_KERNEL) { -		if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) -			return -EACCES; +	if (br_type & PERF_SAMPLE_BRANCH_KERNEL)  		mask |= X86_BR_KERNEL; -	}  	/* we ignore BRANCH_HV here */ @@ -337,13 +361,21 @@ static int 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_ABORT_TX) +		mask |= X86_BR_ABORT; + +	if (br_type & PERF_SAMPLE_BRANCH_IN_TX) +		mask |= X86_BR_IN_TX; + +	if (br_type & PERF_SAMPLE_BRANCH_NO_TX) +		mask |= X86_BR_NO_TX; +  	/*  	 * stash actual user request into reg, it may  	 * be used by fixup code for some CPU  	 */  	event->hw.branch_reg.reg = mask; - -	return 0;  }  /* @@ -391,9 +423,7 @@ int intel_pmu_setup_lbr_filter(struct perf_event *event)  	/*  	 * setup SW LBR filter  	 */ -	ret = intel_pmu_setup_sw_lbr_filter(event); -	if (ret) -		return ret; +	intel_pmu_setup_sw_lbr_filter(event);  	/*  	 * setup HW LBR filter, if any @@ -415,7 +445,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; @@ -435,6 +465,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 @@ -581,7 +614,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].in_tx) +				type |= X86_BR_IN_TX; +			else +				type |= X86_BR_NO_TX; +		}  		/* if type does not correspond, then discard */  		if (type == X86_BR_NONE || (br_sel & type) != type) {  | 
