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, pid, cpu, -1, 41 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, pid, cpu, -1, 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