Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(C) 2025 Marvell International Ltd.
3 : : */
4 : :
5 : : #include <errno.h>
6 : : #include <ctype.h>
7 : : #include <dirent.h>
8 : : #include <stdlib.h>
9 : : #include <unistd.h>
10 : : #include <sys/mman.h>
11 : : #include <sys/queue.h>
12 : : #include <sys/ioctl.h>
13 : : #include <sys/syscall.h>
14 : :
15 : : #include <eal_export.h>
16 : : #include <rte_bitops.h>
17 : : #include <rte_tailq.h>
18 : : #include <rte_log.h>
19 : :
20 : : #include "rte_pmu.h"
21 : : #include "pmu_private.h"
22 : :
23 : : #define EVENT_SOURCE_DEVICES_PATH "/sys/bus/event_source/devices"
24 : :
25 : : #define FIELD_PREP(m, v) (((uint64_t)(v) << (rte_ffs64(m) - 1)) & (m))
26 : :
27 [ - + ]: 254 : RTE_LOG_REGISTER_DEFAULT(rte_pmu_logtype, INFO)
28 : :
29 : : /* A structure describing an event */
30 : : struct rte_pmu_event {
31 : : char *name;
32 : : unsigned int index;
33 : : TAILQ_ENTRY(rte_pmu_event) next;
34 : : };
35 : :
36 : : RTE_EXPORT_INTERNAL_SYMBOL(rte_pmu)
37 : : struct rte_pmu rte_pmu;
38 : :
39 : : const struct pmu_arch_ops *arch_ops;
40 : :
41 : : static int
42 : 0 : get_term_format(const char *name, int *num, uint64_t *mask)
43 : : {
44 : : char path[PATH_MAX];
45 : 0 : char *config = NULL;
46 : : int high, low, ret;
47 : : FILE *fp;
48 : :
49 : 0 : *num = *mask = 0;
50 : 0 : snprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH "/%s/format/%s", rte_pmu.name, name);
51 : 0 : fp = fopen(path, "r");
52 [ # # ]: 0 : if (fp == NULL)
53 : 0 : return -errno;
54 : :
55 : 0 : errno = 0;
56 : 0 : ret = fscanf(fp, "%m[^:]:%d-%d", &config, &low, &high);
57 [ # # ]: 0 : if (ret < 2) {
58 : : ret = -ENODATA;
59 : 0 : goto out;
60 : : }
61 [ # # ]: 0 : if (errno) {
62 : 0 : ret = -errno;
63 : 0 : goto out;
64 : : }
65 : :
66 [ # # ]: 0 : if (ret == 2)
67 : 0 : high = low;
68 : :
69 : 0 : *mask = RTE_GENMASK64(high, low);
70 : : /* Last digit should be [012]. If last digit is missing 0 is implied. */
71 : 0 : *num = config[strlen(config) - 1];
72 [ # # ]: 0 : *num = isdigit(*num) ? *num - '0' : 0;
73 : :
74 : : ret = 0;
75 : 0 : out:
76 : 0 : free(config);
77 : 0 : fclose(fp);
78 : :
79 : 0 : return ret;
80 : : }
81 : :
82 : : static int
83 : 0 : parse_event(char *buf, uint64_t config[3])
84 : : {
85 : : char *token, *term;
86 : : int num, ret, val;
87 : : uint64_t mask;
88 : : char *tmp;
89 : :
90 : 0 : config[0] = config[1] = config[2] = 0;
91 : :
92 : 0 : token = strtok_r(buf, ",", &tmp);
93 [ # # ]: 0 : while (token) {
94 : 0 : errno = 0;
95 : : /* <term>=<value> */
96 : 0 : ret = sscanf(token, "%m[^=]=%i", &term, &val);
97 [ # # ]: 0 : if (ret < 1)
98 : : return -ENODATA;
99 [ # # ]: 0 : if (errno)
100 : 0 : return -errno;
101 [ # # ]: 0 : if (ret == 1)
102 : 0 : val = 1;
103 : :
104 : 0 : ret = get_term_format(term, &num, &mask);
105 : 0 : free(term);
106 [ # # ]: 0 : if (ret)
107 : 0 : return ret;
108 : :
109 : 0 : config[num] |= FIELD_PREP(mask, val);
110 : 0 : token = strtok_r(NULL, ",", &tmp);
111 : : }
112 : :
113 : : return 0;
114 : : }
115 : :
116 : : static int
117 : 0 : get_event_config(const char *name, uint64_t config[3])
118 : : {
119 : : char path[PATH_MAX], buf[BUFSIZ];
120 : : FILE *fp;
121 : : int ret;
122 : :
123 : 0 : snprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH "/%s/events/%s", rte_pmu.name, name);
124 : 0 : fp = fopen(path, "r");
125 [ # # ]: 0 : if (fp == NULL)
126 : 0 : return -errno;
127 : :
128 : 0 : ret = fread(buf, 1, sizeof(buf), fp);
129 [ # # ]: 0 : if (ret == 0) {
130 : 0 : fclose(fp);
131 : :
132 : 0 : return -EINVAL;
133 : : }
134 : 0 : fclose(fp);
135 : 0 : buf[ret] = '\0';
136 : :
137 : 0 : return parse_event(buf, config);
138 : : }
139 : :
140 : : static int
141 : 0 : do_perf_event_open(uint64_t config[3], int group_fd)
142 : : {
143 : 0 : struct perf_event_attr attr = {
144 : : .size = sizeof(struct perf_event_attr),
145 : : .type = PERF_TYPE_RAW,
146 : : .exclude_kernel = 1,
147 : : .exclude_hv = 1,
148 : : .disabled = 1,
149 [ # # ]: 0 : .pinned = group_fd == -1,
150 : : };
151 : :
152 : : pmu_arch_fixup_config(config);
153 : :
154 : 0 : attr.config = config[0];
155 : 0 : attr.config1 = config[1];
156 : 0 : attr.config2 = config[2];
157 : :
158 : 0 : return syscall(SYS_perf_event_open, &attr, 0, -1, group_fd, 0);
159 : : }
160 : :
161 : : static int
162 : 0 : open_events(struct rte_pmu_event_group *group)
163 : : {
164 : : struct rte_pmu_event *event;
165 : : uint64_t config[3];
166 : : int num = 0, ret;
167 : :
168 : : /* group leader gets created first, with fd = -1 */
169 : 0 : group->fds[0] = -1;
170 : :
171 [ # # ]: 0 : TAILQ_FOREACH(event, &rte_pmu.event_list, next) {
172 : 0 : ret = get_event_config(event->name, config);
173 [ # # ]: 0 : if (ret)
174 : 0 : continue;
175 : :
176 : 0 : ret = do_perf_event_open(config, group->fds[0]);
177 [ # # ]: 0 : if (ret == -1) {
178 : 0 : ret = -errno;
179 : 0 : goto out;
180 : : }
181 : :
182 : 0 : group->fds[event->index] = ret;
183 : 0 : num++;
184 : : }
185 : :
186 : : return 0;
187 : : out:
188 [ # # ]: 0 : for (--num; num >= 0; num--) {
189 : 0 : close(group->fds[num]);
190 : 0 : group->fds[num] = -1;
191 : : }
192 : :
193 : : return ret;
194 : : }
195 : :
196 : : /* Inspired by rte_mem_page_size() from /lib/eal/unix/eal_unix_memory.c */
197 : : static size_t
198 : 0 : mem_page_size(void)
199 : : {
200 : : static size_t page_size;
201 : :
202 [ # # ]: 0 : if (unlikely(page_size == 0)) {
203 : : /*
204 : : * When the sysconf value cannot be determined, sysconf()
205 : : * returns -1 without setting errno.
206 : : * To distinguish an indeterminate value from an error,
207 : : * clear errno before calling sysconf(), and check whether
208 : : * errno has been set if sysconf() returns -1.
209 : : */
210 : 0 : errno = 0;
211 : 0 : page_size = sysconf(_SC_PAGESIZE);
212 [ # # # # ]: 0 : if ((ssize_t)page_size < 0 && errno == 0)
213 : 0 : errno = ENOENT;
214 : : }
215 : :
216 : 0 : return page_size;
217 : : }
218 : :
219 : : static int
220 : 0 : mmap_events(struct rte_pmu_event_group *group)
221 : : {
222 : 0 : size_t page_size = mem_page_size();
223 : : unsigned int i;
224 : : void *addr;
225 : : int ret;
226 : :
227 [ # # ]: 0 : if ((ssize_t)page_size < 0)
228 : 0 : return -errno;
229 : :
230 [ # # ]: 0 : for (i = 0; i < rte_pmu.num_group_events; i++) {
231 : 0 : addr = mmap(0, page_size, PROT_READ, MAP_SHARED, group->fds[i], 0);
232 [ # # ]: 0 : if (addr == MAP_FAILED) {
233 : 0 : ret = -errno;
234 : 0 : goto out;
235 : : }
236 : :
237 : 0 : group->mmap_pages[i] = addr;
238 : : }
239 : :
240 : : return 0;
241 : : out:
242 [ # # ]: 0 : for (; i; i--) {
243 : 0 : munmap(group->mmap_pages[i - 1], page_size);
244 : 0 : group->mmap_pages[i - 1] = NULL;
245 : : }
246 : :
247 : : return ret;
248 : : }
249 : :
250 : : static void
251 : 0 : cleanup_events(struct rte_pmu_event_group *group)
252 : : {
253 : 0 : size_t page_size = mem_page_size();
254 : : unsigned int i;
255 : :
256 [ # # ]: 0 : if (group->fds[0] != -1)
257 : 0 : ioctl(group->fds[0], PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
258 : :
259 [ # # ]: 0 : for (i = 0; i < rte_pmu.num_group_events; i++) {
260 [ # # ]: 0 : if (group->mmap_pages[i]) {
261 [ # # ]: 0 : __rte_assume((ssize_t)page_size >= 0);
262 : 0 : munmap(group->mmap_pages[i], page_size);
263 : 0 : group->mmap_pages[i] = NULL;
264 : : }
265 : :
266 [ # # ]: 0 : if (group->fds[i] != -1) {
267 : 0 : close(group->fds[i]);
268 : 0 : group->fds[i] = -1;
269 : : }
270 : : }
271 : :
272 : 0 : group->enabled = false;
273 : 0 : }
274 : :
275 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(__rte_pmu_enable_group, 25.07)
276 : : int
277 : 0 : __rte_pmu_enable_group(struct rte_pmu_event_group *group)
278 : : {
279 : : int ret;
280 : :
281 [ # # ]: 0 : if (rte_pmu.num_group_events == 0)
282 : : return -ENODEV;
283 : :
284 : 0 : ret = open_events(group);
285 [ # # ]: 0 : if (ret)
286 : 0 : goto out;
287 : :
288 : 0 : ret = mmap_events(group);
289 [ # # ]: 0 : if (ret)
290 : 0 : goto out;
291 : :
292 [ # # ]: 0 : if (ioctl(group->fds[0], PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
293 : 0 : ret = -errno;
294 : 0 : goto out;
295 : : }
296 : :
297 [ # # ]: 0 : if (ioctl(group->fds[0], PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
298 : 0 : ret = -errno;
299 : 0 : goto out;
300 : : }
301 : :
302 : 0 : group->enabled = true;
303 : :
304 : 0 : return 0;
305 : 0 : out:
306 : 0 : cleanup_events(group);
307 : :
308 : 0 : return ret;
309 : : }
310 : :
311 : : static int
312 : 0 : scan_pmus(void)
313 : : {
314 : : char path[PATH_MAX];
315 : : struct dirent *dent;
316 : : const char *name;
317 : : DIR *dirp;
318 : :
319 : 0 : dirp = opendir(EVENT_SOURCE_DEVICES_PATH);
320 [ # # ]: 0 : if (dirp == NULL)
321 : 0 : return -errno;
322 : :
323 [ # # ]: 0 : while ((dent = readdir(dirp))) {
324 : 0 : name = dent->d_name;
325 [ # # ]: 0 : if (name[0] == '.')
326 : 0 : continue;
327 : :
328 : : /* sysfs entry should either contain cpus or be a cpu */
329 [ # # ]: 0 : if (!strcmp(name, "cpu"))
330 : : break;
331 : :
332 : : snprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH "/%s/cpus", name);
333 [ # # ]: 0 : if (access(path, F_OK) == 0)
334 : : break;
335 : : }
336 : :
337 [ # # ]: 0 : if (dent) {
338 : 0 : rte_pmu.name = strdup(name);
339 [ # # ]: 0 : if (rte_pmu.name == NULL) {
340 : 0 : closedir(dirp);
341 : :
342 : 0 : return -ENOMEM;
343 : : }
344 : : }
345 : :
346 : 0 : closedir(dirp);
347 : :
348 [ # # ]: 0 : return rte_pmu.name ? 0 : -ENODEV;
349 : : }
350 : :
351 : : static struct rte_pmu_event *
352 : 0 : new_event(const char *name)
353 : : {
354 : : struct rte_pmu_event *event;
355 : :
356 : 0 : event = calloc(1, sizeof(*event));
357 [ # # ]: 0 : if (event == NULL)
358 : 0 : goto out;
359 : :
360 : 0 : event->name = strdup(name);
361 [ # # ]: 0 : if (event->name == NULL) {
362 : 0 : free(event);
363 : : event = NULL;
364 : : }
365 : :
366 : 0 : out:
367 : 0 : return event;
368 : : }
369 : :
370 : : static void
371 : : free_event(struct rte_pmu_event *event)
372 : : {
373 : 0 : free(event->name);
374 : 0 : free(event);
375 : : }
376 : :
377 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pmu_add_event, 25.07)
378 : : int
379 : 0 : rte_pmu_add_event(const char *name)
380 : : {
381 : : struct rte_pmu_event *event;
382 : : char path[PATH_MAX];
383 : :
384 [ # # ]: 0 : if (!rte_pmu.initialized) {
385 : 0 : PMU_LOG(ERR, "PMU is not initialized");
386 : 0 : return -ENODEV;
387 : : }
388 : :
389 [ # # ]: 0 : if (rte_pmu.num_group_events + 1 >= RTE_MAX_NUM_GROUP_EVENTS) {
390 : 0 : PMU_LOG(ERR, "Excessive number of events in a group (%d > %d)",
391 : : rte_pmu.num_group_events, RTE_MAX_NUM_GROUP_EVENTS);
392 : 0 : return -ENOSPC;
393 : : }
394 : :
395 : 0 : snprintf(path, sizeof(path), EVENT_SOURCE_DEVICES_PATH "/%s/events/%s", rte_pmu.name, name);
396 [ # # ]: 0 : if (access(path, R_OK)) {
397 : 0 : PMU_LOG(ERR, "Cannot access %s", path);
398 : 0 : return -ENODEV;
399 : : }
400 : :
401 [ # # ]: 0 : TAILQ_FOREACH(event, &rte_pmu.event_list, next) {
402 [ # # ]: 0 : if (strcmp(event->name, name))
403 : : continue;
404 : :
405 : 0 : return event->index;
406 : : }
407 : :
408 : 0 : event = new_event(name);
409 [ # # ]: 0 : if (event == NULL) {
410 : 0 : PMU_LOG(ERR, "Failed to create event %s", name);
411 : 0 : return -ENOMEM;
412 : : }
413 : :
414 : 0 : event->index = rte_pmu.num_group_events++;
415 : 0 : TAILQ_INSERT_TAIL(&rte_pmu.event_list, event, next);
416 : :
417 : 0 : return event->index;
418 : : }
419 : :
420 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pmu_init, 25.07)
421 : : int
422 : 0 : rte_pmu_init(void)
423 : : {
424 : : int ret;
425 : :
426 [ # # ]: 0 : if (rte_pmu.initialized)
427 : : return 0;
428 : :
429 : 0 : ret = scan_pmus();
430 [ # # ]: 0 : if (ret) {
431 : 0 : PMU_LOG(ERR, "Failed to scan for event sources");
432 : 0 : goto out;
433 : : }
434 : :
435 : : ret = pmu_arch_init();
436 [ # # ]: 0 : if (ret) {
437 : 0 : PMU_LOG(ERR, "Failed to setup arch internals");
438 : 0 : goto out;
439 : : }
440 : :
441 : 0 : TAILQ_INIT(&rte_pmu.event_list);
442 : 0 : rte_pmu.initialized = 1;
443 : 0 : out:
444 [ # # ]: 0 : if (ret) {
445 : 0 : free(rte_pmu.name);
446 : 0 : rte_pmu.name = NULL;
447 : : }
448 : :
449 : : return ret;
450 : : }
451 : :
452 : : RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pmu_fini, 25.07)
453 : : void
454 : 0 : rte_pmu_fini(void)
455 : : {
456 : : struct rte_pmu_event *event, *tmp_event;
457 : : struct rte_pmu_event_group *group;
458 : : unsigned int i;
459 : :
460 [ # # ]: 0 : if (!rte_pmu.initialized)
461 : : return;
462 : :
463 [ # # ]: 0 : RTE_TAILQ_FOREACH_SAFE(event, &rte_pmu.event_list, next, tmp_event) {
464 [ # # ]: 0 : TAILQ_REMOVE(&rte_pmu.event_list, event, next);
465 : : free_event(event);
466 : : }
467 : :
468 [ # # ]: 0 : for (i = 0; i < RTE_DIM(rte_pmu.event_groups); i++) {
469 : 0 : group = &rte_pmu.event_groups[i];
470 [ # # ]: 0 : if (!group->enabled)
471 : 0 : continue;
472 : :
473 : 0 : cleanup_events(group);
474 : : }
475 : :
476 : : pmu_arch_fini();
477 : 0 : free(rte_pmu.name);
478 : 0 : rte_pmu.name = NULL;
479 : 0 : rte_pmu.num_group_events = 0;
480 : : }
|