[PATCH v5 3/5] perf tools: fall back to eventfs for unprivileged event discovery

Anubhav Shelat <[email protected]>
Newsgroups org.kernel.vger.linux-trace-kernel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
When tracefs events are not readable by unprivileged users, fall back
to /sys/kernel/events (the read-only eventfs mount) for tracepoint
format and id file discovery. This allows perf trace to work for
unprivileged users on kernels that expose the eventfs filesystem.

The fallback is transparent: get_events_file() tries the tracefs path
first and only switches to eventfs when access() fails. On kernels
without eventfs, the existing error path is preserved.

Assisted-by: CLAUDE:claude-opus-4 Apogee
Signed-off-by: Anubhav Shelat <[email protected]>
---
 tools/lib/api/fs/fs.c              | 10 ++++++
 tools/lib/api/fs/fs.h              |  1 +
 tools/lib/api/fs/tracing_path.c    | 52 ++++++++++++++++++++++++++----
 tools/lib/api/fs/tracing_path.h    |  1 +
 tools/perf/util/tp_pmu.c           |  5 +--
 tools/perf/util/trace-event-info.c | 19 ++++++-----
 6 files changed, 71 insertions(+), 17 deletions(-)

diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c
index cbd8eab0d1df..abc3581a9703 100644
--- a/tools/lib/api/fs/fs.c
+++ b/tools/lib/api/fs/fs.c
@@ -46,6 +46,10 @@
 #define BPF_FS_MAGIC           0xcafe4a11
 #endif
 
+#ifndef EVENTFS_SUPER_MAGIC
+#define EVENTFS_SUPER_MAGIC    0x65766673
+#endif
+
 static const char * const sysfs__known_mountpoints[] = {
 	"/sys",
 	0,
@@ -88,6 +92,11 @@ static const char * const bpf_fs__known_mountpoints[] = {
 	0,
 };
 
+static const char * const eventfs__known_mountpoints[] = {
+	"/sys/kernel/events",
+	0,
+};
+
 struct fs {
 	const char *		 const name;
 	const char * const *	 const mounts;
@@ -150,6 +159,7 @@ FS(debugfs, debugfs, DEBUGFS);
 FS(tracefs, tracefs, TRACEFS);
 FS(hugetlbfs, hugetlbfs, HUGETLBFS);
 FS(bpf_fs, bpf, BPF_FS);
+FS(eventfs, eventfs, EVENTFS_SUPER);
 
 static bool fs__read_mounts(struct fs *fs)
 {
diff --git a/tools/lib/api/fs/fs.h b/tools/lib/api/fs/fs.h
index aa222ca30311..57790c93f80e 100644
--- a/tools/lib/api/fs/fs.h
+++ b/tools/lib/api/fs/fs.h
@@ -36,6 +36,7 @@ FS(debugfs)
 FS(tracefs)
 FS(hugetlbfs)
 FS(bpf_fs)
+FS(eventfs)
 
 #undef FS
 
diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c
index 834fd64c7130..de4ed7c61415 100644
--- a/tools/lib/api/fs/tracing_path.c
+++ b/tools/lib/api/fs/tracing_path.c
@@ -3,6 +3,8 @@
 # define _GNU_SOURCE
 #endif
 
+#include <dirent.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -80,13 +82,46 @@ void put_tracing_file(char *file)
 	free(file);
 }
 
+char *get_events_dir(void)
+{
+	const char *eventfs;
+	char *path;
+	int saved_errno;
+
+	path = get_tracing_file("events");
+	if (path && faccessat(AT_FDCWD, path, R_OK, AT_EACCESS) == 0)
+		return path;
+
+	saved_errno = errno;
+	put_tracing_file(path);
+
+	eventfs = eventfs__mount();
+	if (eventfs && faccessat(AT_FDCWD, eventfs, R_OK, AT_EACCESS) == 0 &&
+	    eventfs__configured())
+		return strdup(eventfs);
+
+	/*
+	 * Prefer EACCES over other errors: it tells the user that events
+	 * exist but are not accessible, which is more actionable than
+	 * ENOENT from a missing filesystem.
+	 */
+	if (errno != EACCES)
+		errno = saved_errno;
+	return NULL;
+}
+
 char *get_events_file(const char *name)
 {
-	char *file;
+	char *dir, *file;
 
-	if (asprintf(&file, "%s/events/%s", tracing_path_mount(), name) < 0)
+	dir = get_events_dir();
+	if (!dir)
 		return NULL;
 
+	if (asprintf(&file, "%s/%s", dir, name) < 0)
+		file = NULL;
+
+	free(dir);
 	return file;
 }
 
@@ -97,8 +132,8 @@ void put_events_file(char *file)
 
 DIR *tracing_events__opendir(void)
 {
+	char *path = get_events_dir();
 	DIR *dir = NULL;
-	char *path = get_tracing_file("events");
 
 	if (path) {
 		dir = opendir(path);
@@ -110,7 +145,7 @@ DIR *tracing_events__opendir(void)
 
 int tracing_events__scandir_alphasort(struct dirent ***namelist)
 {
-	char *path = get_tracing_file("events");
+	char *path = get_events_dir();
 	int ret;
 
 	if (!path) {
@@ -121,6 +156,9 @@ int tracing_events__scandir_alphasort(struct dirent ***namelist)
 	ret = scandir(path, namelist, NULL, alphasort);
 	put_events_file(path);
 
+	if (ret < 0)
+		*namelist = NULL;
+
 	return ret;
 }
 
@@ -162,12 +200,12 @@ int tracing_path__strerror_open_tp(int err, char *buf, size_t size,
 			 "Hint:\tIs the debugfs/tracefs filesystem mounted?\n"
 			 "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
 		break;
-	case EACCES: {
+	case EACCES:
 		snprintf(buf, size,
 			 "Error:\tNo permissions to read %s/events/%s\n"
-			 "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
+			 "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n"
+			 "Hint:\tOr check if /sys/kernel/events is available\n",
 			 tracing_path, filename, tracing_path_mount());
-	}
 		break;
 	default:
 		snprintf(buf, size, "%s", str_error_r(err, sbuf, sizeof(sbuf)));
diff --git a/tools/lib/api/fs/tracing_path.h b/tools/lib/api/fs/tracing_path.h
index fc6347c11deb..a93befb38f80 100644
--- a/tools/lib/api/fs/tracing_path.h
+++ b/tools/lib/api/fs/tracing_path.h
@@ -14,6 +14,7 @@ const char *tracing_path_mount(void);
 char *get_tracing_file(const char *name);
 void put_tracing_file(char *file);
 
+char *get_events_dir(void);
 char *get_events_file(const char *name);
 void put_events_file(char *file);
 
diff --git a/tools/perf/util/tp_pmu.c b/tools/perf/util/tp_pmu.c
index c2be8c9f9084..4f45bd816945 100644
--- a/tools/perf/util/tp_pmu.c
+++ b/tools/perf/util/tp_pmu.c
@@ -6,6 +6,7 @@
 #include <api/io_dir.h>
 #include <linux/kernel.h>
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
 
 int tp_pmu__id(const char *sys, const char *name)
@@ -15,7 +16,7 @@ int tp_pmu__id(const char *sys, const char *name)
 	int id, err;
 
 	if (!tp_dir)
-		return -1;
+		return -errno;
 
 	scnprintf(path, PATH_MAX, "%s/%s/id", tp_dir, name);
 	put_events_file(tp_dir);
@@ -66,7 +67,7 @@ int tp_pmu__for_each_tp_sys(void *state, tp_sys_callback cb)
 	struct io_dirent64 *events_ent;
 	struct io_dir events_dir;
 	int ret = 0;
-	char *events_dir_path = get_tracing_file("events");
+	char *events_dir_path = get_events_dir();
 
 	if (!events_dir_path)
 		return -errno;
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 45774722f249..643451d2c08d 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -92,8 +92,9 @@ static int record_header_files(void)
 	int err = -EIO;
 
 	if (!path) {
+		err = -errno;
 		pr_debug("can't get tracing/events/header_page");
-		return -ENOMEM;
+		return err;
 	}
 
 	if (stat(path, &st) < 0) {
@@ -115,8 +116,8 @@ static int record_header_files(void)
 
 	path = get_events_file("header_event");
 	if (!path) {
+		err = -errno;
 		pr_debug("can't get tracing/events/header_event");
-		err = -ENOMEM;
 		goto out;
 	}
 
@@ -228,13 +229,14 @@ static int record_ftrace_files(struct tracepoint_path *tps)
 
 	path = get_events_file("ftrace");
 	if (!path) {
+		ret = -errno;
 		pr_debug("can't get tracing/events/ftrace");
-		return -ENOMEM;
+		return ret;
 	}
 
 	ret = copy_event_system(path, tps);
 
-	put_tracing_file(path);
+	put_events_file(path);
 
 	return ret;
 }
@@ -256,15 +258,16 @@ static int record_event_files(struct tracepoint_path *tps)
 	struct stat st;
 	char *path;
 	char *sys;
-	DIR *dir;
+	DIR *dir = NULL;
 	int count = 0;
 	int ret;
 	int err;
 
-	path = get_tracing_file("events");
+	path = get_events_dir();
 	if (!path) {
+		err = -errno;
 		pr_debug("can't get tracing/events");
-		return -ENOMEM;
+		goto out;
 	}
 
 	dir = opendir(path);
@@ -315,7 +318,7 @@ static int record_event_files(struct tracepoint_path *tps)
 out:
 	if (dir)
 		closedir(dir);
-	put_tracing_file(path);
+	put_events_file(path);
 
 	return err;
 }
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.