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