Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <errno.h>
0003 #include <sched.h>
0004 #include "util.h" // for sched_getcpu()
0005 #include "../perf-sys.h"
0006 #include "cloexec.h"
0007 #include "event.h"
0008 #include "asm/bug.h"
0009 #include "debug.h"
0010 #include <unistd.h>
0011 #include <sys/syscall.h>
0012 #include <linux/string.h>
0013 
0014 static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
0015 
0016 int __weak sched_getcpu(void)
0017 {
0018 #ifdef __NR_getcpu
0019     unsigned cpu;
0020     int err = syscall(__NR_getcpu, &cpu, NULL, NULL);
0021     if (!err)
0022         return cpu;
0023 #else
0024     errno = ENOSYS;
0025 #endif
0026     return -1;
0027 }
0028 
0029 static int perf_flag_probe(void)
0030 {
0031     /* use 'safest' configuration as used in evsel__fallback() */
0032     struct perf_event_attr attr = {
0033         .type = PERF_TYPE_SOFTWARE,
0034         .config = PERF_COUNT_SW_CPU_CLOCK,
0035         .exclude_kernel = 1,
0036     };
0037     int fd;
0038     int err;
0039     int cpu;
0040     pid_t pid = -1;
0041     char sbuf[STRERR_BUFSIZE];
0042 
0043     cpu = sched_getcpu();
0044     if (cpu < 0)
0045         cpu = 0;
0046 
0047     /*
0048      * Using -1 for the pid is a workaround to avoid gratuitous jump label
0049      * changes.
0050      */
0051     while (1) {
0052         /* check cloexec flag */
0053         fd = sys_perf_event_open(&attr, pid, cpu, -1,
0054                      PERF_FLAG_FD_CLOEXEC);
0055         if (fd < 0 && pid == -1 && errno == EACCES) {
0056             pid = 0;
0057             continue;
0058         }
0059         break;
0060     }
0061     err = errno;
0062 
0063     if (fd >= 0) {
0064         close(fd);
0065         return 1;
0066     }
0067 
0068     WARN_ONCE(err != EINVAL && err != EBUSY && err != EACCES,
0069           "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
0070           err, str_error_r(err, sbuf, sizeof(sbuf)));
0071 
0072     /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
0073     while (1) {
0074         fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
0075         if (fd < 0 && pid == -1 && errno == EACCES) {
0076             pid = 0;
0077             continue;
0078         }
0079         break;
0080     }
0081     err = errno;
0082 
0083     if (fd >= 0)
0084         close(fd);
0085 
0086     if (WARN_ONCE(fd < 0 && err != EBUSY && err != EACCES,
0087               "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
0088               err, str_error_r(err, sbuf, sizeof(sbuf))))
0089         return -1;
0090 
0091     return 0;
0092 }
0093 
0094 unsigned long perf_event_open_cloexec_flag(void)
0095 {
0096     static bool probed;
0097 
0098     if (!probed) {
0099         if (perf_flag_probe() <= 0)
0100             flag = 0;
0101         probed = true;
0102     }
0103 
0104     return flag;
0105 }