Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf test: Expand user space event reading (rdpmc) tests

Test that disabling rdpmc support via /sys/bus/event_source/cpu*/rdpmc
disables reading in the mmap (libperf read support will fallback to
using a system call).
Test all hybrid PMUs support rdpmc.
Ensure hybrid PMUs use the correct CPU to rdpmc the correct
event. Previously the test would open cycles or instructions with no
extended type then rdpmc it on whatever CPU. This could fail/skip due
to which CPU the test was scheduled upon.

Signed-off-by: Ian Rogers <irogers@google.com>
Reviewed-by: Kan Liang <kan.liang@linux.intel.com>
Link: https://lore.kernel.org/r/20250614004528.1652860-1-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
588d22b4 ce3d5af2

+249 -87
+220 -87
tools/perf/tests/mmap-basic.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 #include <errno.h> 3 + #include <fcntl.h> 3 4 #include <inttypes.h> 4 5 #include <stdlib.h> 5 6 #include <perf/cpumap.h> 6 7 8 + #include "cpumap.h" 7 9 #include "debug.h" 8 10 #include "event.h" 9 11 #include "evlist.h" 10 12 #include "evsel.h" 11 13 #include "thread_map.h" 12 14 #include "tests.h" 15 + #include "util/affinity.h" 13 16 #include "util/mmap.h" 14 17 #include "util/sample.h" 15 18 #include <linux/err.h> ··· 175 172 return err; 176 173 } 177 174 178 - static int test_stat_user_read(int event) 175 + enum user_read_state { 176 + USER_READ_ENABLED, 177 + USER_READ_DISABLED, 178 + USER_READ_UNKNOWN, 179 + }; 180 + 181 + static enum user_read_state set_user_read(struct perf_pmu *pmu, enum user_read_state enabled) 179 182 { 180 - struct perf_counts_values counts = { .val = 0 }; 181 - struct perf_thread_map *threads; 182 - struct perf_evsel *evsel; 183 - struct perf_event_mmap_page *pc; 184 - struct perf_event_attr attr = { 185 - .type = PERF_TYPE_HARDWARE, 186 - .config = event, 187 - #ifdef __aarch64__ 188 - .config1 = 0x2, /* Request user access */ 189 - #endif 190 - }; 191 - int err, i, ret = TEST_FAIL; 192 - bool opened = false, mapped = false; 183 + char buf[2] = {0, '\n'}; 184 + ssize_t len; 185 + int events_fd, rdpmc_fd; 186 + enum user_read_state old_user_read = USER_READ_UNKNOWN; 193 187 194 - threads = perf_thread_map__new_dummy(); 195 - TEST_ASSERT_VAL("failed to create threads", threads); 188 + if (enabled == USER_READ_UNKNOWN) 189 + return USER_READ_UNKNOWN; 196 190 191 + events_fd = perf_pmu__event_source_devices_fd(); 192 + if (events_fd < 0) 193 + return USER_READ_UNKNOWN; 194 + 195 + rdpmc_fd = perf_pmu__pathname_fd(events_fd, pmu->name, "rdpmc", O_RDWR); 196 + if (rdpmc_fd < 0) { 197 + close(events_fd); 198 + return USER_READ_UNKNOWN; 199 + } 200 + 201 + len = read(rdpmc_fd, buf, sizeof(buf)); 202 + if (len != sizeof(buf)) 203 + pr_debug("%s read failed\n", __func__); 204 + 205 + // Note, on Intel hybrid disabling on 1 PMU will implicitly disable on 206 + // all the core PMUs. 207 + old_user_read = (buf[0] == '1') ? USER_READ_ENABLED : USER_READ_DISABLED; 208 + 209 + if (enabled != old_user_read) { 210 + buf[0] = (enabled == USER_READ_ENABLED) ? '1' : '0'; 211 + len = write(rdpmc_fd, buf, sizeof(buf)); 212 + if (len != sizeof(buf)) 213 + pr_debug("%s write failed\n", __func__); 214 + } 215 + close(rdpmc_fd); 216 + close(events_fd); 217 + return old_user_read; 218 + } 219 + 220 + static int test_stat_user_read(u64 event, enum user_read_state enabled) 221 + { 222 + struct perf_pmu *pmu = NULL; 223 + struct perf_thread_map *threads = perf_thread_map__new_dummy(); 224 + int ret = TEST_OK; 225 + 226 + pr_err("User space counter reading %" PRIu64 "\n", event); 227 + if (!threads) { 228 + pr_err("User space counter reading [Failed to create threads]\n"); 229 + return TEST_FAIL; 230 + } 197 231 perf_thread_map__set_pid(threads, 0, 0); 198 232 199 - evsel = perf_evsel__new(&attr); 200 - TEST_ASSERT_VAL("failed to create evsel", evsel); 233 + while ((pmu = perf_pmus__scan_core(pmu)) != NULL) { 234 + enum user_read_state saved_user_read_state = set_user_read(pmu, enabled); 235 + struct perf_event_attr attr = { 236 + .type = PERF_TYPE_HARDWARE, 237 + .config = perf_pmus__supports_extended_type() 238 + ? event | ((u64)pmu->type << PERF_PMU_TYPE_SHIFT) 239 + : event, 240 + #ifdef __aarch64__ 241 + .config1 = 0x2, /* Request user access */ 242 + #endif 243 + }; 244 + struct perf_evsel *evsel = NULL; 245 + int err; 246 + struct perf_event_mmap_page *pc; 247 + bool mapped = false, opened = false, rdpmc_supported; 248 + struct perf_counts_values counts = { .val = 0 }; 201 249 202 - err = perf_evsel__open(evsel, NULL, threads); 203 - if (err) { 204 - pr_err("failed to open evsel: %s\n", strerror(-err)); 205 - ret = TEST_SKIP; 206 - goto out; 207 - } 208 - opened = true; 209 250 210 - err = perf_evsel__mmap(evsel, 0); 211 - if (err) { 212 - pr_err("failed to mmap evsel: %s\n", strerror(-err)); 213 - goto out; 214 - } 215 - mapped = true; 251 + pr_debug("User space counter reading for PMU %s\n", pmu->name); 252 + /* 253 + * Restrict scheduling to only use the rdpmc on the CPUs the 254 + * event can be on. If the test doesn't run on the CPU of the 255 + * event then the event will be disabled and the pc->index test 256 + * will fail. 257 + */ 258 + if (pmu->cpus != NULL) 259 + cpu_map__set_affinity(pmu->cpus); 216 260 217 - pc = perf_evsel__mmap_base(evsel, 0, 0); 218 - if (!pc) { 219 - pr_err("failed to get mmapped address\n"); 220 - goto out; 221 - } 222 - 223 - if (!pc->cap_user_rdpmc || !pc->index) { 224 - pr_err("userspace counter access not %s\n", 225 - !pc->cap_user_rdpmc ? "supported" : "enabled"); 226 - ret = TEST_SKIP; 227 - goto out; 228 - } 229 - if (pc->pmc_width < 32) { 230 - pr_err("userspace counter width not set (%d)\n", pc->pmc_width); 231 - goto out; 232 - } 233 - 234 - perf_evsel__read(evsel, 0, 0, &counts); 235 - if (counts.val == 0) { 236 - pr_err("failed to read value for evsel\n"); 237 - goto out; 238 - } 239 - 240 - for (i = 0; i < 5; i++) { 241 - volatile int count = 0x10000 << i; 242 - __u64 start, end, last = 0; 243 - 244 - pr_debug("\tloop = %u, ", count); 245 - 246 - perf_evsel__read(evsel, 0, 0, &counts); 247 - start = counts.val; 248 - 249 - while (count--) ; 250 - 251 - perf_evsel__read(evsel, 0, 0, &counts); 252 - end = counts.val; 253 - 254 - if ((end - start) < last) { 255 - pr_err("invalid counter data: end=%llu start=%llu last= %llu\n", 256 - end, start, last); 257 - goto out; 261 + /* Make the evsel. */ 262 + evsel = perf_evsel__new(&attr); 263 + if (!evsel) { 264 + pr_err("User space counter reading for PMU %s [Failed to allocate evsel]\n", 265 + pmu->name); 266 + ret = TEST_FAIL; 267 + goto cleanup; 258 268 } 259 - last = end - start; 260 - pr_debug("count = %llu\n", end - start); 269 + 270 + err = perf_evsel__open(evsel, NULL, threads); 271 + if (err) { 272 + pr_err("User space counter reading for PMU %s [Failed to open evsel]\n", 273 + pmu->name); 274 + ret = TEST_SKIP; 275 + goto cleanup; 276 + } 277 + opened = true; 278 + err = perf_evsel__mmap(evsel, 0); 279 + if (err) { 280 + pr_err("User space counter reading for PMU %s [Failed to mmap evsel]\n", 281 + pmu->name); 282 + ret = TEST_FAIL; 283 + goto cleanup; 284 + } 285 + mapped = true; 286 + 287 + pc = perf_evsel__mmap_base(evsel, 0, 0); 288 + if (!pc) { 289 + pr_err("User space counter reading for PMU %s [Failed to get mmaped address]\n", 290 + pmu->name); 291 + ret = TEST_FAIL; 292 + goto cleanup; 293 + } 294 + 295 + if (saved_user_read_state == USER_READ_UNKNOWN) 296 + rdpmc_supported = pc->cap_user_rdpmc && pc->index; 297 + else 298 + rdpmc_supported = (enabled == USER_READ_ENABLED); 299 + 300 + if (rdpmc_supported && (!pc->cap_user_rdpmc || !pc->index)) { 301 + pr_err("User space counter reading for PMU %s [Failed unexpected supported counter access %d %d]\n", 302 + pmu->name, pc->cap_user_rdpmc, pc->index); 303 + ret = TEST_FAIL; 304 + goto cleanup; 305 + } 306 + 307 + if (!rdpmc_supported && pc->cap_user_rdpmc) { 308 + pr_err("User space counter reading for PMU %s [Failed unexpected unsupported counter access %d]\n", 309 + pmu->name, pc->cap_user_rdpmc); 310 + ret = TEST_FAIL; 311 + goto cleanup; 312 + } 313 + 314 + if (rdpmc_supported && pc->pmc_width < 32) { 315 + pr_err("User space counter reading for PMU %s [Failed width not set %d]\n", 316 + pmu->name, pc->pmc_width); 317 + ret = TEST_FAIL; 318 + goto cleanup; 319 + } 320 + 321 + perf_evsel__read(evsel, 0, 0, &counts); 322 + if (counts.val == 0) { 323 + pr_err("User space counter reading for PMU %s [Failed read]\n", pmu->name); 324 + ret = TEST_FAIL; 325 + goto cleanup; 326 + } 327 + 328 + for (int i = 0; i < 5; i++) { 329 + volatile int count = 0x10000 << i; 330 + __u64 start, end, last = 0; 331 + 332 + pr_debug("\tloop = %u, ", count); 333 + 334 + perf_evsel__read(evsel, 0, 0, &counts); 335 + start = counts.val; 336 + 337 + while (count--) ; 338 + 339 + perf_evsel__read(evsel, 0, 0, &counts); 340 + end = counts.val; 341 + 342 + if ((end - start) < last) { 343 + pr_err("User space counter reading for PMU %s [Failed invalid counter data: end=%llu start=%llu last= %llu]\n", 344 + pmu->name, end, start, last); 345 + ret = TEST_FAIL; 346 + goto cleanup; 347 + } 348 + last = end - start; 349 + pr_debug("count = %llu\n", last); 350 + } 351 + pr_debug("User space counter reading for PMU %s [Success]\n", pmu->name); 352 + cleanup: 353 + if (mapped) 354 + perf_evsel__munmap(evsel); 355 + if (opened) 356 + perf_evsel__close(evsel); 357 + perf_evsel__delete(evsel); 358 + 359 + /* If the affinity was changed, then put it back to all CPUs. */ 360 + if (pmu->cpus != NULL) { 361 + struct perf_cpu_map *cpus = cpu_map__online(); 362 + 363 + cpu_map__set_affinity(cpus); 364 + perf_cpu_map__put(cpus); 365 + } 366 + set_user_read(pmu, saved_user_read_state); 261 367 } 262 - ret = TEST_OK; 263 - 264 - out: 265 - if (mapped) 266 - perf_evsel__munmap(evsel); 267 - if (opened) 268 - perf_evsel__close(evsel); 269 - perf_evsel__delete(evsel); 270 - 271 368 perf_thread_map__put(threads); 272 369 return ret; 273 370 } ··· 375 272 static int test__mmap_user_read_instr(struct test_suite *test __maybe_unused, 376 273 int subtest __maybe_unused) 377 274 { 378 - return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS); 275 + return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_ENABLED); 379 276 } 380 277 381 278 static int test__mmap_user_read_cycles(struct test_suite *test __maybe_unused, 382 279 int subtest __maybe_unused) 383 280 { 384 - return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES); 281 + return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_ENABLED); 282 + } 283 + 284 + static int test__mmap_user_read_instr_disabled(struct test_suite *test __maybe_unused, 285 + int subtest __maybe_unused) 286 + { 287 + return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_DISABLED); 288 + } 289 + 290 + static int test__mmap_user_read_cycles_disabled(struct test_suite *test __maybe_unused, 291 + int subtest __maybe_unused) 292 + { 293 + return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_DISABLED); 385 294 } 386 295 387 296 static struct test_case tests__basic_mmap[] = { 388 297 TEST_CASE_REASON("Read samples using the mmap interface", 389 298 basic_mmap, 390 299 "permissions"), 391 - TEST_CASE_REASON("User space counter reading of instructions", 300 + TEST_CASE_REASON_EXCLUSIVE("User space counter reading of instructions", 392 301 mmap_user_read_instr, 393 302 #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ 394 303 (defined(__riscv) && __riscv_xlen == 64) ··· 409 294 "unsupported" 410 295 #endif 411 296 ), 412 - TEST_CASE_REASON("User space counter reading of cycles", 297 + TEST_CASE_REASON_EXCLUSIVE("User space counter reading of cycles", 413 298 mmap_user_read_cycles, 299 + #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ 300 + (defined(__riscv) && __riscv_xlen == 64) 301 + "permissions" 302 + #else 303 + "unsupported" 304 + #endif 305 + ), 306 + TEST_CASE_REASON_EXCLUSIVE("User space counter disabling instructions", 307 + mmap_user_read_instr_disabled, 308 + #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ 309 + (defined(__riscv) && __riscv_xlen == 64) 310 + "permissions" 311 + #else 312 + "unsupported" 313 + #endif 314 + ), 315 + TEST_CASE_REASON_EXCLUSIVE("User space counter disabling cycles", 316 + mmap_user_read_cycles_disabled, 414 317 #if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \ 415 318 (defined(__riscv) && __riscv_xlen == 64) 416 319 "permissions"
+9
tools/perf/tests/tests.h
··· 71 71 .exclusive = true, \ 72 72 } 73 73 74 + #define TEST_CASE_REASON_EXCLUSIVE(description, _name, _reason) \ 75 + { \ 76 + .name = #_name, \ 77 + .desc = description, \ 78 + .run_case = test__##_name, \ 79 + .skip_reason = _reason, \ 80 + .exclusive = true, \ 81 + } 82 + 74 83 #define DEFINE_SUITE(description, _name) \ 75 84 struct test_case tests__##_name[] = { \ 76 85 TEST_CASE(description, _name), \
+18
tools/perf/util/affinity.c
··· 5 5 #include <stdlib.h> 6 6 #include <linux/bitmap.h> 7 7 #include <linux/zalloc.h> 8 + #include <perf/cpumap.h> 8 9 #include "perf.h" 9 10 #include "cpumap.h" 10 11 #include "affinity.h" ··· 83 82 { 84 83 if (a != NULL) 85 84 __affinity__cleanup(a); 85 + } 86 + 87 + void cpu_map__set_affinity(const struct perf_cpu_map *cpumap) 88 + { 89 + int cpu_set_size = get_cpu_set_size(); 90 + unsigned long *cpuset = bitmap_zalloc(cpu_set_size * 8); 91 + struct perf_cpu cpu; 92 + int idx; 93 + 94 + if (!cpuset) 95 + return; 96 + 97 + perf_cpu_map__for_each_cpu_skip_any(cpu, idx, cpumap) 98 + __set_bit(cpu.cpu, cpuset); 99 + 100 + sched_setaffinity(0, cpu_set_size, (cpu_set_t *)cpuset); 101 + zfree(&cpuset); 86 102 }
+2
tools/perf/util/affinity.h
··· 4 4 5 5 #include <stdbool.h> 6 6 7 + struct perf_cpu_map; 7 8 struct affinity { 8 9 unsigned long *orig_cpus; 9 10 unsigned long *sched_cpus; ··· 14 13 void affinity__cleanup(struct affinity *a); 15 14 void affinity__set(struct affinity *a, int cpu); 16 15 int affinity__setup(struct affinity *a); 16 + void cpu_map__set_affinity(const struct perf_cpu_map *cpumap); 17 17 18 18 #endif // PERF_AFFINITY_H