Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752214AbbGIHum (ORCPT ); Thu, 9 Jul 2015 03:50:42 -0400 Received: from mga09.intel.com ([134.134.136.24]:27487 "EHLO mga09.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750728AbbGIHud (ORCPT ); Thu, 9 Jul 2015 03:50:33 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.15,438,1432623600"; d="scan'208";a="521246279" From: Adrian Hunter To: Peter Zijlstra , Ingo Molnar Cc: Arnaldo Carvalho de Melo , Andy Lutomirski , Vince Weaver , Thomas Gleixner , "H. Peter Anvin" , linux-kernel@vger.kernel.org, Jiri Olsa , Stephane Eranian , Borislav Petkov , Alexander Shishkin , Andi Kleen Subject: [RFC PATCH] perf: Provide status of known PMUs Date: Thu, 9 Jul 2015 10:48:00 +0300 Message-Id: <1436428080-3098-1-git-send-email-adrian.hunter@intel.com> X-Mailer: git-send-email 1.9.1 Organization: Intel Finland Oy, Registered Address: PL 281, 00181 Helsinki, Business Identity Code: 0357606 - 4, Domiciled in Helsinki Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 10637 Lines: 379 Known PMUs may not be present for various reasons. Provide a way for the user to know what the reason is. A bus attribute is created for each known PMU beneath a group "known_pmus". The attribute name is the same as the PMU name. The value is a string consisting of one or, optionally, two parts: a canonical part, and a driver specific part. If there are two parts, they are separated by " - ". The canonical part is one of: Supported Driver error Driver not loaded Driver not in kernel config Not supported by kernel Not supported by hardware Wrong vendor Wrong architecture Unknown status Example: $ cat /sys/bus/event_source/known_pmus/intel_pt Supported Signed-off-by: Adrian Hunter --- arch/x86/kernel/cpu/perf_event.c | 3 + arch/x86/kernel/cpu/perf_event_intel_bts.c | 20 +++- arch/x86/kernel/cpu/perf_event_intel_pt.c | 18 ++- include/linux/perf_event.h | 23 ++++ kernel/events/core.c | 171 +++++++++++++++++++++++++++++ 5 files changed, 226 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 3658de47900f..546163a42a30 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -1670,12 +1670,15 @@ static int __init init_hw_perf_events(void) switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_INTEL: + perf_pmu_vendor("Intel"); err = intel_pmu_init(); break; case X86_VENDOR_AMD: + perf_pmu_vendor("AMD"); err = amd_pmu_init(); break; default: + perf_pmu_vendor("Unknown"); err = -ENOTSUPP; } if (err != 0) { diff --git a/arch/x86/kernel/cpu/perf_event_intel_bts.c b/arch/x86/kernel/cpu/perf_event_intel_bts.c index 43dd672d788b..6eb1b637668a 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_bts.c +++ b/arch/x86/kernel/cpu/perf_event_intel_bts.c @@ -514,8 +514,19 @@ static void bts_event_read(struct perf_event *event) static __init int bts_init(void) { - if (!boot_cpu_has(X86_FEATURE_DTES64) || !x86_pmu.bts) - return -ENODEV; + const char *status_msg = NULL; + int ret; + + if (!x86_pmu.bts) { + ret = -ENODEV; + goto out; + } + + if (!boot_cpu_has(X86_FEATURE_DTES64)) { + status_msg = "requires 64-bit kernel"; + ret = -ENOTSUPP; + goto out; + } bts_pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_ITRACE; bts_pmu.task_ctx_nr = perf_sw_context; @@ -528,6 +539,9 @@ static __init int bts_init(void) bts_pmu.setup_aux = bts_buffer_setup_aux; bts_pmu.free_aux = bts_buffer_free_aux; - return perf_pmu_register(&bts_pmu, "intel_bts", -1); + ret = perf_pmu_register(&bts_pmu, "intel_bts", -1); +out: + perf_pmu_error_status("intel_bts", ret, status_msg); + return ret; } arch_initcall(bts_init); diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c index 183de719628d..e2dc697ab1a6 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_pt.c +++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c @@ -1059,6 +1059,7 @@ static int pt_event_init(struct perf_event *event) static __init int pt_init(void) { int ret, cpu, prior_warn = 0; + const char *status_msg = NULL; BUILD_BUG_ON(sizeof(struct topa) > PAGE_SIZE); get_online_cpus(); @@ -1073,18 +1074,19 @@ static __init int pt_init(void) if (prior_warn) { x86_add_exclusive(x86_lbr_exclusive_pt); - pr_warn("PT is enabled at boot time, doing nothing\n"); - - return -EBUSY; + status_msg = "PT is enabled at boot time, doing nothing"; + ret = -EBUSY; + goto out; } ret = pt_pmu_hw_init(); if (ret) - return ret; + goto out; if (!pt_cap_get(PT_CAP_topa_output)) { - pr_warn("ToPA output is not supported on this CPU\n"); - return -ENODEV; + status_msg = "ToPA output is not supported on this CPU"; + ret = -ENODEV; + goto out; } if (!pt_cap_get(PT_CAP_topa_multiple_entries)) @@ -1103,6 +1105,10 @@ static __init int pt_init(void) pt_pmu.pmu.setup_aux = pt_buffer_setup_aux; pt_pmu.pmu.free_aux = pt_buffer_free_aux; ret = perf_pmu_register(&pt_pmu.pmu, "intel_pt", -1); +out: + if (status_msg) + pr_warn("%s\n", status_msg); + perf_pmu_error_status("intel_pt", ret, status_msg); return ret; } diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 2027809433b3..e1eb95ec3e23 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -179,6 +179,22 @@ struct perf_event; #define PERF_PMU_CAP_ITRACE 0x20 /** + * enum perf_pmu_status - the status of known PMUs + */ +enum perf_pmu_status { + PERF_PMU_STATUS_SUPPORTED, + PERF_PMU_STATUS_ERROR, + PERF_PMU_STATUS_NOT_LOADED, + PERF_PMU_STATUS_NOT_CONFIG, + PERF_PMU_STATUS_NOT_SUPPORTED, + PERF_PMU_STATUS_WRONG_HW, + PERF_PMU_STATUS_WRONG_VENDOR, + PERF_PMU_STATUS_WRONG_ARCH, + PERF_PMU_STATUS_UNKNOWN, + PERF_PMU_STATUS_MAX, +}; + +/** * struct pmu - generic performance monitoring unit */ struct pmu { @@ -631,6 +647,13 @@ extern void *perf_get_aux(struct perf_output_handle *handle); extern int perf_pmu_register(struct pmu *pmu, const char *name, int type); extern void perf_pmu_unregister(struct pmu *pmu); +extern void perf_pmu_update_status(const char *name, + enum perf_pmu_status status, + const char *status_msg); +extern void perf_pmu_error_status(const char *name, int err, + const char *status_msg); +extern void perf_pmu_vendor(const char *vendor); + extern int perf_num_counters(void); extern const char *perf_pmu_name(void); extern void __perf_event_task_sched_in(struct task_struct *prev, diff --git a/kernel/events/core.c b/kernel/events/core.c index d3dae3419b99..ad3e9ec1f32b 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7154,6 +7154,176 @@ out: } static struct idr pmu_idr; +struct known_pmu { + const char *name; + const char *vendor; + const char *status_msg; + int status; +}; + +#define KNOWN_PMU(_name, _vendor, _status) { \ + .name = _name, \ + .vendor = _vendor, \ + .status = _status, \ +} + +#if defined(CONFIG_X86) +#define PERF_PMU_STATUS_ARCH_X86 PERF_PMU_STATUS_UNKNOWN +#else +#define PERF_PMU_STATUS_ARCH_X86 PERF_PMU_STATUS_WRONG_ARCH +#endif + +static struct known_pmu known_pmus[] = { + KNOWN_PMU("intel_pt", "Intel", PERF_PMU_STATUS_ARCH_X86), + KNOWN_PMU("intel_bts", "Intel", PERF_PMU_STATUS_ARCH_X86), + KNOWN_PMU(NULL, NULL, 0), +}; + +static DEFINE_MUTEX(known_pmus_lock); + +static struct known_pmu *known_pmu_find(const char *name) +{ + struct known_pmu *known_pmu; + + if (!name) + return NULL; + + for (known_pmu = known_pmus; known_pmu->name; known_pmu++) { + if (!strcmp(known_pmu->name, name)) + return known_pmu; + } + return NULL; +} + +static const char *pmu_status_msg[] = { + [PERF_PMU_STATUS_SUPPORTED] = "Supported", + [PERF_PMU_STATUS_ERROR] = "Driver error", + [PERF_PMU_STATUS_NOT_LOADED] = "Driver not loaded", + [PERF_PMU_STATUS_NOT_CONFIG] = "Driver not in kernel config", + [PERF_PMU_STATUS_NOT_SUPPORTED] = "Not supported by kernel", + [PERF_PMU_STATUS_WRONG_HW] = "Not supported by hardware", + [PERF_PMU_STATUS_WRONG_VENDOR] = "Wrong vendor", + [PERF_PMU_STATUS_WRONG_ARCH] = "Wrong architecture", + [PERF_PMU_STATUS_UNKNOWN] = "Unknown status", +}; + +static ssize_t __known_pmu_show(struct known_pmu *known_pmu, char *buf) +{ + const char *msg, *vendor; + int status; + + status = known_pmu->status; + msg = known_pmu->status_msg; + vendor = known_pmu->vendor; + + if (status < 0 || status >= PERF_PMU_STATUS_MAX) + status = PERF_PMU_STATUS_UNKNOWN; + + if (!msg && status == PERF_PMU_STATUS_WRONG_VENDOR && vendor) + return sprintf(buf, "%s - requires %s CPU\n", + pmu_status_msg[status], vendor); + + if (!msg) + return sprintf(buf, "%s\n", pmu_status_msg[status]); + + return sprintf(buf, "%s - %s\n", pmu_status_msg[status], msg); +} + +static ssize_t known_pmu_show(const char *name, char *buf) +{ + struct known_pmu *known_pmu = known_pmu_find(name); + ssize_t ret; + + if (!known_pmu) + return sprintf(buf, "Unknown PMU\n"); + + mutex_lock(&known_pmus_lock); + ret = __known_pmu_show(known_pmu, buf); + mutex_unlock(&known_pmus_lock); + + return ret; +} + +void perf_pmu_update_status(const char *name, enum perf_pmu_status status, + const char *status_msg) +{ + struct known_pmu *known_pmu = known_pmu_find(name); + + if (known_pmu) { + mutex_lock(&known_pmus_lock); + known_pmu->status = status; + kfree_const(known_pmus->status_msg); + known_pmus->status_msg = kstrdup_const(status_msg, GFP_KERNEL); + mutex_unlock(&known_pmus_lock); + } +} + +void perf_pmu_error_status(const char *name, int err, const char *status_msg) +{ + enum perf_pmu_status status; + + switch (err) { + case 0: + status = PERF_PMU_STATUS_SUPPORTED; + break; + case -ENODEV: + status = PERF_PMU_STATUS_WRONG_HW; + break; + case -ENOTSUPP: + status = PERF_PMU_STATUS_NOT_SUPPORTED; + break; + default: + status = PERF_PMU_STATUS_ERROR; + } + + perf_pmu_update_status(name, status, status_msg); +} + +void perf_pmu_vendor(const char *vendor) +{ + struct known_pmu *known_pmu; + + if (!vendor) + return; + + mutex_lock(&known_pmus_lock); + for (known_pmu = known_pmus; known_pmu->name; known_pmu++) { + if (known_pmu->status == PERF_PMU_STATUS_UNKNOWN && + known_pmu->vendor && strcmp(known_pmu->vendor, vendor)) { + known_pmu->status = PERF_PMU_STATUS_WRONG_VENDOR; + kfree_const(known_pmus->status_msg); + known_pmu->status_msg = NULL; + } + } + mutex_unlock(&known_pmus_lock); +} + +#define KNOWN_PMU_ATTR(_name) \ +static ssize_t _name##_show(struct bus_type *bus, char *buf) \ +{ \ + return known_pmu_show(__stringify(_name), buf); \ +} \ +static BUS_ATTR_RO(_name); + +KNOWN_PMU_ATTR(intel_pt); +KNOWN_PMU_ATTR(intel_bts); + +static struct attribute *known_pmus_attrs[] = { + &bus_attr_intel_pt.attr, + &bus_attr_intel_bts.attr, + NULL, +}; + +static const struct attribute_group pmu_bus_known_pmus_group = { + .name = "known_pmus", + .attrs = known_pmus_attrs, +}; + +static const struct attribute_group *pmu_bus_groups[] = { + &pmu_bus_known_pmus_group, + NULL, +}; + static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *page) { @@ -7224,6 +7394,7 @@ ATTRIBUTE_GROUPS(pmu_dev); static int pmu_bus_running; static struct bus_type pmu_bus = { .name = "event_source", + .bus_groups = pmu_bus_groups, .dev_groups = pmu_dev_groups, }; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/