1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <errno.h> |
3 | #include <sched.h> |
4 | #include "util.h" // for sched_getcpu() |
5 | #include "../perf-sys.h" |
6 | #include "cloexec.h" |
7 | #include "event.h" |
8 | #include "asm/bug.h" |
9 | #include "debug.h" |
10 | #include <unistd.h> |
11 | #include <sys/syscall.h> |
12 | #include <linux/string.h> |
13 | |
14 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; |
15 | |
16 | static int perf_flag_probe(void) |
17 | { |
18 | /* use 'safest' configuration as used in evsel__fallback() */ |
19 | struct perf_event_attr attr = { |
20 | .type = PERF_TYPE_SOFTWARE, |
21 | .config = PERF_COUNT_SW_CPU_CLOCK, |
22 | .exclude_kernel = 1, |
23 | }; |
24 | int fd; |
25 | int err; |
26 | int cpu; |
27 | pid_t pid = -1; |
28 | char sbuf[STRERR_BUFSIZE]; |
29 | |
30 | cpu = sched_getcpu(); |
31 | if (cpu < 0) |
32 | cpu = 0; |
33 | |
34 | /* |
35 | * Using -1 for the pid is a workaround to avoid gratuitous jump label |
36 | * changes. |
37 | */ |
38 | while (1) { |
39 | /* check cloexec flag */ |
40 | fd = sys_perf_event_open(attr: &attr, pid, cpu, group_fd: -1, |
41 | flags: PERF_FLAG_FD_CLOEXEC); |
42 | if (fd < 0 && pid == -1 && errno == EACCES) { |
43 | pid = 0; |
44 | continue; |
45 | } |
46 | break; |
47 | } |
48 | err = errno; |
49 | |
50 | if (fd >= 0) { |
51 | close(fd); |
52 | return 1; |
53 | } |
54 | |
55 | WARN_ONCE(err != EINVAL && err != EBUSY && err != EACCES, |
56 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n" , |
57 | err, str_error_r(err, sbuf, sizeof(sbuf))); |
58 | |
59 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ |
60 | while (1) { |
61 | fd = sys_perf_event_open(attr: &attr, pid, cpu, group_fd: -1, flags: 0); |
62 | if (fd < 0 && pid == -1 && errno == EACCES) { |
63 | pid = 0; |
64 | continue; |
65 | } |
66 | break; |
67 | } |
68 | err = errno; |
69 | |
70 | if (fd >= 0) |
71 | close(fd); |
72 | |
73 | if (WARN_ONCE(fd < 0 && err != EBUSY && err != EACCES, |
74 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n" , |
75 | err, str_error_r(err, sbuf, sizeof(sbuf)))) |
76 | return -1; |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | unsigned long perf_event_open_cloexec_flag(void) |
82 | { |
83 | static bool probed; |
84 | |
85 | if (!probed) { |
86 | if (perf_flag_probe() <= 0) |
87 | flag = 0; |
88 | probed = true; |
89 | } |
90 | |
91 | return flag; |
92 | } |
93 | |