[PATCH 02/10] perf/x86/intel/cstate: Apply "domain" for cstate

From: kan . liang
Date: Tue Feb 19 2019 - 15:01:01 EST


From: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>

There are duplicate codes implemented to support different scopes of
counters. Apply the new concept, "domain", for cstate to reduce the
redundancy.

Add struct cstate_pmus to store the PMU related information. Each
available type needs a dedicated cstate_pmus, which is allocated
in cstate_probe_msr().
Remove hardcode cstate_core_pmu and cstate_pkg_pmu. The PMU information
can be found via domain type now.
Cleanup the codes in cstate_pmu_event_init(), cstate_get_attr_cpumask()
and cstate_init().

The format attrs are the same for PACKAGE_DOMAIN and CORE_DOMAIN.
Remove the duplicate codes.

The cpu_mask of a domain type can be retrieved from the common
functions. Cleanup cstate_cpu_init/exit, and remove duplicate codes.

Signed-off-by: Kan Liang <kan.liang@xxxxxxxxxxxxxxx>
---
arch/x86/events/intel/cstate.c | 341 ++++++++++++++++++++++-------------------
1 file changed, 184 insertions(+), 157 deletions(-)

diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index d2e7807..5f71606 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -96,6 +96,7 @@
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#include "../perf_event.h"
+#include "../domain.h"

MODULE_LICENSE("GPL");

@@ -110,14 +111,15 @@ static ssize_t __cstate_##_var##_show(struct kobject *kobj, \
static struct kobj_attribute format_attr_##_var = \
__ATTR(_name, 0444, __cstate_##_var##_show, NULL)

-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;
+ union {
+ unsigned long events[DOMAIN_TYPE_MAX];
+ struct {
+ unsigned long pkg_events;
+ unsigned long core_events;
+ };
+ };
unsigned long quirks;
};

@@ -130,10 +132,17 @@ struct perf_cstate_msr {
struct perf_pmu_events_attr *attr;
};

+struct cstate_pmus {
+ struct pmu pmu;
+ struct domain_type type;
+ int event_max;
+ struct perf_cstate_msr *msrs;
+ struct attribute **attrs;
+ cpumask_t cpu_mask;
+};
+static struct cstate_pmus *cstate_pmus[DOMAIN_TYPE_MAX];

/* cstate_core PMU */
-static struct pmu cstate_core_pmu;
-static bool has_cstate_core;

enum perf_cstate_core_events {
PERF_CSTATE_CORE_C1_RES = 0,
@@ -166,17 +175,33 @@ static struct attribute_group core_events_attr_group = {
};

DEFINE_CSTATE_FORMAT_ATTR(core_event, event, "config:0-63");
-static struct attribute *core_format_attrs[] = {
+static struct attribute *format_attrs[] = {
&format_attr_core_event.attr,
NULL,
};

-static struct attribute_group core_format_attr_group = {
+static struct attribute_group format_attr_group = {
.name = "format",
- .attrs = core_format_attrs,
+ .attrs = format_attrs,
};

-static cpumask_t cstate_core_cpu_mask;
+static ssize_t cstate_get_attr_cpumask(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ struct cstate_pmus *pmus;
+ int i;
+
+ for (i = 0; i < DOMAIN_TYPE_MAX; i++) {
+ pmus = cstate_pmus[i];
+ if (!pmus || &pmus->pmu != pmu)
+ continue;
+ return cpumap_print_to_pagebuf(true, buf, &pmus->cpu_mask);
+ }
+ return 0;
+}
+
static DEVICE_ATTR(cpumask, S_IRUGO, cstate_get_attr_cpumask, NULL);

static struct attribute *cstate_cpumask_attrs[] = {
@@ -190,15 +215,12 @@ static struct attribute_group cpumask_attr_group = {

static const struct attribute_group *core_attr_groups[] = {
&core_events_attr_group,
- &core_format_attr_group,
+ &format_attr_group,
&cpumask_attr_group,
NULL,
};

/* cstate_pkg PMU */
-static struct pmu cstate_pkg_pmu;
-static bool has_cstate_pkg;
-
enum perf_cstate_pkg_events {
PERF_CSTATE_PKG_C2_RES = 0,
PERF_CSTATE_PKG_C3_RES,
@@ -238,44 +260,24 @@ static struct attribute_group pkg_events_attr_group = {
.attrs = pkg_events_attrs,
};

-DEFINE_CSTATE_FORMAT_ATTR(pkg_event, event, "config:0-63");
-static struct attribute *pkg_format_attrs[] = {
- &format_attr_pkg_event.attr,
- NULL,
-};
-static struct attribute_group pkg_format_attr_group = {
- .name = "format",
- .attrs = pkg_format_attrs,
-};
-
-static cpumask_t cstate_pkg_cpu_mask;
-
static const struct attribute_group *pkg_attr_groups[] = {
&pkg_events_attr_group,
- &pkg_format_attr_group,
+ &format_attr_group,
&cpumask_attr_group,
NULL,
};

-static ssize_t cstate_get_attr_cpumask(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct pmu *pmu = dev_get_drvdata(dev);
-
- if (pmu == &cstate_core_pmu)
- return cpumap_print_to_pagebuf(true, buf, &cstate_core_cpu_mask);
- else if (pmu == &cstate_pkg_pmu)
- return cpumap_print_to_pagebuf(true, buf, &cstate_pkg_cpu_mask);
- else
- return 0;
-}
-
static int cstate_pmu_event_init(struct perf_event *event)
{
+ const struct cpumask *cpu_mask;
u64 cfg = event->attr.config;
+ struct cstate_pmus *pmus;
int cpu;

+ pmus = container_of(event->pmu, struct cstate_pmus, pmu);
+ if (!pmus)
+ return -ENOENT;
+
if (event->attr.type != event->pmu->type)
return -ENOENT;

@@ -292,26 +294,19 @@ static int cstate_pmu_event_init(struct perf_event *event)
if (event->cpu < 0)
return -EINVAL;

- if (event->pmu == &cstate_core_pmu) {
- if (cfg >= PERF_CSTATE_CORE_EVENT_MAX)
- return -EINVAL;
- if (!core_msr[cfg].attr)
- return -EINVAL;
- event->hw.event_base = core_msr[cfg].msr;
- cpu = cpumask_any_and(&cstate_core_cpu_mask,
- topology_sibling_cpumask(event->cpu));
- } else if (event->pmu == &cstate_pkg_pmu) {
- if (cfg >= PERF_CSTATE_PKG_EVENT_MAX)
- return -EINVAL;
- cfg = array_index_nospec((unsigned long)cfg, PERF_CSTATE_PKG_EVENT_MAX);
- if (!pkg_msr[cfg].attr)
- return -EINVAL;
- event->hw.event_base = pkg_msr[cfg].msr;
- cpu = cpumask_any_and(&cstate_pkg_cpu_mask,
- topology_core_cpumask(event->cpu));
- } else {
- return -ENOENT;
- }
+ if (cfg >= pmus->event_max)
+ return -EINVAL;
+
+ cfg = array_index_nospec((unsigned long)cfg, pmus->event_max);
+ if (!pmus->msrs[cfg].attr)
+ return -EINVAL;
+
+ event->hw.event_base = pmus->msrs[cfg].msr;
+
+ cpu_mask = get_domain_cpu_mask(event->cpu, &pmus->type);
+ if (!cpu_mask)
+ return -ENODEV;
+ cpu = cpumask_any_and(&pmus->cpu_mask, cpu_mask);

if (cpu >= nr_cpu_ids)
return -ENODEV;
@@ -375,85 +370,61 @@ static int cstate_pmu_event_add(struct perf_event *event, int mode)
*/
static int cstate_cpu_exit(unsigned int cpu)
{
+ const struct cpumask *cpu_mask;
+ struct cstate_pmus *pmus;
unsigned int target;
+ int i;

- if (has_cstate_core &&
- cpumask_test_and_clear_cpu(cpu, &cstate_core_cpu_mask)) {
+ for (i = 0; i < DOMAIN_TYPE_MAX; i++) {
+ if (!cstate_pmus[i])
+ continue;

- target = cpumask_any_but(topology_sibling_cpumask(cpu), cpu);
- /* Migrate events if there is a valid target */
- if (target < nr_cpu_ids) {
- cpumask_set_cpu(target, &cstate_core_cpu_mask);
- perf_pmu_migrate_context(&cstate_core_pmu, cpu, target);
- }
- }
+ cpu_mask = get_domain_cpu_mask(cpu, &cstate_pmus[i]->type);
+ if (!cpu_mask)
+ continue;

- if (has_cstate_pkg &&
- cpumask_test_and_clear_cpu(cpu, &cstate_pkg_cpu_mask)) {
+ pmus = cstate_pmus[i];
+ if (!cpumask_test_and_clear_cpu(cpu, &pmus->cpu_mask))
+ continue;

- target = cpumask_any_but(topology_core_cpumask(cpu), cpu);
+ target = cpumask_any_but(cpu_mask, cpu);
/* Migrate events if there is a valid target */
if (target < nr_cpu_ids) {
- cpumask_set_cpu(target, &cstate_pkg_cpu_mask);
- perf_pmu_migrate_context(&cstate_pkg_pmu, cpu, target);
+ cpumask_set_cpu(target, &pmus->cpu_mask);
+ perf_pmu_migrate_context(&pmus->pmu, cpu, target);
}
}
+
return 0;
}

static int cstate_cpu_init(unsigned int cpu)
{
+ const struct cpumask *cpu_mask;
+ struct cstate_pmus *pmus;
unsigned int target;
+ int i;

- /*
- * If this is the first online thread of that core, set it in
- * the core cpu mask as the designated reader.
- */
- target = cpumask_any_and(&cstate_core_cpu_mask,
- topology_sibling_cpumask(cpu));
-
- if (has_cstate_core && target >= nr_cpu_ids)
- cpumask_set_cpu(cpu, &cstate_core_cpu_mask);
-
- /*
- * If this is the first online thread of that package, set it
- * in the package cpu mask as the designated reader.
- */
- target = cpumask_any_and(&cstate_pkg_cpu_mask,
- topology_core_cpumask(cpu));
- if (has_cstate_pkg && target >= nr_cpu_ids)
- cpumask_set_cpu(cpu, &cstate_pkg_cpu_mask);
+ for (i = 0; i < DOMAIN_TYPE_MAX; i++) {
+ if (!cstate_pmus[i])
+ continue;

- return 0;
-}
+ cpu_mask = get_domain_cpu_mask(cpu, &cstate_pmus[i]->type);
+ if (!cpu_mask)
+ continue;

-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,
- .module = THIS_MODULE,
-};
+ pmus = cstate_pmus[i];
+ /*
+ * If this is the first online thread of that core, set it in
+ * the core cpu mask as the designated reader.
+ */
+ target = cpumask_any_and(&pmus->cpu_mask, cpu_mask);

-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,
- .module = THIS_MODULE,
-};
+ if (target >= nr_cpu_ids)
+ cpumask_set_cpu(cpu, &pmus->cpu_mask);
+ }
+ return 0;
+}

static const struct cstate_model nhm_cstates __initconst = {
.core_events = BIT(PERF_CSTATE_CORE_C3_RES) |
@@ -592,14 +563,28 @@ MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match);
* Probe the cstate events and insert the available one into sysfs attrs
* Return false if there are no available events.
*/
-static bool __init cstate_probe_msr(const unsigned long evmsk, int max,
- struct perf_cstate_msr *msr,
- struct attribute **attrs)
+static bool __init cstate_probe_msr(const unsigned long evmsk,
+ enum domain_types type)
{
+ struct perf_cstate_msr *msr;
+ struct attribute **attrs;
+ struct cstate_pmus *pmus;
bool found = false;
unsigned int bit;
+ int max;
u64 val;

+ if (type == PACKAGE_DOMAIN) {
+ max = PERF_CSTATE_PKG_EVENT_MAX;
+ msr = pkg_msr;
+ attrs = pkg_events_attrs;
+ } else if (type == CORE_DOMAIN) {
+ max = PERF_CSTATE_CORE_EVENT_MAX;
+ msr = core_msr;
+ attrs = core_events_attrs;
+ } else
+ return false;
+
for (bit = 0; bit < max; bit++) {
if (test_bit(bit, &evmsk) && !rdmsrl_safe(msr[bit].msr, &val)) {
*attrs++ = &msr[bit].attr->attr.attr;
@@ -610,11 +595,32 @@ static bool __init cstate_probe_msr(const unsigned long evmsk, int max,
}
*attrs = NULL;

- return found;
+ if (!found)
+ return false;
+
+ pmus = kzalloc(sizeof(struct cstate_pmus), GFP_KERNEL);
+ if (!pmus)
+ return false;
+
+ pmus->type.type = type;
+ if (domain_type_init(&pmus->type)) {
+ kfree(pmus);
+ return false;
+ }
+ pmus->event_max = max;
+ pmus->msrs = msr;
+ pmus->attrs = attrs;
+
+ cstate_pmus[type] = pmus;
+
+ return true;
}

static int __init cstate_probe(const struct cstate_model *cm)
{
+ bool found = false;
+ enum domain_types i;
+
/* SLM has different MSR for PKG C6 */
if (cm->quirks & SLM_PKG_C6_USE_C7_MSR)
pkg_msr[PERF_CSTATE_PKG_C6_RES].msr = MSR_PKG_C7_RESIDENCY;
@@ -624,58 +630,79 @@ static int __init cstate_probe(const struct cstate_model *cm)
pkg_msr[PERF_CSTATE_CORE_C6_RES].msr = MSR_KNL_CORE_C6_RESIDENCY;


- has_cstate_core = cstate_probe_msr(cm->core_events,
- PERF_CSTATE_CORE_EVENT_MAX,
- core_msr, core_events_attrs);
+ for (i = 0; i < DOMAIN_TYPE_MAX; i++) {
+ if (!cm->events[i])
+ continue;

- 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;
+ if (cstate_probe_msr(cm->events[i], i))
+ found = true;
+ }
+ return found ? 0 : -ENODEV;
}

static inline void cstate_cleanup(void)
{
+ int i;
+
cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_CSTATE_ONLINE);
cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_CSTATE_STARTING);

- if (has_cstate_core)
- perf_pmu_unregister(&cstate_core_pmu);
-
- if (has_cstate_pkg)
- perf_pmu_unregister(&cstate_pkg_pmu);
+ for (i = 0; i < DOMAIN_TYPE_MAX; i++) {
+ if (!cstate_pmus[i])
+ continue;
+ perf_pmu_unregister(&cstate_pmus[i]->pmu);
+ kfree(cstate_pmus[i]);
+ cstate_pmus[i] = NULL;
+ }
}

static int __init cstate_init(void)
{
- int err;
+ struct pmu *pmu;
+ char name[DOMAIN_NAME_LEN];
+ int i, err = 0;

cpuhp_setup_state(CPUHP_AP_PERF_X86_CSTATE_STARTING,
"perf/x86/cstate:starting", cstate_cpu_init, NULL);
cpuhp_setup_state(CPUHP_AP_PERF_X86_CSTATE_ONLINE,
"perf/x86/cstate:online", NULL, cstate_cpu_exit);

- if (has_cstate_core) {
- err = perf_pmu_register(&cstate_core_pmu, cstate_core_pmu.name, -1);
- if (err) {
- has_cstate_core = false;
- pr_info("Failed to register cstate core pmu\n");
- cstate_cleanup();
- return err;
+ for (i = 0; i < DOMAIN_TYPE_MAX; i++) {
+ if (!cstate_pmus[i])
+ continue;
+ pmu = &cstate_pmus[i]->pmu;
+
+ if (i == PACKAGE_DOMAIN)
+ pmu->attr_groups = pkg_attr_groups;
+ else if (i == CORE_DOMAIN)
+ pmu->attr_groups = core_attr_groups;
+
+ pmu->task_ctx_nr = perf_invalid_context;
+ pmu->event_init = cstate_pmu_event_init;
+ pmu->add = cstate_pmu_event_add;
+ pmu->del = cstate_pmu_event_del;
+ pmu->start = cstate_pmu_event_start;
+ pmu->stop = cstate_pmu_event_stop;
+ pmu->read = cstate_pmu_event_update;
+ pmu->capabilities = PERF_PMU_CAP_NO_INTERRUPT;
+ pmu->module = THIS_MODULE;
+
+ err = snprintf(name, DOMAIN_NAME_LEN, "cstate_%s",
+ cstate_pmus[i]->type.postfix);
+ if (err < 0) {
+ kfree(cstate_pmus[i]);
+ cstate_pmus[i] = NULL;
+ continue;
}
- }
-
- if (has_cstate_pkg) {
- err = perf_pmu_register(&cstate_pkg_pmu, cstate_pkg_pmu.name, -1);
+ err = perf_pmu_register(pmu, name, -1);
if (err) {
- has_cstate_pkg = false;
- pr_info("Failed to register cstate pkg pmu\n");
- cstate_cleanup();
- return err;
+ kfree(cstate_pmus[i]);
+ cstate_pmus[i] = NULL;
+ pr_info("Failed to register %s pmu\n", name);
}
}
- return 0;
+
+ return err;
}

static int __init cstate_pmu_init(void)
--
2.7.4