From: Kan Liang <[email protected]> The RAPL counters are not package scope only anymore. For example, there will be die scope RAPL counters on CLX-AP. Apply "domain" for RAPL, and make it easy to be extended later.
Each type of domain needs a dedicated rapl_pmus. The struct rapl_pmus is modified accordingly. - The fixed counters may be different among different domain types. Move rapl_cntr_mask to struct rapl_pmus. - The CPU mask may be different among different domain types as well. Move rapl_cpu_mask to struct rapl_pmus. Also update rapl_cpu_online/offline accordingly. - Replace maxpkg by the number of domain Rename rapl_pmu_events_group to rapl_pkg_pmu_events_group for domains of PACKAGE_DOMAIN type. Added PMU name in rapl_advertise() to distinguish between different type of domain. Extend intel_rapl_init_fun to support events from different type of domain. If there are more than two types of domain on a machine, using new PMU name, "power_$domain_type", otherwise, still use "power". Signed-off-by: Kan Liang <[email protected]> --- arch/x86/events/intel/rapl.c | 306 +++++++++++++++++++++++++++++++------------ 1 file changed, 224 insertions(+), 82 deletions(-) diff --git a/arch/x86/events/intel/rapl.c b/arch/x86/events/intel/rapl.c index 91039ff..c1ba09c 100644 --- a/arch/x86/events/intel/rapl.c +++ b/arch/x86/events/intel/rapl.c @@ -57,6 +57,7 @@ #include <asm/cpu_device_id.h> #include <asm/intel-family.h> #include "../perf_event.h" +#include "../domain.h" MODULE_LICENSE("GPL"); @@ -148,26 +149,31 @@ struct rapl_pmu { struct rapl_pmus { struct pmu pmu; - unsigned int maxpkg; + struct domain_type type; + unsigned int rapl_cntr_mask; + cpumask_t rapl_cpu_mask; struct rapl_pmu *pmus[]; }; /* 1/2^hw_unit Joule */ static int rapl_hw_unit[NR_RAPL_DOMAINS] __read_mostly; -static struct rapl_pmus *rapl_pmus; -static cpumask_t rapl_cpu_mask; -static unsigned int rapl_cntr_mask; +static struct rapl_pmus *rapl_pmus[DOMAIN_TYPE_MAX]; static u64 rapl_timer_ms; +static unsigned int rapl_domain_type_mask; -static inline struct rapl_pmu *cpu_to_rapl_pmu(unsigned int cpu) +static inline struct rapl_pmu *cpu_to_rapl_pmu(unsigned int cpu, + struct rapl_pmus *pmus) { - unsigned int pkgid = topology_logical_package_id(cpu); + unsigned int id = get_domain_id(cpu, &pmus->type); + + if (!pmus) + return NULL; /* * The unsigned check also catches the '-1' return value for non * existent mappings in the topology map. */ - return pkgid < rapl_pmus->maxpkg ? rapl_pmus->pmus[pkgid] : NULL; + return id < pmus->type.max_domains ? pmus->pmus[id] : NULL; } static inline u64 rapl_read_counter(struct perf_event *event) @@ -350,10 +356,15 @@ static int rapl_pmu_event_init(struct perf_event *event) { u64 cfg = event->attr.config & RAPL_EVENT_MASK; int bit, msr, ret = 0; + struct rapl_pmus *pmus; struct rapl_pmu *pmu; + pmus = container_of(event->pmu, struct rapl_pmus, pmu); + if (!pmus) + return -ENOENT; + /* only look at RAPL events */ - if (event->attr.type != rapl_pmus->pmu.type) + if (event->attr.type != pmus->pmu.type) return -ENOENT; /* check only supported bits are set */ @@ -393,7 +404,7 @@ static int rapl_pmu_event_init(struct perf_event *event) return -EINVAL; } /* check event supported */ - if (!(rapl_cntr_mask & (1 << bit))) + if (!(pmus->rapl_cntr_mask & (1 << bit))) return -EINVAL; /* unsupported modes and filters */ @@ -407,7 +418,7 @@ static int rapl_pmu_event_init(struct perf_event *event) return -EINVAL; /* must be done before validate_group */ - pmu = cpu_to_rapl_pmu(event->cpu); + pmu = cpu_to_rapl_pmu(event->cpu, pmus); if (!pmu) return -EINVAL; event->cpu = pmu->cpu; @@ -425,9 +436,21 @@ static void rapl_pmu_event_read(struct perf_event *event) } static ssize_t rapl_get_attr_cpumask(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, + char *buf) { - return cpumap_print_to_pagebuf(true, buf, &rapl_cpu_mask); + struct pmu *pmu = dev_get_drvdata(dev); + struct rapl_pmus *pmus; + int i; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + pmus = rapl_pmus[i]; + if (!pmus || &pmus->pmu != pmu) + continue; + + return cpumap_print_to_pagebuf(true, buf, &pmus->rapl_cpu_mask); + } + return 0; } static DEVICE_ATTR(cpumask, S_IRUGO, rapl_get_attr_cpumask, NULL); @@ -543,7 +566,7 @@ static struct attribute *rapl_events_knl_attr[] = { NULL, }; -static struct attribute_group rapl_pmu_events_group = { +static struct attribute_group rapl_pkg_pmu_events_group = { .name = "events", .attrs = NULL, /* patched at runtime */ }; @@ -559,39 +582,63 @@ static struct attribute_group rapl_pmu_format_group = { .attrs = rapl_formats_attr, }; -static const struct attribute_group *rapl_attr_groups[] = { +static const struct attribute_group *rapl_pkg_attr_groups[] = { &rapl_pmu_attr_group, &rapl_pmu_format_group, - &rapl_pmu_events_group, + &rapl_pkg_pmu_events_group, NULL, }; -static int rapl_cpu_offline(unsigned int cpu) +static int __rapl_cpu_offline(unsigned int cpu, struct rapl_pmus *pmus) { - struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu); + struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu, pmus); + const struct cpumask *cpu_mask; int target; + if (!pmus) + return -1; + /* Check if exiting cpu is used for collecting rapl events */ - if (!cpumask_test_and_clear_cpu(cpu, &rapl_cpu_mask)) + if (!cpumask_test_and_clear_cpu(cpu, &pmus->rapl_cpu_mask)) return 0; pmu->cpu = -1; /* Find a new cpu to collect rapl events */ - target = cpumask_any_but(topology_core_cpumask(cpu), cpu); + cpu_mask = get_domain_cpu_mask(cpu, &pmus->type); + if (!cpu_mask) + return -1; + target = cpumask_any_but(cpu_mask, cpu); /* Migrate rapl events to the new target */ if (target < nr_cpu_ids) { - cpumask_set_cpu(target, &rapl_cpu_mask); + cpumask_set_cpu(target, &pmus->rapl_cpu_mask); pmu->cpu = target; perf_pmu_migrate_context(pmu->pmu, cpu, target); } return 0; } -static int rapl_cpu_online(unsigned int cpu) +static int rapl_cpu_offline(unsigned int cpu) { - struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu); - int target; + int i; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_pmus[i]) + continue; + + __rapl_cpu_offline(cpu, rapl_pmus[i]); + } + return 0; +} + +static int __rapl_cpu_online(unsigned int cpu, struct rapl_pmus *pmus) +{ + struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu, pmus); + const struct cpumask *cpu_mask; + int target, id; + + if (!pmus) + return -EINVAL; if (!pmu) { pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu)); @@ -600,26 +647,47 @@ static int rapl_cpu_online(unsigned int cpu) raw_spin_lock_init(&pmu->lock); INIT_LIST_HEAD(&pmu->active_list); - pmu->pmu = &rapl_pmus->pmu; + pmu->pmu = &pmus->pmu; pmu->timer_interval = ms_to_ktime(rapl_timer_ms); rapl_hrtimer_init(pmu); - rapl_pmus->pmus[topology_logical_package_id(cpu)] = pmu; + id = get_domain_id(cpu, &pmus->type); + if (id < 0) { + kfree(pmu); + return -EINVAL; + } + pmus->pmus[id] = pmu; } /* * Check if there is an online cpu in the package which collects rapl * events already. */ - target = cpumask_any_and(&rapl_cpu_mask, topology_core_cpumask(cpu)); + cpu_mask = get_domain_cpu_mask(cpu, &pmus->type); + if (!cpu_mask) + return -1; + target = cpumask_any_and(&pmus->rapl_cpu_mask, cpu_mask); if (target < nr_cpu_ids) return 0; - cpumask_set_cpu(cpu, &rapl_cpu_mask); + cpumask_set_cpu(cpu, &pmus->rapl_cpu_mask); pmu->cpu = cpu; return 0; } +static int rapl_cpu_online(unsigned int cpu) +{ + int i; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_pmus[i]) + continue; + + __rapl_cpu_online(cpu, rapl_pmus[i]); + } + return 0; +} + static int rapl_check_hw_unit(bool apply_quirk) { u64 msr_rapl_power_unit_bits; @@ -657,94 +725,163 @@ static int rapl_check_hw_unit(bool apply_quirk) static void __init rapl_advertise(void) { - int i; - - pr_info("API unit is 2^-32 Joules, %d fixed counters, %llu ms ovfl timer\n", - hweight32(rapl_cntr_mask), rapl_timer_ms); - - for (i = 0; i < NR_RAPL_DOMAINS; i++) { - if (rapl_cntr_mask & (1 << i)) { - pr_info("hw unit of domain %s 2^-%d Joules\n", - rapl_domain_names[i], rapl_hw_unit[i]); + int i, j; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_pmus[i]) + continue; + + pr_info("%s: API unit is 2^-32 Joules, " + "%d fixed counters, %llu ms ovfl timer\n", + rapl_pmus[i]->pmu.name, + hweight32(rapl_pmus[i]->rapl_cntr_mask), + rapl_timer_ms); + + for (j = 0; j < NR_RAPL_DOMAINS; j++) { + if (rapl_pmus[i]->rapl_cntr_mask & (1 << j)) { + pr_info("hw unit of domain %s 2^-%d Joules\n", + rapl_domain_names[j], rapl_hw_unit[j]); + } } } } static void cleanup_rapl_pmus(void) { - int i; - - for (i = 0; i < rapl_pmus->maxpkg; i++) - kfree(rapl_pmus->pmus[i]); - kfree(rapl_pmus); + int i, j; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_pmus[i]) + continue; + for (j = 0; j < rapl_pmus[i]->type.max_domains; j++) + kfree(rapl_pmus[i]->pmus[j]); + kfree(rapl_pmus[i]); + rapl_pmus[i] = NULL; + } } -static int __init init_rapl_pmus(void) +struct intel_rapl_events { + int cntr_mask; + struct attribute **attrs; +}; + +struct intel_rapl_init_fun { + bool apply_quirk; + const struct intel_rapl_events events[DOMAIN_TYPE_MAX]; +}; + +static int __init init_rapl_pmus(const struct intel_rapl_events *events, + enum domain_types type) { - int maxpkg = topology_max_packages(); + struct domain_type domain_type; + struct rapl_pmus *pmus; size_t size; - size = sizeof(*rapl_pmus) + maxpkg * sizeof(struct rapl_pmu *); - rapl_pmus = kzalloc(size, GFP_KERNEL); - if (!rapl_pmus) + domain_type.type = type; + if (domain_type_init(&domain_type)) + return -ENODEV; + + size = sizeof(struct rapl_pmus) + domain_type.max_domains * sizeof(struct rapl_pmu *); + pmus = kzalloc(size, GFP_KERNEL); + if (!pmus) return -ENOMEM; - rapl_pmus->maxpkg = maxpkg; - rapl_pmus->pmu.attr_groups = rapl_attr_groups; - rapl_pmus->pmu.task_ctx_nr = perf_invalid_context; - rapl_pmus->pmu.event_init = rapl_pmu_event_init; - rapl_pmus->pmu.add = rapl_pmu_event_add; - rapl_pmus->pmu.del = rapl_pmu_event_del; - rapl_pmus->pmu.start = rapl_pmu_event_start; - rapl_pmus->pmu.stop = rapl_pmu_event_stop; - rapl_pmus->pmu.read = rapl_pmu_event_read; - rapl_pmus->pmu.module = THIS_MODULE; + memcpy(&pmus->type, &domain_type, sizeof(struct domain_type)); + pmus->rapl_cntr_mask = events->cntr_mask; + if (type == PACKAGE_DOMAIN) { + rapl_pkg_pmu_events_group.attrs = events->attrs; + pmus->pmu.attr_groups = rapl_pkg_attr_groups; + } + pmus->pmu.task_ctx_nr = perf_invalid_context; + pmus->pmu.event_init = rapl_pmu_event_init; + pmus->pmu.add = rapl_pmu_event_add; + pmus->pmu.del = rapl_pmu_event_del; + pmus->pmu.start = rapl_pmu_event_start; + pmus->pmu.stop = rapl_pmu_event_stop; + pmus->pmu.read = rapl_pmu_event_read; + pmus->pmu.module = THIS_MODULE; + + rapl_pmus[type] = pmus; return 0; } +static int __init rapl_pmus_register(void) +{ + bool registered = false; + char name[DOMAIN_NAME_LEN]; + int i, ret; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_pmus[i]) + continue; + + if (hweight32(rapl_domain_type_mask) > 1) + ret = snprintf(name, DOMAIN_NAME_LEN, "power_%s", + rapl_pmus[i]->type.postfix); + else + ret = snprintf(name, DOMAIN_NAME_LEN, "power"); + if (ret < 0) + continue; + ret = perf_pmu_register(&rapl_pmus[i]->pmu, name, -1); + if (ret) { + kfree(rapl_pmus[i]); + rapl_pmus[i] = NULL; + continue; + } + registered = true; + } + + return registered ? 0 : -1; +} + +static void rapl_pmus_unregister(void) +{ + int i; + + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_pmus[i]) + continue; + perf_pmu_unregister(&rapl_pmus[i]->pmu); + } +} + #define X86_RAPL_MODEL_MATCH(model, init) \ { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&init } -struct intel_rapl_init_fun { - bool apply_quirk; - int cntr_mask; - struct attribute **attrs; -}; - static const struct intel_rapl_init_fun snb_rapl_init __initconst = { .apply_quirk = false, - .cntr_mask = RAPL_IDX_CLN, - .attrs = rapl_events_cln_attr, + .events[PACKAGE_DOMAIN].cntr_mask = RAPL_IDX_CLN, + .events[PACKAGE_DOMAIN].attrs = rapl_events_cln_attr, }; static const struct intel_rapl_init_fun hsx_rapl_init __initconst = { .apply_quirk = true, - .cntr_mask = RAPL_IDX_SRV, - .attrs = rapl_events_srv_attr, + .events[PACKAGE_DOMAIN].cntr_mask = RAPL_IDX_SRV, + .events[PACKAGE_DOMAIN].attrs = rapl_events_srv_attr, }; static const struct intel_rapl_init_fun hsw_rapl_init __initconst = { .apply_quirk = false, - .cntr_mask = RAPL_IDX_HSW, - .attrs = rapl_events_hsw_attr, + .events[PACKAGE_DOMAIN].cntr_mask = RAPL_IDX_HSW, + .events[PACKAGE_DOMAIN].attrs = rapl_events_hsw_attr, }; static const struct intel_rapl_init_fun snbep_rapl_init __initconst = { .apply_quirk = false, - .cntr_mask = RAPL_IDX_SRV, - .attrs = rapl_events_srv_attr, + .events[PACKAGE_DOMAIN].cntr_mask = RAPL_IDX_SRV, + .events[PACKAGE_DOMAIN].attrs = rapl_events_srv_attr, }; static const struct intel_rapl_init_fun knl_rapl_init __initconst = { .apply_quirk = true, - .cntr_mask = RAPL_IDX_KNL, - .attrs = rapl_events_knl_attr, + .events[PACKAGE_DOMAIN].cntr_mask = RAPL_IDX_KNL, + .events[PACKAGE_DOMAIN].attrs = rapl_events_knl_attr, }; static const struct intel_rapl_init_fun skl_rapl_init __initconst = { .apply_quirk = false, - .cntr_mask = RAPL_IDX_SKL_CLN, - .attrs = rapl_events_skl_attr, + .events[PACKAGE_DOMAIN].cntr_mask = RAPL_IDX_SKL_CLN, + .events[PACKAGE_DOMAIN].attrs = rapl_events_skl_attr, }; static const struct x86_cpu_id rapl_cpu_match[] __initconst = { @@ -790,7 +927,7 @@ static int __init rapl_pmu_init(void) const struct x86_cpu_id *id; struct intel_rapl_init_fun *rapl_init; bool apply_quirk; - int ret; + int i, ret; id = x86_match_cpu(rapl_cpu_match); if (!id) @@ -798,16 +935,21 @@ static int __init rapl_pmu_init(void) rapl_init = (struct intel_rapl_init_fun *)id->driver_data; apply_quirk = rapl_init->apply_quirk; - rapl_cntr_mask = rapl_init->cntr_mask; - rapl_pmu_events_group.attrs = rapl_init->attrs; ret = rapl_check_hw_unit(apply_quirk); if (ret) return ret; - ret = init_rapl_pmus(); - if (ret) - return ret; + for (i = 0; i < DOMAIN_TYPE_MAX; i++) { + if (!rapl_init->events[i].cntr_mask) + continue; + ret = init_rapl_pmus(&rapl_init->events[i], i); + if (ret) + continue; + rapl_domain_type_mask |= (1 << i); + } + if (hweight32(rapl_domain_type_mask) == 0) + return -ENODEV; /* * Install callbacks. Core will call them for each online cpu. @@ -818,7 +960,7 @@ static int __init rapl_pmu_init(void) if (ret) goto out; - ret = perf_pmu_register(&rapl_pmus->pmu, "power", -1); + ret = rapl_pmus_register(); if (ret) goto out1; @@ -837,7 +979,7 @@ module_init(rapl_pmu_init); static void __exit intel_rapl_exit(void) { cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_RAPL_ONLINE); - perf_pmu_unregister(&rapl_pmus->pmu); + rapl_pmus_unregister(); cleanup_rapl_pmus(); } module_exit(intel_rapl_exit); -- 2.7.4

