cloexec.c 2.0 KB

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