cloexec.c 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. static unsigned long flag = PERF_FLAG_FD_CLOEXEC;
  14. static int perf_flag_probe(void)
  15. {
  16. /* use 'safest' configuration as used in evsel__fallback() */
  17. struct perf_event_attr attr = {
  18. .type = PERF_TYPE_SOFTWARE,
  19. .config = PERF_COUNT_SW_CPU_CLOCK,
  20. .exclude_kernel = 1,
  21. };
  22. int fd;
  23. int err;
  24. int cpu;
  25. pid_t pid = -1;
  26. char sbuf[STRERR_BUFSIZE];
  27. cpu = sched_getcpu();
  28. if (cpu < 0)
  29. cpu = 0;
  30. /*
  31. * Using -1 for the pid is a workaround to avoid gratuitous jump label
  32. * changes.
  33. */
  34. while (1) {
  35. /* check cloexec flag */
  36. fd = sys_perf_event_open(&attr, pid, cpu, -1,
  37. PERF_FLAG_FD_CLOEXEC);
  38. if (fd < 0 && pid == -1 && errno == EACCES) {
  39. pid = 0;
  40. continue;
  41. }
  42. break;
  43. }
  44. err = errno;
  45. if (fd >= 0) {
  46. close(fd);
  47. return 1;
  48. }
  49. WARN_ONCE(err != EINVAL && err != EBUSY && err != EACCES,
  50. "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
  51. err, str_error_r(err, sbuf, sizeof(sbuf)));
  52. /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
  53. while (1) {
  54. fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
  55. if (fd < 0 && pid == -1 && errno == EACCES) {
  56. pid = 0;
  57. continue;
  58. }
  59. break;
  60. }
  61. err = errno;
  62. if (fd >= 0)
  63. close(fd);
  64. if (WARN_ONCE(fd < 0 && err != EBUSY && err != EACCES,
  65. "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
  66. err, str_error_r(err, sbuf, sizeof(sbuf))))
  67. return -1;
  68. return 0;
  69. }
  70. unsigned long perf_event_open_cloexec_flag(void)
  71. {
  72. static bool probed;
  73. if (!probed) {
  74. if (perf_flag_probe() <= 0)
  75. flag = 0;
  76. probed = true;
  77. }
  78. return flag;
  79. }