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