diff options
Diffstat (limited to 'tools/perf/tests/mmap-basic.c')
| -rw-r--r-- | tools/perf/tests/mmap-basic.c | 189 |
1 files changed, 114 insertions, 75 deletions
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 5cec7644952c..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; - - 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__); + 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) { @@ -378,13 +429,13 @@ cleanup: 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,46 +457,34 @@ 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, } }; struct test_suite suite__basic_mmap = { .desc = "mmap interface tests", .test_cases = tests__basic_mmap, + .needs_mlock = true, }; |
