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 } 105