xref: /openbmc/linux/tools/perf/util/cloexec.c (revision e0bf6c5c)
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 }
87