From: Adrian Hunter <adrian.hunter@intel.com>
To: Peter Zijlstra <peterz@infradead.org>, Ingo Molnar <mingo@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>,
Andy Lutomirski <luto@amacapital.net>,
Vince Weaver <vincent.weaver@maine.edu>,
Thomas Gleixner <tglx@linutronix.de>,
"H. Peter Anvin" <hpa@linux.intel.com>,
linux-kernel@vger.kernel.org, Jiri Olsa <jolsa@redhat.com>,
Stephane Eranian <eranian@google.com>,
Borislav Petkov <bp@suse.de>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Andi Kleen <ak@linux.intel.com>
Subject: [RFC PATCH] perf: Provide status of known PMUs
Date: Thu, 9 Jul 2015 10:48:00 +0300 [thread overview]
Message-ID: <1436428080-3098-1-git-send-email-adrian.hunter@intel.com> (raw)
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 <adrian.hunter@intel.com>
---
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
next reply other threads:[~2015-07-09 7:50 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-09 7:48 Adrian Hunter [this message]
2015-07-09 8:10 ` [RFC PATCH] perf: Provide status of known PMUs Ingo Molnar
2015-07-09 8:44 ` Adrian Hunter
2015-07-09 8:50 ` Peter Zijlstra
2015-07-09 9:26 ` Ingo Molnar
2015-07-09 11:59 ` Peter Zijlstra
2015-07-09 12:32 ` Ingo Molnar
2015-07-09 12:42 ` Peter Zijlstra
2015-07-09 14:47 ` Arnaldo Carvalho de Melo
2015-07-10 8:35 ` Ingo Molnar
2015-07-10 18:59 ` Stephane Eranian
2015-07-09 9:30 ` Adrian Hunter
2015-07-09 11:44 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1436428080-3098-1-git-send-email-adrian.hunter@intel.com \
--to=adrian.hunter@intel.com \
--cc=acme@kernel.org \
--cc=ak@linux.intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=bp@suse.de \
--cc=eranian@google.com \
--cc=hpa@linux.intel.com \
--cc=jolsa@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=mingo@kernel.org \
--cc=peterz@infradead.org \
--cc=tglx@linutronix.de \
--cc=vincent.weaver@maine.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.