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