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


Reply via email to