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