linux/tools/perf/util/cloexec.c
<<
>>
Prefs
   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
  14static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
  15
  16int __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
  29static int perf_flag_probe(void)
  30{
  31        /* use 'safest' configuration as used in 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 && err != EACCES,
  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 && err != EACCES,
  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
  94unsigned 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