projects/linux·blob uaccess-cleanups · refs

451 lines · 12.2 KiB

1// SPDX-License-Identifier: GPL-2.0
2#include <errno.h>
3#include <inttypes.h>
4#include <stdlib.h>
5
6#include <fcntl.h>
7#include <linux/err.h>
8#include <linux/kernel.h>
9#include <linux/string.h>
10
11#include <perf/cpumap.h>
12#include <perf/evlist.h>
13#include <perf/mmap.h>
14
15#include "cpumap.h"
16#include "debug.h"
17#include "event.h"
18#include "evlist.h"
19#include "evsel.h"
20#include "pmu.h"
21#include "pmus.h"
22#include "tests.h"
23#include "thread_map.h"
24#include "util/affinity.h"
25#include "util/mmap.h"
26#include "util/sample.h"
27
28/*
29 * This test will generate random numbers of calls to some getpid syscalls,
30 * then establish an mmap for a group of events that are created to monitor
31 * the syscalls.
32 *
33 * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated
34 * sample.id field to map back to its respective perf_evsel instance.
35 *
36 * Then it checks if the number of syscalls reported as perf events by
37 * the kernel corresponds to the number of syscalls made.
38 */
39static int test__basic_mmap(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
40{
41 int err = TEST_FAIL;
42 union perf_event *event;
43 struct perf_thread_map *threads;
44 struct perf_cpu_map *cpus;
45 struct evlist *evlist;
46 cpu_set_t cpu_set;
47 const char *syscall_names[] = { "getsid", "getppid", "getpgid", };
48 pid_t (*syscalls[])(void) = { (void *)getsid, getppid, (void*)getpgid };
49#define nsyscalls ARRAY_SIZE(syscall_names)
50 unsigned int nr_events[nsyscalls],
51 expected_nr_events[nsyscalls], i, j;
52 struct evsel *evsels[nsyscalls], *evsel;
53 char sbuf[STRERR_BUFSIZE];
54 struct mmap *md;
55
56 threads = thread_map__new_by_tid(getpid());
57 if (threads == NULL) {
58 pr_debug("thread_map__new\n");
59 return -1;
60 }
61
62 cpus = perf_cpu_map__new_online_cpus();
63 if (cpus == NULL) {
64 pr_debug("perf_cpu_map__new\n");
65 goto out_free_threads;
66 }
67
68 CPU_ZERO(&cpu_set);
69 CPU_SET(perf_cpu_map__cpu(cpus, 0).cpu, &cpu_set);
70 sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
71 if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
72 pr_debug("sched_setaffinity() failed on CPU %d: %s ",
73 perf_cpu_map__cpu(cpus, 0).cpu,
74 str_error_r(errno, sbuf, sizeof(sbuf)));
75 goto out_free_cpus;
76 }
77
78 evlist = evlist__new();
79 if (evlist == NULL) {
80 pr_debug("evlist__new\n");
81 goto out_free_cpus;
82 }
83
84 perf_evlist__set_maps(evlist__core(evlist), cpus, threads);
85
86 for (i = 0; i < nsyscalls; ++i) {
87 char name[64];
88
89 snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
90 evsels[i] = evsel__newtp("syscalls", name);
91 if (IS_ERR(evsels[i])) {
92 pr_debug("evsel__new(%s)\n", name);
93 if (PTR_ERR(evsels[i]) == -EACCES) {
94 /* Permissions failure, flag the failure as a skip. */
95 err = TEST_SKIP;
96 }
97 goto out_put_evlist;
98 }
99
100 evsels[i]->core.attr.wakeup_events = 1;
101 evsel__set_sample_id(evsels[i], false);
102
103 evlist__add(evlist, evsels[i]);
104
105 if (evsel__open(evsels[i], cpus, threads) < 0) {
106 pr_debug("failed to open counter: %s, "
107 "tweak /proc/sys/kernel/perf_event_paranoid?\n",
108 str_error_r(errno, sbuf, sizeof(sbuf)));
109 goto out_put_evlist;
110 }
111
112 nr_events[i] = 0;
113 expected_nr_events[i] = 1 + rand() % 127;
114 }
115
116 if (evlist__do_mmap(evlist, 128) < 0) {
117 pr_debug("failed to mmap events: %d (%s)\n", errno,
118 str_error_r(errno, sbuf, sizeof(sbuf)));
119 goto out_put_evlist;
120 }
121
122 for (i = 0; i < nsyscalls; ++i)
123 for (j = 0; j < expected_nr_events[i]; ++j) {
124 syscalls[i]();
125 }
126
127 md = &evlist__mmap(evlist)[0];
128 if (perf_mmap__read_init(&md->core) < 0)
129 goto out_init;
130
131 while ((event = perf_mmap__read_event(&md->core)) != NULL) {
132 struct perf_sample sample;
133
134 if (event->header.type != PERF_RECORD_SAMPLE) {
135 pr_debug("unexpected %s event\n",
136 perf_event__name(event->header.type));
137 goto out_put_evlist;
138 }
139
140 perf_sample__init(&sample, /*all=*/false);
141 err = evlist__parse_sample(evlist, event, &sample);
142 if (err) {
143 pr_err("Can't parse sample, err = %d\n", err);
144 perf_sample__exit(&sample);
145 goto out_put_evlist;
146 }
147
148 err = -1;
149 evsel = sample.evsel;
150 if (!evsel)
151 evsel = evlist__id2evsel(evlist, sample.id);
152 perf_sample__exit(&sample);
153 if (evsel == NULL) {
154 pr_debug("event with id %" PRIu64
155 " doesn't map to an evsel\n", sample.id);
156 goto out_put_evlist;
157 }
158 nr_events[evsel->core.idx]++;
159 perf_mmap__consume(&md->core);
160 }
161 perf_mmap__read_done(&md->core);
162
163out_init:
164 err = 0;
165 evlist__for_each_entry(evlist, evsel) {
166 if (nr_events[evsel->core.idx] != expected_nr_events[evsel->core.idx]) {
167 pr_debug("expected %d %s events, got %d\n",
168 expected_nr_events[evsel->core.idx],
169 evsel__name(evsel), nr_events[evsel->core.idx]);
170 err = -1;
171 goto out_put_evlist;
172 }
173 }
174
175out_put_evlist:
176 evlist__put(evlist);
177out_free_cpus:
178 perf_cpu_map__put(cpus);
179out_free_threads:
180 perf_thread_map__put(threads);
181 return err;
182}
183
184enum user_read_state {
185 USER_READ_ENABLED,
186 USER_READ_DISABLED,
187 USER_READ_UNKNOWN,
188};
189
190static enum user_read_state set_user_read(struct perf_pmu *pmu, enum user_read_state enabled)
191{
192 char buf[2] = {0, '\n'};
193 ssize_t len;
194 int events_fd, rdpmc_fd;
195 enum user_read_state old_user_read = USER_READ_UNKNOWN;
196
197 if (enabled == USER_READ_UNKNOWN)
198 return USER_READ_UNKNOWN;
199
200 events_fd = perf_pmu__event_source_devices_fd();
201 if (events_fd < 0)
202 return USER_READ_UNKNOWN;
203
204 rdpmc_fd = perf_pmu__pathname_fd(events_fd, pmu->name, "rdpmc", O_RDWR);
205 if (rdpmc_fd < 0) {
206 close(events_fd);
207 return USER_READ_UNKNOWN;
208 }
209
210 len = read(rdpmc_fd, buf, sizeof(buf));
211 if (len != sizeof(buf))
212 pr_debug("%s read failed\n", __func__);
213
214 // Note, on Intel hybrid disabling on 1 PMU will implicitly disable on
215 // all the core PMUs.
216 old_user_read = (buf[0] == '1') ? USER_READ_ENABLED : USER_READ_DISABLED;
217
218 if (enabled != old_user_read) {
219 buf[0] = (enabled == USER_READ_ENABLED) ? '1' : '0';
220 len = write(rdpmc_fd, buf, sizeof(buf));
221 if (len != sizeof(buf))
222 pr_debug("%s write failed\n", __func__);
223 }
224 close(rdpmc_fd);
225 close(events_fd);
226 return old_user_read;
227}
228
229static int test_stat_user_read(u64 event, enum user_read_state enabled)
230{
231 struct perf_pmu *pmu = NULL;
232 struct perf_thread_map *threads = perf_thread_map__new_dummy();
233 int ret = TEST_OK;
234
235 pr_err("User space counter reading %" PRIu64 "\n", event);
236 if (!threads) {
237 pr_err("User space counter reading [Failed to create threads]\n");
238 return TEST_FAIL;
239 }
240 perf_thread_map__set_pid(threads, 0, 0);
241
242 while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
243 enum user_read_state saved_user_read_state = set_user_read(pmu, enabled);
244 struct perf_event_attr attr = {
245 .type = PERF_TYPE_HARDWARE,
246 .config = perf_pmus__supports_extended_type()
247 ? event | ((u64)pmu->type << PERF_PMU_TYPE_SHIFT)
248 : event,
249#ifdef __aarch64__
250 .config1 = 0x2, /* Request user access */
251#endif
252 };
253 struct perf_evsel *evsel = NULL;
254 int err;
255 struct perf_event_mmap_page *pc;
256 bool mapped = false, opened = false, rdpmc_supported;
257 struct perf_counts_values counts = { .val = 0 };
258
259
260 pr_debug("User space counter reading for PMU %s\n", pmu->name);
261 /*
262 * Restrict scheduling to only use the rdpmc on the CPUs the
263 * event can be on. If the test doesn't run on the CPU of the
264 * event then the event will be disabled and the pc->index test
265 * will fail.
266 */
267 if (pmu->cpus != NULL)
268 cpu_map__set_affinity(pmu->cpus);
269
270 /* Make the evsel. */
271 evsel = perf_evsel__new(&attr);
272 if (!evsel) {
273 pr_err("User space counter reading for PMU %s [Failed to allocate evsel]\n",
274 pmu->name);
275 ret = TEST_FAIL;
276 goto cleanup;
277 }
278
279 err = perf_evsel__open(evsel, NULL, threads);
280 if (err) {
281 pr_err("User space counter reading for PMU %s [Failed to open evsel]\n",
282 pmu->name);
283 ret = TEST_SKIP;
284 goto cleanup;
285 }
286 opened = true;
287 err = perf_evsel__mmap(evsel, 0);
288 if (err) {
289 pr_err("User space counter reading for PMU %s [Failed to mmap evsel]\n",
290 pmu->name);
291 ret = TEST_FAIL;
292 goto cleanup;
293 }
294 mapped = true;
295
296 pc = perf_evsel__mmap_base(evsel, 0, 0);
297 if (!pc) {
298 pr_err("User space counter reading for PMU %s [Failed to get mmaped address]\n",
299 pmu->name);
300 ret = TEST_FAIL;
301 goto cleanup;
302 }
303
304 if (saved_user_read_state == USER_READ_UNKNOWN)
305 rdpmc_supported = pc->cap_user_rdpmc && pc->index;
306 else
307 rdpmc_supported = (enabled == USER_READ_ENABLED);
308
309 if (rdpmc_supported && (!pc->cap_user_rdpmc || !pc->index)) {
310 pr_err("User space counter reading for PMU %s [Failed unexpected supported counter access %d %d]\n",
311 pmu->name, pc->cap_user_rdpmc, pc->index);
312 ret = TEST_FAIL;
313 goto cleanup;
314 }
315
316 if (!rdpmc_supported && pc->cap_user_rdpmc) {
317 pr_err("User space counter reading for PMU %s [Failed unexpected unsupported counter access %d]\n",
318 pmu->name, pc->cap_user_rdpmc);
319 ret = TEST_FAIL;
320 goto cleanup;
321 }
322
323 if (rdpmc_supported && pc->pmc_width < 32) {
324 pr_err("User space counter reading for PMU %s [Failed width not set %d]\n",
325 pmu->name, pc->pmc_width);
326 ret = TEST_FAIL;
327 goto cleanup;
328 }
329
330 perf_evsel__read(evsel, 0, 0, &counts);
331 if (rdpmc_supported && counts.val == 0) {
332 pr_err("User space counter reading for PMU %s [Failed read]\n", pmu->name);
333 ret = TEST_FAIL;
334 goto cleanup;
335 }
336
337 for (int i = 0; i < 5; i++) {
338 volatile int count = 0x10000 << i;
339 __u64 start, end, last = 0;
340
341 pr_debug("\tloop = %u, ", count);
342
343 perf_evsel__read(evsel, 0, 0, &counts);
344 start = counts.val;
345
346 while (count--) ;
347
348 perf_evsel__read(evsel, 0, 0, &counts);
349 end = counts.val;
350
351 if ((end - start) < last) {
352 pr_err("User space counter reading for PMU %s [Failed invalid counter data: end=%llu start=%llu last= %llu]\n",
353 pmu->name, end, start, last);
354 ret = TEST_FAIL;
355 goto cleanup;
356 }
357 last = end - start;
358 pr_debug("count = %llu\n", last);
359 }
360 pr_debug("User space counter reading for PMU %s [Success]\n", pmu->name);
361cleanup:
362 if (mapped)
363 perf_evsel__munmap(evsel);
364 if (opened)
365 perf_evsel__close(evsel);
366 perf_evsel__delete(evsel);
367
368 /* If the affinity was changed, then put it back to all CPUs. */
369 if (pmu->cpus != NULL) {
370 struct perf_cpu_map *cpus = cpu_map__online();
371
372 cpu_map__set_affinity(cpus);
373 perf_cpu_map__put(cpus);
374 }
375 set_user_read(pmu, saved_user_read_state);
376 }
377 perf_thread_map__put(threads);
378 return ret;
379}
380
381static int test__mmap_user_read_instr(struct test_suite *test __maybe_unused,
382 int subtest __maybe_unused)
383{
384 return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_ENABLED);
385}
386
387static int test__mmap_user_read_cycles(struct test_suite *test __maybe_unused,
388 int subtest __maybe_unused)
389{
390 return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_ENABLED);
391}
392
393static int test__mmap_user_read_instr_disabled(struct test_suite *test __maybe_unused,
394 int subtest __maybe_unused)
395{
396 return test_stat_user_read(PERF_COUNT_HW_INSTRUCTIONS, USER_READ_DISABLED);
397}
398
399static int test__mmap_user_read_cycles_disabled(struct test_suite *test __maybe_unused,
400 int subtest __maybe_unused)
401{
402 return test_stat_user_read(PERF_COUNT_HW_CPU_CYCLES, USER_READ_DISABLED);
403}
404
405static struct test_case tests__basic_mmap[] = {
406 TEST_CASE_REASON("Read samples using the mmap interface",
407 basic_mmap,
408 "permissions"),
409 TEST_CASE_REASON_EXCLUSIVE("User space counter reading of instructions",
410 mmap_user_read_instr,
411#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
412 (defined(__riscv) && __riscv_xlen == 64)
413 "permissions"
414#else
415 "unsupported"
416#endif
417 ),
418 TEST_CASE_REASON_EXCLUSIVE("User space counter reading of cycles",
419 mmap_user_read_cycles,
420#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
421 (defined(__riscv) && __riscv_xlen == 64)
422 "permissions"
423#else
424 "unsupported"
425#endif
426 ),
427 TEST_CASE_REASON_EXCLUSIVE("User space counter disabling instructions",
428 mmap_user_read_instr_disabled,
429#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
430 (defined(__riscv) && __riscv_xlen == 64)
431 "permissions"
432#else
433 "unsupported"
434#endif
435 ),
436 TEST_CASE_REASON_EXCLUSIVE("User space counter disabling cycles",
437 mmap_user_read_cycles_disabled,
438#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) || \
439 (defined(__riscv) && __riscv_xlen == 64)
440 "permissions"
441#else
442 "unsupported"
443#endif
444 ),
445 { .name = NULL, }
446};
447
448struct test_suite suite__basic_mmap = {
449 .desc = "mmap interface tests",
450 .test_cases = tests__basic_mmap,
451};