summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorJames Clark <james.clark@linaro.org>2026-10-07 11:52:13 +0100
committerArnaldo Carvalho de Melo <acme@redhat.com>2026-10-27 17:40:24 -0300
commitfe7b73c293f1a5b0b7d8a4748b4c076fa7db852c (patch)
tree9c47a74041df06934d021db214593bf5d6904d5b /tools
parente793b6a4be3c7810c92eeeb21bfd0f03c22fcbcb (diff)
perf tests mmap-basic: Fix user rdpmc detection logic
RISC-V and Arm control userspace counter access through /proc/sys/kernel/perf_user_access. Add that as a fallback to set_user_read() so the test can exercise both enabled and disabled states on those platforms. Document disabled, enabled and legacy states in user_read_state and fail on values outside the known 0-2 range. Preserve and restore any value in this range, rather than just 0 or 1. Skip when the control files exist but can't be written to (USER_READ_ERR_PERM). We can't rely on always being able to write to them when an exclude_kernel=0 event can be opened because opening events might succeed for non-root users when perf_event_paranoid=-1. This test isn't run on unsupported platforms since the previous commit, so we can simplify the following things: * Test that cap_user_rdpmc is always equal to the requested state rather than checking it only when disabled. As long as we stop setting the cap in the attr unconditionally on Arm, this is ok. * Remove the USER_READ_UNKNOWN/rdpmc_supported fallback in the checks. Now the expected state is always the one requested. USER_READ_UNKNOWN only controls whether to skip restoration of the state on unsupported platforms, so call it USER_READ_ERR_NO_FILE. Signed-off-by: James Clark <james.clark@linaro.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Alexandre Ghiti <alex@ghiti.fr> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Leo Yan <leo.yan@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Michael Petlan <mpetlan@redhat.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <pjw@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Assisted-by: Codex:GPT-6.1-Sol [Re-write: [Re-write to fix bugs in set_user_read() and simplify tests] Signed-off-by: Qiao Zhao <qzhao@redhat.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/tests/mmap-basic.c137
1 files changed, 94 insertions, 43 deletions
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index bd9b9cce6a64..602a78d48b10 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
+#include <unistd.h>
#include <fcntl.h>
#include <linux/err.h>
@@ -12,6 +13,8 @@
#include <perf/evlist.h>
#include <perf/mmap.h>
+#include <tools/config.h>
+
#include "cpumap.h"
#include "debug.h"
#include "event.h"
@@ -182,50 +185,83 @@ out_free_threads:
}
enum user_read_state {
- USER_READ_ENABLED,
- USER_READ_DISABLED,
- USER_READ_UNKNOWN,
+ USER_READ_ERR_PERM = -2,
+ USER_READ_ERR_NO_FILE = -1,
+
+ USER_READ_DISABLED = '0',
+ USER_READ_ENABLED = '1',
+#if defined(__i386__) || defined(__x86_64__) || (defined(__riscv) && __riscv_xlen == 64)
+ /* Unrestricted access on x86, legacy access on RISC-V. */
+ USER_READ_LEGACY = '2',
+#endif
+ USER_READ_MAX
};
-static enum user_read_state set_user_read(struct perf_pmu *pmu, enum user_read_state enabled)
+static enum user_read_state user_read_file_op(int fd, enum user_read_state enabled)
{
char buf[2] = {0, '\n'};
- ssize_t len;
- int events_fd, rdpmc_fd;
- enum user_read_state old_user_read = USER_READ_UNKNOWN;
-
- if (enabled == USER_READ_UNKNOWN)
- return USER_READ_UNKNOWN;
-
- events_fd = perf_pmu__event_source_devices_fd();
- if (events_fd < 0)
- return USER_READ_UNKNOWN;
+ ssize_t len = read(fd, buf, 1);
+ enum user_read_state old_user_read = USER_READ_ERR_NO_FILE;
- rdpmc_fd = perf_pmu__pathname_fd(events_fd, pmu->name, "rdpmc", O_RDWR);
- if (rdpmc_fd < 0) {
- close(events_fd);
- return USER_READ_UNKNOWN;
- }
-
- len = read(rdpmc_fd, buf, sizeof(buf));
- if (len != sizeof(buf))
+ if (len <= 0) {
pr_debug("%s read failed\n", __func__);
+ goto out;
+ }
- // Note, on Intel hybrid disabling on 1 PMU will implicitly disable on
- // all the core PMUs.
- old_user_read = (buf[0] == '1') ? USER_READ_ENABLED : USER_READ_DISABLED;
+ old_user_read = buf[0];
if (enabled != old_user_read) {
- buf[0] = (enabled == USER_READ_ENABLED) ? '1' : '0';
- len = write(rdpmc_fd, buf, sizeof(buf));
+ buf[0] = enabled;
+ lseek(fd, 0, SEEK_SET);
+ len = write(fd, buf, sizeof(buf));
if (len != sizeof(buf))
pr_debug("%s write failed\n", __func__);
}
- close(rdpmc_fd);
- close(events_fd);
+
+out:
+ close(fd);
return old_user_read;
}
+static enum user_read_state set_user_read(struct perf_pmu *pmu,
+ enum user_read_state enabled)
+{
+ int events_fd, fd;
+ enum user_read_state ret = USER_READ_ERR_NO_FILE;
+
+ if (enabled == USER_READ_ERR_NO_FILE || enabled == USER_READ_ERR_PERM)
+ return enabled;
+
+ events_fd = perf_pmu__event_source_devices_fd();
+ if (events_fd >= 0) {
+ fd = perf_pmu__pathname_fd(events_fd, pmu->name, "rdpmc", O_RDWR);
+ if (fd >= 0) {
+ /*
+ * Note, on Intel hybrid disabling on 1 PMU will implicitly
+ * disable on all the core PMUs.
+ */
+ ret = user_read_file_op(fd, enabled);
+ close(events_fd);
+ return ret;
+ } else if (errno == EACCES || errno == EROFS) {
+ /* Permissions failure, flag the failure for a skip. */
+ close(events_fd);
+ return USER_READ_ERR_PERM;
+ }
+ close(events_fd);
+ }
+
+ /* Fallback: perf_user_access interface (arm64, riscv, or similar) */
+ fd = open("/proc/sys/kernel/perf_user_access", O_RDWR);
+ if (fd >= 0)
+ ret = user_read_file_op(fd, enabled);
+ else if (errno == EACCES || errno == EROFS)
+ ret = USER_READ_ERR_PERM;
+
+ return ret;
+}
+
+
static int test_stat_user_read(u64 event, enum user_read_state enabled)
{
struct perf_pmu *pmu = NULL;
@@ -246,9 +282,6 @@ static int test_stat_user_read(u64 event, enum user_read_state enabled)
.config = perf_pmus__supports_extended_type()
? event | ((u64)pmu->type << PERF_PMU_TYPE_SHIFT)
: event,
-#ifdef __aarch64__
- .config1 = 0x2, /* Request user access */
-#endif
};
struct perf_evsel *evsel = NULL;
int err;
@@ -256,8 +289,28 @@ static int test_stat_user_read(u64 event, enum user_read_state enabled)
bool mapped = false, opened = false, rdpmc_supported;
struct perf_counts_values counts = { .val = 0 };
+ if (__is_defined(__aarch64__) && enabled == USER_READ_ENABLED)
+ attr.config1 = 0x2; /* Request user access */
pr_debug("User space counter reading for PMU %s\n", pmu->name);
+
+ if (saved_user_read_state == USER_READ_ERR_PERM) {
+ pr_debug("User space counter reading for PMU %s [Failed to get user read state]\n",
+ pmu->name);
+ ret = TEST_SKIP;
+ goto cleanup;
+ }
+
+ /* Check value is in the expected range */
+ if (saved_user_read_state != USER_READ_ERR_NO_FILE &&
+ (saved_user_read_state < USER_READ_DISABLED ||
+ saved_user_read_state >= USER_READ_MAX)) {
+ pr_err("User space counter reading for PMU %s [Unexpected current state %d]\n",
+ pmu->name, saved_user_read_state);
+ ret = TEST_FAIL;
+ goto cleanup;
+ }
+
/*
* Restrict scheduling to only use the rdpmc on the CPUs the
* event can be on. If the test doesn't run on the CPU of the
@@ -301,21 +354,18 @@ static int test_stat_user_read(u64 event, enum user_read_state enabled)
goto cleanup;
}
- if (saved_user_read_state == USER_READ_UNKNOWN)
- rdpmc_supported = pc->cap_user_rdpmc && pc->index;
- else
- rdpmc_supported = (enabled == USER_READ_ENABLED);
+ rdpmc_supported = (enabled == USER_READ_ENABLED);
- if (rdpmc_supported && (!pc->cap_user_rdpmc || !pc->index)) {
- pr_err("User space counter reading for PMU %s [Failed unexpected supported counter access %d %d]\n",
- pmu->name, pc->cap_user_rdpmc, pc->index);
+ if (rdpmc_supported && !pc->index) {
+ pr_err("User space counter reading for PMU %s [index must be set when enabled %d]\n",
+ pmu->name, pc->index);
ret = TEST_FAIL;
goto cleanup;
}
- if (!rdpmc_supported && pc->cap_user_rdpmc) {
- pr_err("User space counter reading for PMU %s [Failed unexpected unsupported counter access %d]\n",
- pmu->name, pc->cap_user_rdpmc);
+ if (rdpmc_supported != pc->cap_user_rdpmc) {
+ pr_err("User space counter reading for PMU %s [cap should match requested state %d %d]\n",
+ pmu->name, pc->cap_user_rdpmc, rdpmc_supported);
ret = TEST_FAIL;
goto cleanup;
}
@@ -363,7 +413,8 @@ cleanup:
perf_evsel__munmap(evsel);
if (opened)
perf_evsel__close(evsel);
- perf_evsel__delete(evsel);
+ if (evsel)
+ perf_evsel__delete(evsel);
/* If the affinity was changed, then put it back to all CPUs. */
if (pmu->cpus != NULL) {