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

Commit 424646ee authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Ingo Molnar
Browse files

x86/perf/intel/cstate: Sanitize probing



The whole probing functionality can simply be expressed with model matching
and a bunch of structures describing the variants. This is a first step to
make that driver modular.

While at it, get rid of completely pointless comments and name the enums so
they are self explaining.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
[ Reworked probing to clear msr[].attr for all !present msrs. ]
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Vince Weaver <vincent.weaver@maine.edu>
Link: http://lkml.kernel.org/r/20160320185623.500381872@linutronix.de


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 49de0493
Loading
Loading
Loading
Loading
+160 −199
Original line number Diff line number Diff line
@@ -106,22 +106,27 @@ static ssize_t cstate_get_attr_cpumask(struct device *dev,
				       struct device_attribute *attr,
				       char *buf);

/* Model -> events mapping */
struct cstate_model {
	unsigned long		core_events;
	unsigned long		pkg_events;
	unsigned long		quirks;
};

/* Quirk flags */
#define SLM_PKG_C6_USE_C7_MSR	(1UL << 0)

struct perf_cstate_msr {
	u64	msr;
	struct	perf_pmu_events_attr *attr;
	bool	(*test)(int idx);
};


/* cstate_core PMU */

static struct pmu cstate_core_pmu;
static bool has_cstate_core;

enum perf_cstate_core_id {
	/*
	 * cstate_core events
	 */
enum perf_cstate_core_events {
	PERF_CSTATE_CORE_C1_RES = 0,
	PERF_CSTATE_CORE_C3_RES,
	PERF_CSTATE_CORE_C6_RES,
@@ -130,69 +135,16 @@ enum perf_cstate_core_id {
	PERF_CSTATE_CORE_EVENT_MAX,
};

bool test_core(int idx)
{
	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
	    boot_cpu_data.x86 != 6)
		return false;

	switch (boot_cpu_data.x86_model) {
	case 30: /* 45nm Nehalem    */
	case 26: /* 45nm Nehalem-EP */
	case 46: /* 45nm Nehalem-EX */

	case 37: /* 32nm Westmere    */
	case 44: /* 32nm Westmere-EP */
	case 47: /* 32nm Westmere-EX */
		if (idx == PERF_CSTATE_CORE_C3_RES ||
		    idx == PERF_CSTATE_CORE_C6_RES)
			return true;
		break;
	case 42: /* 32nm SandyBridge         */
	case 45: /* 32nm SandyBridge-E/EN/EP */

	case 58: /* 22nm IvyBridge       */
	case 62: /* 22nm IvyBridge-EP/EX */

	case 60: /* 22nm Haswell Core */
	case 63: /* 22nm Haswell Server */
	case 69: /* 22nm Haswell ULT */
	case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */

	case 61: /* 14nm Broadwell Core-M */
	case 86: /* 14nm Broadwell Xeon D */
	case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */
	case 79: /* 14nm Broadwell Server */

	case 78: /* 14nm Skylake Mobile */
	case 94: /* 14nm Skylake Desktop */
		if (idx == PERF_CSTATE_CORE_C3_RES ||
		    idx == PERF_CSTATE_CORE_C6_RES ||
		    idx == PERF_CSTATE_CORE_C7_RES)
			return true;
		break;
	case 55: /* 22nm Atom "Silvermont"                */
	case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
	case 76: /* 14nm Atom "Airmont"                   */
		if (idx == PERF_CSTATE_CORE_C1_RES ||
		    idx == PERF_CSTATE_CORE_C6_RES)
			return true;
		break;
	}

	return false;
}

PMU_EVENT_ATTR_STRING(c1-residency, evattr_cstate_core_c1, "event=0x00");
PMU_EVENT_ATTR_STRING(c3-residency, evattr_cstate_core_c3, "event=0x01");
PMU_EVENT_ATTR_STRING(c6-residency, evattr_cstate_core_c6, "event=0x02");
PMU_EVENT_ATTR_STRING(c7-residency, evattr_cstate_core_c7, "event=0x03");

static struct perf_cstate_msr core_msr[] = {
	[PERF_CSTATE_CORE_C1_RES] = { MSR_CORE_C1_RES,		&evattr_cstate_core_c1,	test_core, },
	[PERF_CSTATE_CORE_C3_RES] = { MSR_CORE_C3_RESIDENCY,	&evattr_cstate_core_c3, test_core, },
	[PERF_CSTATE_CORE_C6_RES] = { MSR_CORE_C6_RESIDENCY,	&evattr_cstate_core_c6, test_core, },
	[PERF_CSTATE_CORE_C7_RES] = { MSR_CORE_C7_RESIDENCY,	&evattr_cstate_core_c7,	test_core, },
	[PERF_CSTATE_CORE_C1_RES] = { MSR_CORE_C1_RES,		&evattr_cstate_core_c1 },
	[PERF_CSTATE_CORE_C3_RES] = { MSR_CORE_C3_RESIDENCY,	&evattr_cstate_core_c3 },
	[PERF_CSTATE_CORE_C6_RES] = { MSR_CORE_C6_RESIDENCY,	&evattr_cstate_core_c6 },
	[PERF_CSTATE_CORE_C7_RES] = { MSR_CORE_C7_RESIDENCY,	&evattr_cstate_core_c7 },
};

static struct attribute *core_events_attrs[PERF_CSTATE_CORE_EVENT_MAX + 1] = {
@@ -234,18 +186,11 @@ static const struct attribute_group *core_attr_groups[] = {
	NULL,
};

/* cstate_core PMU end */


/* cstate_pkg PMU */

static struct pmu cstate_pkg_pmu;
static bool has_cstate_pkg;

enum perf_cstate_pkg_id {
	/*
	 * cstate_pkg events
	 */
enum perf_cstate_pkg_events {
	PERF_CSTATE_PKG_C2_RES = 0,
	PERF_CSTATE_PKG_C3_RES,
	PERF_CSTATE_PKG_C6_RES,
@@ -257,69 +202,6 @@ enum perf_cstate_pkg_id {
	PERF_CSTATE_PKG_EVENT_MAX,
};

bool test_pkg(int idx)
{
	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
	    boot_cpu_data.x86 != 6)
		return false;

	switch (boot_cpu_data.x86_model) {
	case 30: /* 45nm Nehalem    */
	case 26: /* 45nm Nehalem-EP */
	case 46: /* 45nm Nehalem-EX */

	case 37: /* 32nm Westmere    */
	case 44: /* 32nm Westmere-EP */
	case 47: /* 32nm Westmere-EX */
		if (idx == PERF_CSTATE_CORE_C3_RES ||
		    idx == PERF_CSTATE_CORE_C6_RES ||
		    idx == PERF_CSTATE_CORE_C7_RES)
			return true;
		break;
	case 42: /* 32nm SandyBridge         */
	case 45: /* 32nm SandyBridge-E/EN/EP */

	case 58: /* 22nm IvyBridge       */
	case 62: /* 22nm IvyBridge-EP/EX */

	case 60: /* 22nm Haswell Core */
	case 63: /* 22nm Haswell Server */
	case 70: /* 22nm Haswell + GT3e (Intel Iris Pro graphics) */

	case 61: /* 14nm Broadwell Core-M */
	case 86: /* 14nm Broadwell Xeon D */
	case 71: /* 14nm Broadwell + GT3e (Intel Iris Pro graphics) */
	case 79: /* 14nm Broadwell Server */

	case 78: /* 14nm Skylake Mobile */
	case 94: /* 14nm Skylake Desktop */
		if (idx == PERF_CSTATE_PKG_C2_RES ||
		    idx == PERF_CSTATE_PKG_C3_RES ||
		    idx == PERF_CSTATE_PKG_C6_RES ||
		    idx == PERF_CSTATE_PKG_C7_RES)
			return true;
		break;
	case 55: /* 22nm Atom "Silvermont"                */
	case 77: /* 22nm Atom "Silvermont Avoton/Rangely" */
	case 76: /* 14nm Atom "Airmont"                   */
		if (idx == PERF_CSTATE_CORE_C6_RES)
			return true;
		break;
	case 69: /* 22nm Haswell ULT */
		if (idx == PERF_CSTATE_PKG_C2_RES ||
		    idx == PERF_CSTATE_PKG_C3_RES ||
		    idx == PERF_CSTATE_PKG_C6_RES ||
		    idx == PERF_CSTATE_PKG_C7_RES ||
		    idx == PERF_CSTATE_PKG_C8_RES ||
		    idx == PERF_CSTATE_PKG_C9_RES ||
		    idx == PERF_CSTATE_PKG_C10_RES)
			return true;
		break;
	}

	return false;
}

PMU_EVENT_ATTR_STRING(c2-residency, evattr_cstate_pkg_c2, "event=0x00");
PMU_EVENT_ATTR_STRING(c3-residency, evattr_cstate_pkg_c3, "event=0x01");
PMU_EVENT_ATTR_STRING(c6-residency, evattr_cstate_pkg_c6, "event=0x02");
@@ -329,13 +211,13 @@ PMU_EVENT_ATTR_STRING(c9-residency, evattr_cstate_pkg_c9, "event=0x05");
PMU_EVENT_ATTR_STRING(c10-residency, evattr_cstate_pkg_c10, "event=0x06");

static struct perf_cstate_msr pkg_msr[] = {
	[PERF_CSTATE_PKG_C2_RES] = { MSR_PKG_C2_RESIDENCY,	&evattr_cstate_pkg_c2,	test_pkg, },
	[PERF_CSTATE_PKG_C3_RES] = { MSR_PKG_C3_RESIDENCY,	&evattr_cstate_pkg_c3,	test_pkg, },
	[PERF_CSTATE_PKG_C6_RES] = { MSR_PKG_C6_RESIDENCY,	&evattr_cstate_pkg_c6,	test_pkg, },
	[PERF_CSTATE_PKG_C7_RES] = { MSR_PKG_C7_RESIDENCY,	&evattr_cstate_pkg_c7,	test_pkg, },
	[PERF_CSTATE_PKG_C8_RES] = { MSR_PKG_C8_RESIDENCY,	&evattr_cstate_pkg_c8,	test_pkg, },
	[PERF_CSTATE_PKG_C9_RES] = { MSR_PKG_C9_RESIDENCY,	&evattr_cstate_pkg_c9,	test_pkg, },
	[PERF_CSTATE_PKG_C10_RES] = { MSR_PKG_C10_RESIDENCY,	&evattr_cstate_pkg_c10,	test_pkg, },
	[PERF_CSTATE_PKG_C2_RES] = { MSR_PKG_C2_RESIDENCY,	&evattr_cstate_pkg_c2 },
	[PERF_CSTATE_PKG_C3_RES] = { MSR_PKG_C3_RESIDENCY,	&evattr_cstate_pkg_c3 },
	[PERF_CSTATE_PKG_C6_RES] = { MSR_PKG_C6_RESIDENCY,	&evattr_cstate_pkg_c6 },
	[PERF_CSTATE_PKG_C7_RES] = { MSR_PKG_C7_RESIDENCY,	&evattr_cstate_pkg_c7 },
	[PERF_CSTATE_PKG_C8_RES] = { MSR_PKG_C8_RESIDENCY,	&evattr_cstate_pkg_c8 },
	[PERF_CSTATE_PKG_C9_RES] = { MSR_PKG_C9_RESIDENCY,	&evattr_cstate_pkg_c9 },
	[PERF_CSTATE_PKG_C10_RES] = { MSR_PKG_C10_RESIDENCY,	&evattr_cstate_pkg_c10 },
};

static struct attribute *pkg_events_attrs[PERF_CSTATE_PKG_EVENT_MAX + 1] = {
@@ -366,8 +248,6 @@ static const struct attribute_group *pkg_attr_groups[] = {
	NULL,
};

/* cstate_pkg PMU end*/

static ssize_t cstate_get_attr_cpumask(struct device *dev,
				       struct device_attribute *attr,
				       char *buf)
@@ -552,48 +432,151 @@ static int cstate_cpu_notifier(struct notifier_block *self,
	return NOTIFY_OK;
}

static struct pmu cstate_core_pmu = {
	.attr_groups	= core_attr_groups,
	.name		= "cstate_core",
	.task_ctx_nr	= perf_invalid_context,
	.event_init	= cstate_pmu_event_init,
	.add		= cstate_pmu_event_add,
	.del		= cstate_pmu_event_del,
	.start		= cstate_pmu_event_start,
	.stop		= cstate_pmu_event_stop,
	.read		= cstate_pmu_event_update,
	.capabilities	= PERF_PMU_CAP_NO_INTERRUPT,
};

static struct pmu cstate_pkg_pmu = {
	.attr_groups	= pkg_attr_groups,
	.name		= "cstate_pkg",
	.task_ctx_nr	= perf_invalid_context,
	.event_init	= cstate_pmu_event_init,
	.add		= cstate_pmu_event_add,
	.del		= cstate_pmu_event_del,
	.start		= cstate_pmu_event_start,
	.stop		= cstate_pmu_event_stop,
	.read		= cstate_pmu_event_update,
	.capabilities	= PERF_PMU_CAP_NO_INTERRUPT,
};

static const struct cstate_model nhm_cstates __initconst = {
	.core_events		= BIT(PERF_CSTATE_CORE_C3_RES) |
				  BIT(PERF_CSTATE_CORE_C6_RES),

	.pkg_events		= BIT(PERF_CSTATE_PKG_C3_RES) |
				  BIT(PERF_CSTATE_PKG_C6_RES) |
				  BIT(PERF_CSTATE_PKG_C7_RES),
};

static const struct cstate_model snb_cstates __initconst = {
	.core_events		= BIT(PERF_CSTATE_CORE_C3_RES) |
				  BIT(PERF_CSTATE_CORE_C6_RES) |
				  BIT(PERF_CSTATE_CORE_C7_RES),

	.pkg_events		= BIT(PERF_CSTATE_PKG_C2_RES) |
				  BIT(PERF_CSTATE_PKG_C3_RES) |
				  BIT(PERF_CSTATE_PKG_C6_RES) |
				  BIT(PERF_CSTATE_PKG_C7_RES),
};

static const struct cstate_model hswult_cstates __initconst = {
	.core_events		= BIT(PERF_CSTATE_CORE_C3_RES) |
				  BIT(PERF_CSTATE_CORE_C6_RES) |
				  BIT(PERF_CSTATE_CORE_C7_RES),

	.pkg_events		= BIT(PERF_CSTATE_PKG_C2_RES) |
				  BIT(PERF_CSTATE_PKG_C3_RES) |
				  BIT(PERF_CSTATE_PKG_C6_RES) |
				  BIT(PERF_CSTATE_PKG_C7_RES) |
				  BIT(PERF_CSTATE_PKG_C8_RES) |
				  BIT(PERF_CSTATE_PKG_C9_RES) |
				  BIT(PERF_CSTATE_PKG_C10_RES),
};

static const struct cstate_model slm_cstates __initconst = {
	.core_events		= BIT(PERF_CSTATE_CORE_C1_RES) |
				  BIT(PERF_CSTATE_CORE_C6_RES),

	.pkg_events		= BIT(PERF_CSTATE_PKG_C6_RES),
	.quirks			= SLM_PKG_C6_USE_C7_MSR,
};

#define X86_CSTATES_MODEL(model, states)				\
	{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long) &(states) }

static const struct x86_cpu_id intel_cstates_match[] __initconst = {
	X86_CSTATES_MODEL(30, nhm_cstates),    /* 45nm Nehalem              */
	X86_CSTATES_MODEL(26, nhm_cstates),    /* 45nm Nehalem-EP           */
	X86_CSTATES_MODEL(46, nhm_cstates),    /* 45nm Nehalem-EX           */

	X86_CSTATES_MODEL(37, nhm_cstates),    /* 32nm Westmere             */
	X86_CSTATES_MODEL(44, nhm_cstates),    /* 32nm Westmere-EP          */
	X86_CSTATES_MODEL(47, nhm_cstates),    /* 32nm Westmere-EX          */

	X86_CSTATES_MODEL(42, snb_cstates),    /* 32nm SandyBridge          */
	X86_CSTATES_MODEL(45, snb_cstates),    /* 32nm SandyBridge-E/EN/EP  */

	X86_CSTATES_MODEL(58, snb_cstates),    /* 22nm IvyBridge            */
	X86_CSTATES_MODEL(62, snb_cstates),    /* 22nm IvyBridge-EP/EX      */

	X86_CSTATES_MODEL(60, snb_cstates),    /* 22nm Haswell Core         */
	X86_CSTATES_MODEL(63, snb_cstates),    /* 22nm Haswell Server       */
	X86_CSTATES_MODEL(70, snb_cstates),    /* 22nm Haswell + GT3e       */

	X86_CSTATES_MODEL(69, hswult_cstates), /* 22nm Haswell ULT          */

	X86_CSTATES_MODEL(55, slm_cstates),    /* 22nm Atom Silvermont      */
	X86_CSTATES_MODEL(77, slm_cstates),    /* 22nm Atom Avoton/Rangely  */
	X86_CSTATES_MODEL(76, slm_cstates),    /* 22nm Atom Airmont         */

	X86_CSTATES_MODEL(61, snb_cstates),    /* 14nm Broadwell Core-M     */
	X86_CSTATES_MODEL(86, snb_cstates),    /* 14nm Broadwell Xeon D     */
	X86_CSTATES_MODEL(71, snb_cstates),    /* 14nm Broadwell + GT3e     */
	X86_CSTATES_MODEL(79, snb_cstates),    /* 14nm Broadwell Server     */

	X86_CSTATES_MODEL(78, snb_cstates),    /* 14nm Skylake Mobile       */
	X86_CSTATES_MODEL(94, snb_cstates),    /* 14nm Skylake Desktop      */
	{ },
};
MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match);

/*
 * Probe the cstate events and insert the available one into sysfs attrs
 * Return false if there is no available events.
 * Return false if there are no available events.
 */
static bool cstate_probe_msr(struct perf_cstate_msr *msr,
			     struct attribute	**events_attrs,
			     int max_event_nr)
static bool __init cstate_probe_msr(const unsigned long evmsk, int max,
                                   struct perf_cstate_msr *msr,
                                   struct attribute **attrs)
{
	int i, j = 0;
	bool found = false;
	unsigned int bit;
	u64 val;

	/* Probe the cstate events. */
	for (i = 0; i < max_event_nr; i++) {
		if (!msr[i].test(i) || rdmsrl_safe(msr[i].msr, &val))
			msr[i].attr = NULL;
	for (bit = 0; bit < max; bit++) {
		if (test_bit(bit, &evmsk) && !rdmsrl_safe(msr[bit].msr, &val)) {
			*attrs++ = &msr[bit].attr->attr.attr;
			found = true;
		} else {
			msr[bit].attr = NULL;
		}

	/* List remaining events in the sysfs attrs. */
	for (i = 0; i < max_event_nr; i++) {
		if (msr[i].attr)
			events_attrs[j++] = &msr[i].attr->attr.attr;
	}
	events_attrs[j] = NULL;
	*attrs = NULL;

	return (j > 0) ? true : false;
	return found;
}

static int __init cstate_init(void)
static int __init cstate_probe(const struct cstate_model *cm)
{
	/* SLM has different MSR for PKG C6 */
	switch (boot_cpu_data.x86_model) {
	case 55:
	case 76:
	case 77:
	if (cm->quirks & SLM_PKG_C6_USE_C7_MSR)
		pkg_msr[PERF_CSTATE_PKG_C6_RES].msr = MSR_PKG_C7_RESIDENCY;
	}

	if (cstate_probe_msr(core_msr, core_events_attrs, PERF_CSTATE_CORE_EVENT_MAX))
		has_cstate_core = true;
	has_cstate_core = cstate_probe_msr(cm->core_events,
					   PERF_CSTATE_CORE_EVENT_MAX,
					   core_msr, core_events_attrs);

	if (cstate_probe_msr(pkg_msr, pkg_events_attrs, PERF_CSTATE_PKG_EVENT_MAX))
		has_cstate_pkg = true;
	has_cstate_pkg = cstate_probe_msr(cm->pkg_events,
					  PERF_CSTATE_PKG_EVENT_MAX,
					  pkg_msr, pkg_events_attrs);

	return (has_cstate_core || has_cstate_pkg) ? 0 : -ENODEV;
}
@@ -612,32 +595,6 @@ static void __init cstate_cpumask_init(void)
	cpu_notifier_register_done();
}

static struct pmu cstate_core_pmu = {
	.attr_groups	= core_attr_groups,
	.name		= "cstate_core",
	.task_ctx_nr	= perf_invalid_context,
	.event_init	= cstate_pmu_event_init,
	.add		= cstate_pmu_event_add, /* must have */
	.del		= cstate_pmu_event_del, /* must have */
	.start		= cstate_pmu_event_start,
	.stop		= cstate_pmu_event_stop,
	.read		= cstate_pmu_event_update,
	.capabilities	= PERF_PMU_CAP_NO_INTERRUPT,
};

static struct pmu cstate_pkg_pmu = {
	.attr_groups	= pkg_attr_groups,
	.name		= "cstate_pkg",
	.task_ctx_nr	= perf_invalid_context,
	.event_init	= cstate_pmu_event_init,
	.add		= cstate_pmu_event_add, /* must have */
	.del		= cstate_pmu_event_del, /* must have */
	.start		= cstate_pmu_event_start,
	.stop		= cstate_pmu_event_stop,
	.read		= cstate_pmu_event_update,
	.capabilities	= PERF_PMU_CAP_NO_INTERRUPT,
};

static void __init cstate_pmus_register(void)
{
	int err;
@@ -659,12 +616,17 @@ static void __init cstate_pmus_register(void)

static int __init cstate_pmu_init(void)
{
	const struct x86_cpu_id *id;
	int err;

	if (cpu_has_hypervisor)
	if (boot_cpu_has(X86_FEATURE_HYPERVISOR))
		return -ENODEV;

	err = cstate_init();
	id = x86_match_cpu(intel_cstates_match);
	if (!id)
		return -ENODEV;

	err = cstate_probe((const struct cstate_model *) id->driver_data);
	if (err)
		return err;

@@ -674,5 +636,4 @@ static int __init cstate_pmu_init(void)

	return 0;
}

device_initcall(cstate_pmu_init);