* [PATCH v5 1/2] perf tests: mmap-basic: Only test userspace reading on supported platforms
2026-10-06 16:34 [PATCH v5 0/2] perf tests: mmap-basic: fix user rdpmc detection logic James Clark
@ 2026-10-06 16:34 ` James Clark
2026-10-06 16:42 ` sashiko-bot
2026-10-06 16:34 ` [PATCH v5 2/2] perf tests: mmap-basic: fix user rdpmc detection logic James Clark
1 sibling, 1 reply; 5+ messages in thread
From: James Clark @ 2026-10-06 16:34 UTC (permalink / raw)
To: Ian Rogers, Michael Petlan, Qiao Zhao, Arnaldo Carvalho de Melo,
Namhyung Kim
Cc: Leo Yan, Peter Zijlstra, Ingo Molnar, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Adrian Hunter, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-perf-users,
linux-kernel, linux-riscv, James Clark
mmap_user_read_instr() and mmap_user_read_instr_disabled() are the same
test for platforms that libperf doesn't support userspace counter
reading for. Only run the 'enabled' test for platforms that do support
it in order to simplify it in the next commit and not run the same test
twice. The test can then assert stronger guarantees and have fewer edge
cases to handle as we know it only runs in places where
'rdmpc_supported' will be true.
"unsupported" hasn't been a relevant skip reason since
commit 588d22b40480 ("perf test: Expand user space event reading (rdpmc)
tests") added the UNKNOWN fallback for unsupported platforms. Remove it
and leave only "permissions".
Signed-off-by: James Clark <james.clark@linaro.org>
---
tools/perf/tests/mmap-basic.c | 51 ++++++++++++++++---------------------------
1 file changed, 19 insertions(+), 32 deletions(-)
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 5cec7644952c..208cd889f23d 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -378,13 +378,13 @@ static int test_stat_user_read(u64 event, enum user_read_state enabled)
return ret;
}
-static int test__mmap_user_read_instr(struct test_suite *test __maybe_unused,
+static int __maybe_unused test__mmap_user_read_instr(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_ENABLED);
}
-static int test__mmap_user_read_cycles(struct test_suite *test __maybe_unused,
+static int __maybe_unused test__mmap_user_read_cycles(struct test_suite *test __maybe_unused,
int subtest __maybe_unused)
{
return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_ENABLED);
@@ -406,42 +406,29 @@ static struct test_case tests__basic_mmap[] = {
TEST_CASE_REASON("Read samples using the mmap interface",
basic_mmap,
"permissions"),
- TEST_CASE_REASON_EXCLUSIVE("User space counter reading of instructions",
- mmap_user_read_instr,
+
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
(defined(__riscv) && __riscv_xlen == 64)
- "permissions"
-#else
- "unsupported"
-#endif
- ),
+ /*
+ * libperf only supports userspace read for these platforms, see
+ * tools/lib/perf/mmap.c
+ */
+ TEST_CASE_REASON_EXCLUSIVE("User space counter reading of instructions",
+ mmap_user_read_instr,
+ "permissions"),
TEST_CASE_REASON_EXCLUSIVE("User space counter reading of cycles",
- mmap_user_read_cycles,
-#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
- (defined(__riscv) && __riscv_xlen == 64)
- "permissions"
-#else
- "unsupported"
+ mmap_user_read_cycles,
+ "permissions"),
#endif
- ),
+
+ /* Counter read via userpage fallback is supported on all platforms */
TEST_CASE_REASON_EXCLUSIVE("User space counter disabling instructions",
- mmap_user_read_instr_disabled,
-#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
- (defined(__riscv) && __riscv_xlen == 64)
- "permissions"
-#else
- "unsupported"
-#endif
- ),
+ mmap_user_read_instr_disabled,
+ "permissions"),
TEST_CASE_REASON_EXCLUSIVE("User space counter disabling cycles",
- mmap_user_read_cycles_disabled,
-#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
- (defined(__riscv) && __riscv_xlen == 64)
- "permissions"
-#else
- "unsupported"
-#endif
- ),
+ mmap_user_read_cycles_disabled,
+ "permissions"),
+
{ .name = NULL, }
};
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v5 2/2] perf tests: mmap-basic: fix user rdpmc detection logic
2026-10-06 16:34 [PATCH v5 0/2] perf tests: mmap-basic: fix user rdpmc detection logic James Clark
2026-10-06 16:34 ` [PATCH v5 1/2] perf tests: mmap-basic: Only test userspace reading on supported platforms James Clark
@ 2026-10-06 16:34 ` James Clark
2026-10-06 16:50 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: James Clark @ 2026-10-06 16:34 UTC (permalink / raw)
To: Ian Rogers, Michael Petlan, Qiao Zhao, Arnaldo Carvalho de Melo,
Namhyung Kim
Cc: Leo Yan, Peter Zijlstra, Ingo Molnar, Mark Rutland,
Alexander Shishkin, Jiri Olsa, Adrian Hunter, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-perf-users,
linux-kernel, linux-riscv, James Clark
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: Qiao Zhao <qzhao@redhat.com>
[Re-write to fix bugs in set_user_read() and simplify tests]
Assisted-by: Codex:GPT-6.1-Sol
Signed-off-by: James Clark <james.clark@linaro.org>
---
tools/perf/tests/mmap-basic.c | 131 ++++++++++++++++++++++++++++--------------
1 file changed, 88 insertions(+), 43 deletions(-)
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c
index 208cd889f23d..2c3276e82da8 100644
--- a/tools/perf/tests/mmap-basic.c
+++ b/tools/perf/tests/mmap-basic.c
@@ -12,6 +12,8 @@
#include <perf/evlist.h>
#include <perf/mmap.h>
+#include <tools/config.h>
+
#include "cpumap.h"
#include "debug.h"
#include "event.h"
@@ -182,50 +184,79 @@ static int test__basic_mmap(struct test_suite *test __maybe_unused, int subtest
}
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;
-
- rdpmc_fd = perf_pmu__pathname_fd(events_fd, pmu->name, "rdpmc", O_RDWR);
- if (rdpmc_fd < 0) {
- close(events_fd);
- return USER_READ_UNKNOWN;
- }
+ ssize_t len = read(fd, buf, 1);
+ enum user_read_state old_user_read = USER_READ_ERR_NO_FILE;
- len = read(rdpmc_fd, buf, sizeof(buf));
- if (len != sizeof(buf))
+ if (len <= 0)
pr_debug("%s read failed\n", __func__);
-
- // 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);
+
+ 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)
+ return USER_READ_ERR_NO_FILE;
+
+ 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) {
+ /* 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)
+ 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 +277,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 +284,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;
+ continue;
+ }
+
+ /* 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;
+ continue;
+ }
+
/*
* 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 +349,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;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread