[RFC 5/5] tools/perf: Add support for per-PMU access control
From: Tvrtko Ursulin
Date: Wed Sep 19 2018 - 08:28:09 EST
From: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx>
Now that the perf core supports per-PMU paranoid settings we need to
extend the tool to support that.
We handle the per-PMU setting in the platform support code where
applicable and also notify the user of the new facility on failures to
open the event.
Thanks to Jiri Olsa for contributing most of the "meat" for this patch and
suggestion to improve the error banner as well.
Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@xxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: "H. Peter Anvin" <hpa@xxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
Cc: Madhavan Srinivasan <maddy@xxxxxxxxxxxxxxxxxx>
Cc: Andi Kleen <ak@xxxxxxxxxxxxxxx>
Cc: Alexey Budankov <alexey.budankov@xxxxxxxxxxxxxxx>
Cc: linux-kernel@xxxxxxxxxxxxxxx
Cc: x86@xxxxxxxxxx
---
tools/perf/arch/arm/util/cs-etm.c | 2 +-
tools/perf/arch/arm64/util/arm-spe.c | 2 +-
tools/perf/arch/x86/util/intel-bts.c | 2 +-
tools/perf/arch/x86/util/intel-pt.c | 2 +-
tools/perf/util/evsel.c | 41 ++++++++++++++++++++++++++--
tools/perf/util/pmu.c | 17 ++++++++++++
tools/perf/util/pmu.h | 1 +
7 files changed, 60 insertions(+), 7 deletions(-)
diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c
index 2f595cd73da6..a5437f100ab9 100644
--- a/tools/perf/arch/arm/util/cs-etm.c
+++ b/tools/perf/arch/arm/util/cs-etm.c
@@ -69,7 +69,7 @@ static int cs_etm_recording_options(struct auxtrace_record *itr,
struct perf_pmu *cs_etm_pmu = ptr->cs_etm_pmu;
struct perf_evsel *evsel, *cs_etm_evsel = NULL;
const struct cpu_map *cpus = evlist->cpus;
- bool privileged = (geteuid() == 0 || perf_event_paranoid() < 0);
+ bool privileged = (geteuid() == 0 || cs_etm_pmu->paranoid < 0);
ptr->evlist = evlist;
ptr->snapshot_mode = opts->auxtrace_snapshot_mode;
diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c
index 5ccfce87e693..e7e8154757fa 100644
--- a/tools/perf/arch/arm64/util/arm-spe.c
+++ b/tools/perf/arch/arm64/util/arm-spe.c
@@ -65,7 +65,7 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
container_of(itr, struct arm_spe_recording, itr);
struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
struct perf_evsel *evsel, *arm_spe_evsel = NULL;
- bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
+ bool privileged = geteuid() == 0 || arm_spe_pmu->paranoid < 0;
struct perf_evsel *tracking_evsel;
int err;
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c
index 781df40b2966..c97e6556c8e7 100644
--- a/tools/perf/arch/x86/util/intel-bts.c
+++ b/tools/perf/arch/x86/util/intel-bts.c
@@ -116,7 +116,7 @@ static int intel_bts_recording_options(struct auxtrace_record *itr,
struct perf_pmu *intel_bts_pmu = btsr->intel_bts_pmu;
struct perf_evsel *evsel, *intel_bts_evsel = NULL;
const struct cpu_map *cpus = evlist->cpus;
- bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
+ bool privileged = geteuid() == 0 || intel_bts_pmu->paranoid < 0;
btsr->evlist = evlist;
btsr->snapshot_mode = opts->auxtrace_snapshot_mode;
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c
index db0ba8caf5a2..ffbe5f7f1c57 100644
--- a/tools/perf/arch/x86/util/intel-pt.c
+++ b/tools/perf/arch/x86/util/intel-pt.c
@@ -555,7 +555,7 @@ static int intel_pt_recording_options(struct auxtrace_record *itr,
bool have_timing_info, need_immediate = false;
struct perf_evsel *evsel, *intel_pt_evsel = NULL;
const struct cpu_map *cpus = evlist->cpus;
- bool privileged = geteuid() == 0 || perf_event_paranoid() < 0;
+ bool privileged = geteuid() == 0 || intel_pt_pmu->paranoid < 0;
u64 tsc_bit;
int err;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 1a61628a1c12..fbebce0593f4 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -9,6 +9,7 @@
#include <byteswap.h>
#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <linux/bitops.h>
#include <api/fs/fs.h>
@@ -20,6 +21,7 @@
#include <linux/err.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include "asm/bug.h"
@@ -2843,10 +2845,37 @@ static bool find_process(const char *name)
return ret ? false : true;
}
+static int __pmu_paranoid_value(const char *name, char *buf, int bufsz)
+{
+ char path[PATH_MAX];
+ int fd, ret;
+
+ ret = snprintf(path, sizeof(path),
+ "%s/bus/event_source/devices/%s/perf_event_paranoid",
+ sysfs__mountpoint(), name);
+ if (ret < 0 || ret == sizeof(path))
+ return -1;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ ret = read(fd, buf, bufsz - 1);
+ if (ret <= 0)
+ return -1;
+
+ if (buf[ret - 1] == '\n')
+ buf[ret - 1] = 0;
+ else
+ buf[ret] = 0;
+
+ return 0;
+}
+
int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
int err, char *msg, size_t size)
{
- char sbuf[STRERR_BUFSIZE];
+ char sbuf[STRERR_BUFSIZE], buf[4];
int printed = 0;
switch (err) {
@@ -2870,9 +2899,15 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,
">= 1: Disallow CPU event access by users without CAP_SYS_ADMIN\n"
">= 2: Disallow kernel profiling by users without CAP_SYS_ADMIN\n\n"
"To make this setting permanent, edit /etc/sysctl.conf too, e.g.:\n\n"
- " kernel.perf_event_paranoid = -1\n" ,
+ " kernel.perf_event_paranoid = -1\n\n"
+ "Alternatively an identical per PMU setting can be found and adjusted at\n"
+ "/sys/bus/event_source/devices/%s/perf_event_paranoid for fine-grained\n"
+ "access control. The current value is '%s'.\n",
target->system_wide ? "system-wide " : "",
- perf_event_paranoid());
+ perf_event_paranoid(),
+ evsel->pmu_name,
+ __pmu_paranoid_value(evsel->pmu_name, buf,
+ sizeof(buf)) ? "-" : buf);
case ENOENT:
return scnprintf(msg, size, "The %s event is not supported.",
perf_evsel__name(evsel));
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index afd68524ffa9..5ecead4969af 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -545,6 +545,22 @@ static int pmu_type(const char *name, __u32 *type)
return ret;
}
+static int pmu_paranoid(const char *name)
+{
+ char path[PATH_MAX];
+ int ret, paranoid;
+
+ ret = snprintf(path, sizeof(path),
+ EVENT_SOURCE_DEVICE_PATH "%s/perf_event_paranoid",
+ name);
+
+ if (ret > 0 && ret < (int)sizeof(path) &&
+ !sysfs__read_int(path, ¶noid))
+ return paranoid;
+
+ return perf_event_paranoid();
+}
+
/* Add all pmus in sysfs to pmu list: */
static void pmu_read_sysfs(void)
{
@@ -825,6 +841,7 @@ static struct perf_pmu *pmu_lookup(const char *name)
pmu->name = strdup(name);
pmu->type = type;
pmu->is_uncore = pmu_is_uncore(name);
+ pmu->paranoid = pmu_paranoid(name);
pmu_add_cpu_aliases(&aliases, pmu);
INIT_LIST_HEAD(&pmu->format);
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 76fecec7b3f9..1f00c8bbdb90 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -30,6 +30,7 @@ struct perf_pmu {
struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
struct list_head list; /* ELEM */
int (*set_drv_config) (struct perf_evsel_config_term *term);
+ int paranoid;
};
struct perf_pmu_info {
--
2.17.1