| // SPDX-License-Identifier: GPL-2.0 | 
 | #include <errno.h> | 
 | #include <sched.h> | 
 | #include "util.h" // for sched_getcpu() | 
 | #include "../perf-sys.h" | 
 | #include "cloexec.h" | 
 | #include "event.h" | 
 | #include "asm/bug.h" | 
 | #include "debug.h" | 
 | #include <unistd.h> | 
 | #include <sys/syscall.h> | 
 | #include <linux/string.h> | 
 |  | 
 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; | 
 |  | 
 | static int perf_flag_probe(void) | 
 | { | 
 | 	/* use 'safest' configuration as used in evsel__fallback() */ | 
 | 	struct perf_event_attr attr = { | 
 | 		.type = PERF_TYPE_SOFTWARE, | 
 | 		.config = PERF_COUNT_SW_CPU_CLOCK, | 
 | 		.exclude_kernel = 1, | 
 | 	}; | 
 | 	int fd; | 
 | 	int err; | 
 | 	int cpu; | 
 | 	pid_t pid = -1; | 
 | 	char sbuf[STRERR_BUFSIZE]; | 
 |  | 
 | 	cpu = sched_getcpu(); | 
 | 	if (cpu < 0) | 
 | 		cpu = 0; | 
 |  | 
 | 	/* | 
 | 	 * Using -1 for the pid is a workaround to avoid gratuitous jump label | 
 | 	 * changes. | 
 | 	 */ | 
 | 	while (1) { | 
 | 		/* check cloexec flag */ | 
 | 		fd = sys_perf_event_open(&attr, pid, cpu, -1, | 
 | 					 PERF_FLAG_FD_CLOEXEC); | 
 | 		if (fd < 0 && pid == -1 && errno == EACCES) { | 
 | 			pid = 0; | 
 | 			continue; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	err = errno; | 
 |  | 
 | 	if (fd >= 0) { | 
 | 		close(fd); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	WARN_ONCE(err != EINVAL && err != EBUSY && err != EACCES, | 
 | 		  "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", | 
 | 		  err, str_error_r(err, sbuf, sizeof(sbuf))); | 
 |  | 
 | 	/* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ | 
 | 	while (1) { | 
 | 		fd = sys_perf_event_open(&attr, pid, cpu, -1, 0); | 
 | 		if (fd < 0 && pid == -1 && errno == EACCES) { | 
 | 			pid = 0; | 
 | 			continue; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	err = errno; | 
 |  | 
 | 	if (fd >= 0) | 
 | 		close(fd); | 
 |  | 
 | 	if (WARN_ONCE(fd < 0 && err != EBUSY && err != EACCES, | 
 | 		      "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", | 
 | 		      err, str_error_r(err, sbuf, sizeof(sbuf)))) | 
 | 		return -1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | unsigned long perf_event_open_cloexec_flag(void) | 
 | { | 
 | 	static bool probed; | 
 |  | 
 | 	if (!probed) { | 
 | 		if (perf_flag_probe() <= 0) | 
 | 			flag = 0; | 
 | 		probed = true; | 
 | 	} | 
 |  | 
 | 	return flag; | 
 | } |