Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 48070342 authored by Alexander Shishkin's avatar Alexander Shishkin Committed by Ingo Molnar
Browse files

perf/x86: Mark Intel PT and LBR/BTS as mutually exclusive



Intel PT cannot be used at the same time as LBR or BTS and will cause a
general protection fault if they are used together. In order to avoid
fixing up GPs in the fast path, instead we disallow creating LBR/BTS
events when PT events are present and vice versa.

Signed-off-by: default avatarAlexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Kaixu Xia <kaixu.xia@linaro.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Robert Richter <rric@kernel.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: acme@infradead.org
Cc: adrian.hunter@intel.com
Cc: kan.liang@intel.com
Cc: markus.t.metzger@intel.com
Cc: mathieu.poirier@linaro.org
Link: http://lkml.kernel.org/r/1421237903-181015-12-git-send-email-alexander.shishkin@linux.intel.com


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent ed69628b
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -263,6 +263,14 @@ static void hw_perf_event_destroy(struct perf_event *event)
	}
}

void hw_perf_lbr_event_destroy(struct perf_event *event)
{
	hw_perf_event_destroy(event);

	/* undo the lbr/bts event accounting */
	x86_del_exclusive(x86_lbr_exclusive_lbr);
}

static inline int x86_pmu_initialized(void)
{
	return x86_pmu.handle_irq != NULL;
@@ -302,6 +310,35 @@ set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event *event)
	return x86_pmu_extra_regs(val, event);
}

/*
 * Check if we can create event of a certain type (that no conflicting events
 * are present).
 */
int x86_add_exclusive(unsigned int what)
{
	int ret = -EBUSY, i;

	if (atomic_inc_not_zero(&x86_pmu.lbr_exclusive[what]))
		return 0;

	mutex_lock(&pmc_reserve_mutex);
	for (i = 0; i < ARRAY_SIZE(x86_pmu.lbr_exclusive); i++)
		if (i != what && atomic_read(&x86_pmu.lbr_exclusive[i]))
			goto out;

	atomic_inc(&x86_pmu.lbr_exclusive[what]);
	ret = 0;

out:
	mutex_unlock(&pmc_reserve_mutex);
	return ret;
}

void x86_del_exclusive(unsigned int what)
{
	atomic_dec(&x86_pmu.lbr_exclusive[what]);
}

int x86_setup_perfctr(struct perf_event *event)
{
	struct perf_event_attr *attr = &event->attr;
@@ -346,6 +383,12 @@ int x86_setup_perfctr(struct perf_event *event)
		/* BTS is currently only allowed for user-mode. */
		if (!attr->exclude_kernel)
			return -EOPNOTSUPP;

		/* disallow bts if conflicting events are present */
		if (x86_add_exclusive(x86_lbr_exclusive_lbr))
			return -EBUSY;

		event->destroy = hw_perf_lbr_event_destroy;
	}

	hwc->config |= config;
+40 −0
Original line number Diff line number Diff line
@@ -408,6 +408,12 @@ union x86_pmu_config {

#define X86_CONFIG(args...) ((union x86_pmu_config){.bits = {args}}).value

enum {
	x86_lbr_exclusive_lbr,
	x86_lbr_exclusive_pt,
	x86_lbr_exclusive_max,
};

/*
 * struct x86_pmu - generic x86 pmu
 */
@@ -505,6 +511,11 @@ struct x86_pmu {
	const int	*lbr_sel_map;		   /* lbr_select mappings */
	bool		lbr_double_abort;	   /* duplicated lbr aborts */

	/*
	 * Intel PT/LBR/BTS are exclusive
	 */
	atomic_t	lbr_exclusive[x86_lbr_exclusive_max];

	/*
	 * Extra registers for events
	 */
@@ -603,6 +614,12 @@ static inline int x86_pmu_rdpmc_index(int index)
	return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index;
}

int x86_add_exclusive(unsigned int what);

void x86_del_exclusive(unsigned int what);

void hw_perf_lbr_event_destroy(struct perf_event *event);

int x86_setup_perfctr(struct perf_event *event);

int x86_pmu_hw_config(struct perf_event *event);
@@ -689,6 +706,29 @@ static inline int amd_pmu_init(void)

#ifdef CONFIG_CPU_SUP_INTEL

static inline bool intel_pmu_needs_lbr_smpl(struct perf_event *event)
{
	/* user explicitly requested branch sampling */
	if (has_branch_stack(event))
		return true;

	/* implicit branch sampling to correct PEBS skid */
	if (x86_pmu.intel_cap.pebs_trap && event->attr.precise_ip > 1 &&
	    x86_pmu.intel_cap.pebs_format < 2)
		return true;

	return false;
}

static inline bool intel_pmu_has_bts(struct perf_event *event)
{
	if (event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS &&
	    !event->attr.freq && event->hw.sample_period == 1)
		return true;

	return false;
}

int intel_pmu_save_and_restart(struct perf_event *event);

struct event_constraint *
+11 −0
Original line number Diff line number Diff line
@@ -1942,6 +1942,17 @@ static int intel_pmu_hw_config(struct perf_event *event)
		ret = intel_pmu_setup_lbr_filter(event);
		if (ret)
			return ret;

		/*
		 * BTS is set up earlier in this path, so don't account twice
		 */
		if (!intel_pmu_has_bts(event)) {
			/* disallow lbr if conflicting events are present */
			if (x86_add_exclusive(x86_lbr_exclusive_lbr))
				return -EBUSY;

			event->destroy = hw_perf_lbr_event_destroy;
		}
	}

	if (event->attr.type != PERF_TYPE_RAW)