162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <errno.h>
362306a36Sopenharmony_ci#include <sched.h>
462306a36Sopenharmony_ci#include "util.h" // for sched_getcpu()
562306a36Sopenharmony_ci#include "../perf-sys.h"
662306a36Sopenharmony_ci#include "cloexec.h"
762306a36Sopenharmony_ci#include "event.h"
862306a36Sopenharmony_ci#include "asm/bug.h"
962306a36Sopenharmony_ci#include "debug.h"
1062306a36Sopenharmony_ci#include <unistd.h>
1162306a36Sopenharmony_ci#include <sys/syscall.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic unsigned long flag = PERF_FLAG_FD_CLOEXEC;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int perf_flag_probe(void)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	/* use 'safest' configuration as used in evsel__fallback() */
1962306a36Sopenharmony_ci	struct perf_event_attr attr = {
2062306a36Sopenharmony_ci		.type = PERF_TYPE_SOFTWARE,
2162306a36Sopenharmony_ci		.config = PERF_COUNT_SW_CPU_CLOCK,
2262306a36Sopenharmony_ci		.exclude_kernel = 1,
2362306a36Sopenharmony_ci	};
2462306a36Sopenharmony_ci	int fd;
2562306a36Sopenharmony_ci	int err;
2662306a36Sopenharmony_ci	int cpu;
2762306a36Sopenharmony_ci	pid_t pid = -1;
2862306a36Sopenharmony_ci	char sbuf[STRERR_BUFSIZE];
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	cpu = sched_getcpu();
3162306a36Sopenharmony_ci	if (cpu < 0)
3262306a36Sopenharmony_ci		cpu = 0;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/*
3562306a36Sopenharmony_ci	 * Using -1 for the pid is a workaround to avoid gratuitous jump label
3662306a36Sopenharmony_ci	 * changes.
3762306a36Sopenharmony_ci	 */
3862306a36Sopenharmony_ci	while (1) {
3962306a36Sopenharmony_ci		/* check cloexec flag */
4062306a36Sopenharmony_ci		fd = sys_perf_event_open(&attr, pid, cpu, -1,
4162306a36Sopenharmony_ci					 PERF_FLAG_FD_CLOEXEC);
4262306a36Sopenharmony_ci		if (fd < 0 && pid == -1 && errno == EACCES) {
4362306a36Sopenharmony_ci			pid = 0;
4462306a36Sopenharmony_ci			continue;
4562306a36Sopenharmony_ci		}
4662306a36Sopenharmony_ci		break;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci	err = errno;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (fd >= 0) {
5162306a36Sopenharmony_ci		close(fd);
5262306a36Sopenharmony_ci		return 1;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	WARN_ONCE(err != EINVAL && err != EBUSY && err != EACCES,
5662306a36Sopenharmony_ci		  "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n",
5762306a36Sopenharmony_ci		  err, str_error_r(err, sbuf, sizeof(sbuf)));
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */
6062306a36Sopenharmony_ci	while (1) {
6162306a36Sopenharmony_ci		fd = sys_perf_event_open(&attr, pid, cpu, -1, 0);
6262306a36Sopenharmony_ci		if (fd < 0 && pid == -1 && errno == EACCES) {
6362306a36Sopenharmony_ci			pid = 0;
6462306a36Sopenharmony_ci			continue;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	err = errno;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (fd >= 0)
7162306a36Sopenharmony_ci		close(fd);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (WARN_ONCE(fd < 0 && err != EBUSY && err != EACCES,
7462306a36Sopenharmony_ci		      "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n",
7562306a36Sopenharmony_ci		      err, str_error_r(err, sbuf, sizeof(sbuf))))
7662306a36Sopenharmony_ci		return -1;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ciunsigned long perf_event_open_cloexec_flag(void)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	static bool probed;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (!probed) {
8662306a36Sopenharmony_ci		if (perf_flag_probe() <= 0)
8762306a36Sopenharmony_ci			flag = 0;
8862306a36Sopenharmony_ci		probed = true;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return flag;
9262306a36Sopenharmony_ci}
93