* [PATCH v1 01/11] perf tools: Add HEADER_BOOT_ID feature
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 02/11] perf tools: Add perf_env__is_same_boot() Namhyung Kim
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Save /proc/sys/kernel/random/boot_id in the perf.data header and
struct perf_env so post-processing tools can identify if it's the
same kernel boot on which the data was recorded.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
.../Documentation/perf.data-file-format.txt | 5 +++
tools/perf/builtin-inject.c | 1 +
tools/perf/util/env.c | 44 +++++++++++++++++++
tools/perf/util/env.h | 2 +
| 28 ++++++++++++
| 1 +
6 files changed, 81 insertions(+)
diff --git a/tools/perf/Documentation/perf.data-file-format.txt b/tools/perf/Documentation/perf.data-file-format.txt
index 1f8e7d8e9ae21d73..359d6ef0c80aac26 100644
--- a/tools/perf/Documentation/perf.data-file-format.txt
+++ b/tools/perf/Documentation/perf.data-file-format.txt
@@ -491,6 +491,11 @@ struct memory_range {
u8 remote_region_id;
}; [nr_memory_ranges]
+ HEADER_BOOT_ID = 36,
+
+ A perf_header_string with the kernel boot ID read from
+ /proc/sys/kernel/random/boot_id.
+
other bits are reserved and should be ignored for now
HEADER_FEAT_BITS = 256,
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index ce7562fc788c246e..ddb21d888e00d143 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -2307,6 +2307,7 @@ static bool keep_feat(struct perf_inject *inject, int feat)
case HEADER_CPU_DOMAIN_INFO:
case HEADER_CLN_SIZE:
case HEADER_MEMORY_RANGES:
+ case HEADER_BOOT_ID:
return true;
/* Information that can be updated */
case HEADER_BUILD_ID:
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 50993b0dedc577c7..8ca0b677f3172b5f 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -12,6 +12,7 @@
#include <linux/string.h>
#include <linux/zalloc.h>
#include "cgroup.h"
+#include <api/fs/fs.h>
#include <errno.h>
#include <sys/utsname.h>
#include <stdlib.h>
@@ -258,6 +259,7 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->os_release);
zfree(&env->version);
zfree(&env->arch);
+ zfree(&env->boot_id);
zfree(&env->cpu_desc);
zfree(&env->cpuid);
zfree(&env->cmdline);
@@ -400,6 +402,48 @@ const char *perf_env__os_release(struct perf_env *env)
return release;
}
+static char *read_host_boot_id(void)
+{
+ char *buf = NULL;
+ size_t sz;
+
+ if (procfs__read_str("sys/kernel/random/boot_id", &buf, &sz) < 0)
+ return NULL;
+
+ strim(buf);
+ return buf;
+}
+
+const char *perf_env__boot_id(struct perf_env *env)
+{
+ const char *boot_id;
+
+ if (!env)
+ return NULL;
+
+ mutex_lock(&env->lock);
+ if (env->boot_id) {
+ boot_id = env->boot_id;
+ goto out;
+ }
+
+ /*
+ * If env->arch or env->hostname is set, this is an offline target
+ * environment. If the boot_id is not populated in the file, do not
+ * fall back to the host's boot_id.
+ */
+ if (env->arch || env->hostname) {
+ boot_id = NULL;
+ goto out;
+ }
+
+ env->boot_id = read_host_boot_id();
+ boot_id = env->boot_id;
+out:
+ mutex_unlock(&env->lock);
+ return boot_id;
+}
+
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
{
int i;
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 23d35425efe38b24..772d11eed1bc2a7c 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -81,6 +81,7 @@ struct perf_env {
char *os_release;
char *version;
char *arch;
+ char *boot_id;
/* e_machine expanded from 16 to 32-bits for alignment. */
u32 e_machine;
u32 e_flags;
@@ -187,6 +188,7 @@ void perf_env__exit(struct perf_env *env);
int perf_env__kernel_is_64_bit(struct perf_env *env);
bool perf_arch_is_big_endian(const char *arch);
const char *perf_env__os_release(struct perf_env *env);
+const char *perf_env__boot_id(struct perf_env *env);
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 9eec6deee190ff21..3e0b6fce55973dd1 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -494,6 +494,27 @@ static int write_version(struct feat_fd *ff,
return do_write_string(ff, perf_version_string);
}
+static int write_boot_id(struct feat_fd *ff, struct evlist *evlist)
+{
+ const char *boot_id = NULL;
+ char *buf = NULL;
+ size_t sz;
+ int ret;
+
+ if (evlist__session(evlist))
+ boot_id = perf_env__boot_id(perf_session__env(evlist__session(evlist)));
+
+ if (!boot_id) {
+ if (procfs__read_str("sys/kernel/random/boot_id", &buf, &sz) < 0)
+ return -1;
+ boot_id = strim(buf);
+ }
+
+ ret = do_write_string(ff, boot_id);
+ free(buf);
+ return ret;
+}
+
static int __write_cpudesc(struct feat_fd *ff, const char *cpuinfo_proc)
{
FILE *file;
@@ -2079,6 +2100,11 @@ static void print_version(struct feat_fd *ff, FILE *fp)
fprintf(fp, "# perf version : %s\n", ff->ph->env.version);
}
+static void print_boot_id(struct feat_fd *ff, FILE *fp)
+{
+ fprintf(fp, "# boot id : %s\n", ff->ph->env.boot_id);
+}
+
static void print_cmdline(struct feat_fd *ff, FILE *fp)
{
int nr, i;
@@ -3022,6 +3048,7 @@ FEAT_PROCESS_STR_FUN(osrelease, os_release);
FEAT_PROCESS_STR_FUN(version, version);
FEAT_PROCESS_STR_FUN(cpudesc, cpu_desc);
FEAT_PROCESS_STR_FUN(cpuid, cpuid);
+FEAT_PROCESS_STR_FUN(boot_id, boot_id);
static int process_arch(struct feat_fd *ff, void *data __maybe_unused)
{
@@ -4496,6 +4523,7 @@ const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPR(E_MACHINE, e_machine, false),
FEAT_OPR(CLN_SIZE, cln_size, false),
FEAT_OPR(MEMORY_RANGES, memory_ranges, false),
+ FEAT_OPR(BOOT_ID, boot_id, false),
};
struct header_print_data {
--git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 765037762758ca11..5bbf16342b4d26d8 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -57,6 +57,7 @@ enum {
HEADER_E_MACHINE,
HEADER_CLN_SIZE,
HEADER_MEMORY_RANGES,
+ HEADER_BOOT_ID,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 02/11] perf tools: Add perf_env__is_same_boot()
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 01/11] perf tools: Add HEADER_BOOT_ID feature Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 03/11] perf tools: Add nsinfo__new_thread() to track tid Namhyung Kim
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Add perf_env__is_same_boot() to compare the recorded boot_id in struct
perf_env against the running host's /proc/sys/kernel/random/boot_id.
For live sessions where no offline header was populated, it falls back
to reading the host boot_id and returns true.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/env.c | 21 +++++++++++++++++++++
tools/perf/util/env.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 8ca0b677f3172b5f..735db21d66a019a5 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -444,6 +444,27 @@ const char *perf_env__boot_id(struct perf_env *env)
return boot_id;
}
+bool perf_env__is_same_boot(struct perf_env *env)
+{
+ static char *host_boot_id;
+ const char *env_boot_id;
+
+ if (!host_boot_id) {
+ host_boot_id = read_host_boot_id();
+ if (!host_boot_id)
+ return false;
+ }
+
+ if (!env)
+ return true;
+
+ env_boot_id = perf_env__boot_id(env);
+ if (!env_boot_id)
+ return false;
+
+ return !strcmp(env_boot_id, host_boot_id);
+}
+
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
{
int i;
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 772d11eed1bc2a7c..df1d3d1ac3376e1e 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -189,6 +189,7 @@ int perf_env__kernel_is_64_bit(struct perf_env *env);
bool perf_arch_is_big_endian(const char *arch);
const char *perf_env__os_release(struct perf_env *env);
const char *perf_env__boot_id(struct perf_env *env);
+bool perf_env__is_same_boot(struct perf_env *env);
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[]);
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 03/11] perf tools: Add nsinfo__new_thread() to track tid
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 01/11] perf tools: Add HEADER_BOOT_ID feature Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 02/11] perf tools: Add perf_env__is_same_boot() Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 04/11] perf tools: Defer /proc lookup until nsinfo is used Namhyung Kim
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Record the thread ID (tid) in struct nsinfo via nsinfo__new_thread() and
access /proc/<pid>/task/<tid>/ status and ns/mnt paths instead of only
/proc/<pid>/. Also recreate thread->nsinfo when the pid of the thread
is updated.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/machine.c | 3 +++
tools/perf/util/namespaces.c | 22 ++++++++++++++++++----
tools/perf/util/namespaces.h | 3 +++
tools/perf/util/thread.c | 2 +-
4 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 1d9d3bf57720008e..14f52234d4d99fb6 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -19,6 +19,7 @@
#include "branch.h"
#include "mem-events.h"
#include "mem-info.h"
+#include "namespaces.h"
#include "path.h"
#include "srcline.h"
#include "symbol.h"
@@ -495,6 +496,8 @@ static void machine__update_thread_pid(struct machine *machine,
return;
thread__set_pid(th, pid);
+ nsinfo__zput(RC_CHK_ACCESS(th)->nsinfo);
+ RC_CHK_ACCESS(th)->nsinfo = nsinfo__new_thread(pid, thread__tid(th));
if (thread__pid(th) == thread__tid(th))
return;
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 01502570b32d02ab..f426eba04d6ef449 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -106,7 +106,8 @@ int nsinfo__init(struct nsinfo *nsi)
if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
return rv;
- if (asprintf(&newns, "/proc/%d/ns/mnt", nsinfo__pid(nsi)) == -1)
+ if (asprintf(&newns, "/proc/%d/task/%d/ns/mnt",
+ nsinfo__pid(nsi), nsinfo__tid(nsi)) == -1)
return rv;
if (stat(oldns, &old_stat) < 0)
@@ -127,7 +128,8 @@ int nsinfo__init(struct nsinfo *nsi)
/* If we're dealing with a process that is in a different PID namespace,
* attempt to work out the innermost tgid for the process.
*/
- if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX)
+ if (snprintf(spath, PATH_MAX, "/proc/%d/task/%d/status",
+ nsinfo__pid(nsi), nsinfo__tid(nsi)) >= PATH_MAX)
goto out;
rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
@@ -150,11 +152,11 @@ static struct nsinfo *nsinfo__alloc(void)
return res;
}
-struct nsinfo *nsinfo__new(pid_t pid)
+struct nsinfo *nsinfo__new_thread(pid_t pid, pid_t tid)
{
struct nsinfo *nsi;
- if (pid == 0)
+ if (pid <= 0 || tid <= 0)
return NULL;
nsi = nsinfo__alloc();
@@ -162,6 +164,7 @@ struct nsinfo *nsinfo__new(pid_t pid)
return NULL;
RC_CHK_ACCESS(nsi)->pid = pid;
+ RC_CHK_ACCESS(nsi)->tid = tid;
RC_CHK_ACCESS(nsi)->tgid = pid;
RC_CHK_ACCESS(nsi)->nstgid = pid;
nsinfo__clear_need_setns(nsi);
@@ -176,6 +179,11 @@ struct nsinfo *nsinfo__new(pid_t pid)
return nsi;
}
+struct nsinfo *nsinfo__new(pid_t pid)
+{
+ return nsinfo__new_thread(pid, pid);
+}
+
static const char *nsinfo__mntns_path(const struct nsinfo *nsi)
{
return RC_CHK_ACCESS(nsi)->mntns_path;
@@ -193,6 +201,7 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
return NULL;
RC_CHK_ACCESS(nnsi)->pid = nsinfo__pid(nsi);
+ RC_CHK_ACCESS(nnsi)->tid = nsinfo__tid(nsi);
RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi);
RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi);
@@ -265,6 +274,11 @@ pid_t nsinfo__pid(const struct nsinfo *nsi)
return RC_CHK_ACCESS(nsi)->pid;
}
+pid_t nsinfo__tid(const struct nsinfo *nsi)
+{
+ return RC_CHK_ACCESS(nsi)->tid;
+}
+
bool nsinfo__in_pidns(const struct nsinfo *nsi)
{
return RC_CHK_ACCESS(nsi)->in_pidns;
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index e95c79b80e27c8fc..ea645954944de4be 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -32,6 +32,7 @@ void namespaces__free(struct namespaces *namespaces);
DECLARE_RC_STRUCT(nsinfo) {
pid_t pid;
+ pid_t tid;
pid_t tgid;
pid_t nstgid;
bool need_setns;
@@ -48,6 +49,7 @@ struct nscookie {
int nsinfo__init(struct nsinfo *nsi);
struct nsinfo *nsinfo__new(pid_t pid);
+struct nsinfo *nsinfo__new_thread(pid_t pid, pid_t tid);
struct nsinfo *nsinfo__copy(const struct nsinfo *nsi);
struct nsinfo *nsinfo__get(struct nsinfo *nsi);
@@ -58,6 +60,7 @@ void nsinfo__clear_need_setns(struct nsinfo *nsi);
pid_t nsinfo__tgid(const struct nsinfo *nsi);
pid_t nsinfo__nstgid(const struct nsinfo *nsi);
pid_t nsinfo__pid(const struct nsinfo *nsi);
+pid_t nsinfo__tid(const struct nsinfo *nsi);
bool nsinfo__in_pidns(const struct nsinfo *nsi);
void nsinfo__set_in_pidns(struct nsinfo *nsi);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 91118c301913b834..d12eb6bc63313b00 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -71,7 +71,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
list_add(&comm->list, thread__comm_list(thread));
refcount_set(thread__refcnt(thread), 1);
/* Thread holds first ref to nsdata. */
- RC_CHK_ACCESS(thread)->nsinfo = nsinfo__new(pid);
+ RC_CHK_ACCESS(thread)->nsinfo = nsinfo__new_thread(pid, tid);
srccode_state_init(thread__srccode_state(thread));
}
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 04/11] perf tools: Defer /proc lookup until nsinfo is used
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (2 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 03/11] perf tools: Add nsinfo__new_thread() to track tid Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 05/11] perf tools: Verify thread status and mount namespace inode Namhyung Kim
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Do not read /proc/<pid>/task/<tid>/ eagerly in nsinfo__new_thread().
Instead, initialize struct nsinfo on demand via nsinfo__ensure_init()
when namespace information is queried or entered.
Also reset the initialization state on exec and PERF_RECORD_NAMESPACES
events so namespace changes are picked up.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/namespaces.c | 46 ++++++++++++++++++++++++++++--------
tools/perf/util/namespaces.h | 2 ++
tools/perf/util/thread.c | 4 ++++
3 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index f426eba04d6ef449..843d075d30409029 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -103,6 +103,11 @@ int nsinfo__init(struct nsinfo *nsi)
struct stat new_stat;
int rv = -1;
+ if (!nsi)
+ return -1;
+
+ RC_CHK_ACCESS(nsi)->inited = true;
+
if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
return rv;
@@ -140,6 +145,12 @@ int nsinfo__init(struct nsinfo *nsi)
return rv;
}
+static void nsinfo__ensure_init(struct nsinfo *nsi)
+{
+ if (nsi && !RC_CHK_ACCESS(nsi)->inited)
+ nsinfo__init(nsi);
+}
+
static struct nsinfo *nsinfo__alloc(void)
{
struct nsinfo *res;
@@ -167,15 +178,6 @@ struct nsinfo *nsinfo__new_thread(pid_t pid, pid_t tid)
RC_CHK_ACCESS(nsi)->tid = tid;
RC_CHK_ACCESS(nsi)->tgid = pid;
RC_CHK_ACCESS(nsi)->nstgid = pid;
- nsinfo__clear_need_setns(nsi);
- RC_CHK_ACCESS(nsi)->in_pidns = false;
- /* Init may fail if the process exits while we're trying to look at its
- * proc information. In that case, save the pid but don't try to enter
- * the namespace.
- */
- if (nsinfo__init(nsi) == -1)
- nsinfo__clear_need_setns(nsi);
-
return nsi;
}
@@ -196,6 +198,8 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
if (nsi == NULL)
return NULL;
+ nsinfo__ensure_init((struct nsinfo *)nsi);
+
nnsi = nsinfo__alloc();
if (!nnsi)
return NULL;
@@ -204,7 +208,8 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
RC_CHK_ACCESS(nnsi)->tid = nsinfo__tid(nsi);
RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi);
RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
- RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi);
+ RC_CHK_ACCESS(nnsi)->inited = RC_CHK_ACCESS(nsi)->inited;
+ RC_CHK_ACCESS(nnsi)->need_setns = RC_CHK_ACCESS(nsi)->need_setns;
RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
if (nsinfo__mntns_path(nsi)) {
RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi));
@@ -251,21 +256,41 @@ void nsinfo__put(struct nsinfo *nsi)
bool nsinfo__need_setns(const struct nsinfo *nsi)
{
+ if (!nsi)
+ return false;
+
+ nsinfo__ensure_init((struct nsinfo *)nsi);
return RC_CHK_ACCESS(nsi)->need_setns;
}
void nsinfo__clear_need_setns(struct nsinfo *nsi)
{
+ if (!nsi)
+ return;
+
+ RC_CHK_ACCESS(nsi)->inited = true;
RC_CHK_ACCESS(nsi)->need_setns = false;
+ zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
+}
+
+void nsinfo__reset_init(struct nsinfo *nsi)
+{
+ if (!nsi)
+ return;
+
+ nsinfo__clear_need_setns(nsi);
+ RC_CHK_ACCESS(nsi)->inited = false;
}
pid_t nsinfo__tgid(const struct nsinfo *nsi)
{
+ nsinfo__ensure_init((struct nsinfo *)nsi);
return RC_CHK_ACCESS(nsi)->tgid;
}
pid_t nsinfo__nstgid(const struct nsinfo *nsi)
{
+ nsinfo__ensure_init((struct nsinfo *)nsi);
return RC_CHK_ACCESS(nsi)->nstgid;
}
@@ -281,6 +306,7 @@ pid_t nsinfo__tid(const struct nsinfo *nsi)
bool nsinfo__in_pidns(const struct nsinfo *nsi)
{
+ nsinfo__ensure_init((struct nsinfo *)nsi);
return RC_CHK_ACCESS(nsi)->in_pidns;
}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index ea645954944de4be..77fe6f0eaf2f0618 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -35,6 +35,7 @@ DECLARE_RC_STRUCT(nsinfo) {
pid_t tid;
pid_t tgid;
pid_t nstgid;
+ bool inited;
bool need_setns;
bool in_pidns;
char *mntns_path;
@@ -57,6 +58,7 @@ void nsinfo__put(struct nsinfo *nsi);
bool nsinfo__need_setns(const struct nsinfo *nsi);
void nsinfo__clear_need_setns(struct nsinfo *nsi);
+void nsinfo__reset_init(struct nsinfo *nsi);
pid_t nsinfo__tgid(const struct nsinfo *nsi);
pid_t nsinfo__nstgid(const struct nsinfo *nsi);
pid_t nsinfo__pid(const struct nsinfo *nsi);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index d12eb6bc63313b00..377c39ec624bb7bb 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -199,6 +199,8 @@ int thread__set_namespaces(struct thread *thread, u64 timestamp,
down_write(thread__namespaces_lock(thread));
ret = __thread__set_namespaces(thread, timestamp, event);
up_write(thread__namespaces_lock(thread));
+ if (!ret && event)
+ nsinfo__reset_init(thread__nsinfo(thread));
return ret;
}
@@ -282,6 +284,8 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
down_write(thread__comm_lock(thread));
ret = ____thread__set_comm(thread, str, timestamp, exec);
up_write(thread__comm_lock(thread));
+ if (!ret && exec)
+ nsinfo__reset_init(thread__nsinfo(thread));
return ret;
}
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 05/11] perf tools: Verify thread status and mount namespace inode
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (3 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 04/11] perf tools: Defer /proc lookup until nsinfo is used Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 06/11] perf tools: Check thread liveness when accessing /proc Namhyung Kim
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Verify /proc/<pid>/task/<tid>/status exists and has Tgid == pid before
reading or entering a thread's namespaces to guard against PID/TID
recycling.
Also record the mount namespace inode (mntns_ino) during initialization
and verify via fstat() that the opened namespace file descriptor still
refers to the same mount namespace.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/namespaces.c | 120 +++++++++++++++++++++++++----------
tools/perf/util/namespaces.h | 4 ++
2 files changed, 91 insertions(+), 33 deletions(-)
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 843d075d30409029..bb119a6cc6064d89 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -65,6 +65,7 @@ static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const c
char *statln = NULL;
size_t linesz = 0;
char *nspid;
+ bool got_tgid = false;
f = fopen(path, "r");
if (f == NULL)
@@ -75,6 +76,7 @@ static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const c
if (strstr(statln, "Tgid:") != NULL) {
*tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10);
*nstgid = *tgid;
+ got_tgid = true;
}
if (strstr(statln, "NStgid:") != NULL) {
@@ -91,7 +93,7 @@ static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const c
fclose(f);
free(statln);
- return 0;
+ return got_tgid ? 0 : -1;
}
int nsinfo__init(struct nsinfo *nsi)
@@ -103,17 +105,34 @@ int nsinfo__init(struct nsinfo *nsi)
struct stat new_stat;
int rv = -1;
- if (!nsi)
+ if (!nsi || RC_CHK_ACCESS(nsi)->exited)
return -1;
RC_CHK_ACCESS(nsi)->inited = true;
+ /*
+ * Verify that the thread (pid, tid) still exists in /proc and its Tgid
+ * matches pid (guarding against PID/TID recycling), while resolving the
+ * innermost tgid if the process is in a different PID namespace.
+ */
+ if (snprintf(spath, PATH_MAX, "/proc/%d/task/%d/status",
+ nsinfo__pid(nsi), nsinfo__tid(nsi)) >= PATH_MAX)
+ goto out;
+
+ rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
+ &RC_CHK_ACCESS(nsi)->in_pidns, spath);
+ if (rv < 0 || RC_CHK_ACCESS(nsi)->tgid != nsinfo__pid(nsi)) {
+ nsinfo__set_exited(nsi);
+ rv = -1;
+ goto out;
+ }
+
if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
- return rv;
+ return -1;
if (asprintf(&newns, "/proc/%d/task/%d/ns/mnt",
nsinfo__pid(nsi), nsinfo__tid(nsi)) == -1)
- return rv;
+ return -1;
if (stat(oldns, &old_stat) < 0)
goto out;
@@ -127,19 +146,10 @@ int nsinfo__init(struct nsinfo *nsi)
if (old_stat.st_ino != new_stat.st_ino) {
RC_CHK_ACCESS(nsi)->need_setns = true;
RC_CHK_ACCESS(nsi)->mntns_path = newns;
+ RC_CHK_ACCESS(nsi)->mntns_ino = new_stat.st_ino;
newns = NULL;
}
- /* If we're dealing with a process that is in a different PID namespace,
- * attempt to work out the innermost tgid for the process.
- */
- if (snprintf(spath, PATH_MAX, "/proc/%d/task/%d/status",
- nsinfo__pid(nsi), nsinfo__tid(nsi)) >= PATH_MAX)
- goto out;
-
- rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
- &RC_CHK_ACCESS(nsi)->in_pidns, spath);
-
out:
free(newns);
return rv;
@@ -147,7 +157,7 @@ int nsinfo__init(struct nsinfo *nsi)
static void nsinfo__ensure_init(struct nsinfo *nsi)
{
- if (nsi && !RC_CHK_ACCESS(nsi)->inited)
+ if (nsi && !RC_CHK_ACCESS(nsi)->inited && !RC_CHK_ACCESS(nsi)->exited)
nsinfo__init(nsi);
}
@@ -208,7 +218,9 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
RC_CHK_ACCESS(nnsi)->tid = nsinfo__tid(nsi);
RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi);
RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
+ RC_CHK_ACCESS(nnsi)->mntns_ino = RC_CHK_ACCESS(nsi)->mntns_ino;
RC_CHK_ACCESS(nnsi)->inited = RC_CHK_ACCESS(nsi)->inited;
+ RC_CHK_ACCESS(nnsi)->exited = RC_CHK_ACCESS(nsi)->exited;
RC_CHK_ACCESS(nnsi)->need_setns = RC_CHK_ACCESS(nsi)->need_setns;
RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
if (nsinfo__mntns_path(nsi)) {
@@ -260,7 +272,7 @@ bool nsinfo__need_setns(const struct nsinfo *nsi)
return false;
nsinfo__ensure_init((struct nsinfo *)nsi);
- return RC_CHK_ACCESS(nsi)->need_setns;
+ return !RC_CHK_ACCESS(nsi)->exited && RC_CHK_ACCESS(nsi)->need_setns;
}
void nsinfo__clear_need_setns(struct nsinfo *nsi)
@@ -273,15 +285,44 @@ void nsinfo__clear_need_setns(struct nsinfo *nsi)
zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
}
-void nsinfo__reset_init(struct nsinfo *nsi)
+void nsinfo__set_exited(struct nsinfo *nsi)
{
if (!nsi)
return;
+ RC_CHK_ACCESS(nsi)->exited = true;
+ nsinfo__clear_need_setns(nsi);
+}
+
+void nsinfo__reset_init(struct nsinfo *nsi)
+{
+ if (!nsi || RC_CHK_ACCESS(nsi)->exited)
+ return;
+
nsinfo__clear_need_setns(nsi);
RC_CHK_ACCESS(nsi)->inited = false;
}
+bool nsinfo__check_proc(struct nsinfo *nsi)
+{
+ char spath[64];
+ pid_t tgid = 0, nstgid = 0;
+ bool in_pidns = false;
+
+ if (!nsi || RC_CHK_ACCESS(nsi)->exited)
+ return false;
+
+ scnprintf(spath, sizeof(spath), "/proc/%d/task/%d/status",
+ nsinfo__pid(nsi), nsinfo__tid(nsi));
+ if (nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, spath) < 0 ||
+ tgid != nsinfo__pid(nsi)) {
+ nsinfo__set_exited(nsi);
+ return false;
+ }
+
+ return true;
+}
+
pid_t nsinfo__tgid(const struct nsinfo *nsi)
{
nsinfo__ensure_init((struct nsinfo *)nsi);
@@ -328,10 +369,14 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
nc->oldns = -1;
nc->newns = -1;
+ nc->oldcwd = NULL;
if (!nsi || !nsinfo__need_setns(nsi))
return;
+ if (!nsinfo__check_proc(nsi))
+ return;
+
if (!getcwd(curpath, sizeof(curpath)))
return;
@@ -339,16 +384,26 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
if (!oldcwd)
return;
- oldns = open("/proc/self/ns/mnt", O_RDONLY);
- if (oldns < 0)
- goto errout;
+ if (RC_CHK_ACCESS(nsi)->need_setns) {
+ struct stat st;
+
+ oldns = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+ if (oldns < 0)
+ goto errout;
- newns = open(nsinfo__mntns_path(nsi), O_RDONLY);
- if (newns < 0)
- goto errout;
+ newns = open(nsinfo__mntns_path(nsi), O_RDONLY | O_CLOEXEC);
+ if (newns < 0)
+ goto errout;
- if (setns(newns, CLONE_NEWNS) < 0)
- goto errout;
+ if (fstat(newns, &st) < 0 ||
+ st.st_ino != RC_CHK_ACCESS(nsi)->mntns_ino) {
+ nsinfo__set_exited(nsi);
+ goto errout;
+ }
+
+ if (setns(newns, CLONE_NEWNS) < 0)
+ goto errout;
+ }
nc->oldcwd = oldcwd;
nc->oldns = oldns;
@@ -365,17 +420,11 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
void nsinfo__mountns_exit(struct nscookie *nc)
{
- if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
+ if (nc == NULL || !nc->oldcwd)
return;
- setns(nc->oldns, CLONE_NEWNS);
-
- if (nc->oldcwd) {
- WARN_ON_ONCE(chdir(nc->oldcwd));
- zfree(&nc->oldcwd);
- }
-
if (nc->oldns > -1) {
+ setns(nc->oldns, CLONE_NEWNS);
close(nc->oldns);
nc->oldns = -1;
}
@@ -384,6 +433,11 @@ void nsinfo__mountns_exit(struct nscookie *nc)
close(nc->newns);
nc->newns = -1;
}
+
+ if (nc->oldcwd) {
+ WARN_ON_ONCE(chdir(nc->oldcwd));
+ zfree(&nc->oldcwd);
+ }
}
char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 77fe6f0eaf2f0618..165e9c9994461595 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -35,7 +35,9 @@ DECLARE_RC_STRUCT(nsinfo) {
pid_t tid;
pid_t tgid;
pid_t nstgid;
+ ino_t mntns_ino;
bool inited;
+ bool exited;
bool need_setns;
bool in_pidns;
char *mntns_path;
@@ -58,7 +60,9 @@ void nsinfo__put(struct nsinfo *nsi);
bool nsinfo__need_setns(const struct nsinfo *nsi);
void nsinfo__clear_need_setns(struct nsinfo *nsi);
+void nsinfo__set_exited(struct nsinfo *nsi);
void nsinfo__reset_init(struct nsinfo *nsi);
+bool nsinfo__check_proc(struct nsinfo *nsi);
pid_t nsinfo__tgid(const struct nsinfo *nsi);
pid_t nsinfo__nstgid(const struct nsinfo *nsi);
pid_t nsinfo__pid(const struct nsinfo *nsi);
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 06/11] perf tools: Check thread liveness when accessing /proc
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (4 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 05/11] perf tools: Verify thread status and mount namespace inode Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 07/11] perf tools: Handle chroot inside nsinfo__mountns_enter() Namhyung Kim
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Add thread__exited() and thread__is_alive(), and mark threads as exited
on PERF_RECORD_EXIT and replaced threads on PERF_RECORD_FORK.
Propagate thread exit and boot ID mismatch to thread->nsinfo, and guard
/proc accesses with thread__is_alive().
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-trace.c | 2 +-
tools/perf/util/machine.c | 9 ++++++---
tools/perf/util/thread.c | 33 ++++++++++++++++++++++++++++++++-
tools/perf/util/thread.h | 7 +++++--
4 files changed, 44 insertions(+), 7 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 4094141461f91c32..a21bbf5926617633 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1858,7 +1858,7 @@ static const char *thread__fd_path(struct thread *thread, int fd,
return NULL;
if ((fd > ttrace->files.max || ttrace->files.table[fd].pathname == NULL)) {
- if (!trace->live)
+ if (!trace->live || !thread__is_alive(thread))
return NULL;
++trace->stats.proc_getname;
if (thread__read_fd_path(thread, fd))
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 14f52234d4d99fb6..8f6298aff681f3fc 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -498,6 +498,9 @@ static void machine__update_thread_pid(struct machine *machine,
thread__set_pid(th, pid);
nsinfo__zput(RC_CHK_ACCESS(th)->nsinfo);
RC_CHK_ACCESS(th)->nsinfo = nsinfo__new_thread(pid, thread__tid(th));
+ if (thread__exited(th) || !machine__is_host(machine) ||
+ !perf_env__is_same_boot(machine->env))
+ nsinfo__set_exited(thread__nsinfo(th));
if (thread__pid(th) == thread__tid(th))
return;
@@ -1954,6 +1957,7 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event
/* if a thread currently exists for the thread id remove it */
if (thread != NULL) {
+ thread__set_exited(thread, /*exited=*/true);
machine__remove_thread(machine, thread);
thread__put(thread);
}
@@ -2001,9 +2005,8 @@ int machine__process_exit_event(struct machine *machine, union perf_event *event
/* There is no context switch out before exit, so we decrement here. */
machine->parallelism--;
if (thread != NULL) {
- if (symbol_conf.keep_exited_threads)
- thread__set_exited(thread, /*exited=*/true);
- else
+ thread__set_exited(thread, /*exited=*/true);
+ if (!symbol_conf.keep_exited_threads)
machine__remove_thread(machine, thread);
}
thread__put(thread);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 377c39ec624bb7bb..f75eafb25bfc06cd 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -37,6 +37,10 @@ int thread__init_maps(struct thread *thread, struct machine *machine)
}
}
+ if (!machine || !machine__is_host(machine) ||
+ !perf_env__is_same_boot(machine->env))
+ nsinfo__set_exited(thread__nsinfo(thread));
+
return thread__maps(thread) ? 0 : -1;
}
@@ -289,6 +293,30 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
return ret;
}
+void thread__set_exited(struct thread *thread, bool exited)
+{
+ RC_CHK_ACCESS(thread)->exited = exited;
+ if (exited)
+ nsinfo__set_exited(thread__nsinfo(thread));
+}
+
+bool thread__is_alive(struct thread *thread)
+{
+ struct maps *maps;
+ struct machine *machine;
+
+ if (!thread || thread__exited(thread))
+ return false;
+
+ maps = thread__maps(thread);
+ machine = maps ? maps__machine(maps) : NULL;
+ if (machine && (!machine__is_host(machine) ||
+ !perf_env__is_same_boot(machine->env)))
+ return false;
+
+ return nsinfo__check_proc(thread__nsinfo(thread));
+}
+
int thread__set_comm_from_proc(struct thread *thread)
{
char path[64];
@@ -296,6 +324,9 @@ int thread__set_comm_from_proc(struct thread *thread)
size_t sz;
int err = -1;
+ if (!thread__is_alive(thread))
+ return -1;
+
if (!(snprintf(path, sizeof(path), "%d/task/%d/comm",
thread__pid(thread), thread__tid(thread)) >= (int)sizeof(path)) &&
procfs__read_str(path, &comm, &sz) == 0) {
@@ -541,7 +572,7 @@ uint16_t thread__e_machine_endian(struct thread *thread, struct machine *machine
is_live = !session || !session->data;
}
/* Read from /proc/pid/exe if live. */
- if (is_live) {
+ if (is_live && thread__is_alive(thread)) {
e_machine = read_proc_e_machine_for_pid(pid, &local_e_flags,
&args.is_big_endian);
} else if (machine && machine->env) {
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index d82fce8173aefedc..d6edbe9a840454f1 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -205,11 +205,14 @@ static inline refcount_t *thread__refcnt(struct thread *thread)
return &RC_CHK_ACCESS(thread)->refcnt;
}
-static inline void thread__set_exited(struct thread *thread, bool exited)
+static inline bool thread__exited(const struct thread *thread)
{
- RC_CHK_ACCESS(thread)->exited = exited;
+ return RC_CHK_ACCESS(thread)->exited;
}
+void thread__set_exited(struct thread *thread, bool exited);
+bool thread__is_alive(struct thread *thread);
+
static inline bool thread__comm_set(const struct thread *thread)
{
return RC_CHK_ACCESS(thread)->comm_set;
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 07/11] perf tools: Handle chroot inside nsinfo__mountns_enter()
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (5 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 06/11] perf tools: Check thread liveness when accessing /proc Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 08/11] perf tools: Export filename__read_build_id_ns() Namhyung Kim
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Read /proc/<pid>/task/<tid>/root in nsinfo__init() and enter/exit the
chroot directory alongside the mount namespace inside
nsinfo__mountns_enter() and nsinfo__mountns_exit().
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/namespaces.c | 60 +++++++++++++++++++++++++++++++++++-
tools/perf/util/namespaces.h | 3 ++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index bb119a6cc6064d89..1ec6b1ed4db23109 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -100,10 +100,12 @@ int nsinfo__init(struct nsinfo *nsi)
{
char oldns[PATH_MAX];
char spath[PATH_MAX];
+ char rpath[64];
char *newns = NULL;
struct stat old_stat;
struct stat new_stat;
int rv = -1;
+ ssize_t ret;
if (!nsi || RC_CHK_ACCESS(nsi)->exited)
return -1;
@@ -150,6 +152,19 @@ int nsinfo__init(struct nsinfo *nsi)
newns = NULL;
}
+ /* Check if the task is in a chroot directory. */
+ scnprintf(rpath, sizeof(rpath), "/proc/%d/task/%d/root",
+ nsinfo__pid(nsi), nsinfo__tid(nsi));
+ ret = readlink(rpath, spath, sizeof(spath) - 1);
+ if (ret > 0) {
+ spath[ret] = '\0';
+ if (strcmp(spath, "/") && !strstr(spath, "(deleted)")) {
+ RC_CHK_ACCESS(nsi)->chroot = strdup(spath);
+ if (RC_CHK_ACCESS(nsi)->chroot)
+ RC_CHK_ACCESS(nsi)->in_chroot = true;
+ }
+ }
+
out:
free(newns);
return rv;
@@ -201,6 +216,11 @@ static const char *nsinfo__mntns_path(const struct nsinfo *nsi)
return RC_CHK_ACCESS(nsi)->mntns_path;
}
+static const char *nsinfo__chroot(const struct nsinfo *nsi)
+{
+ return RC_CHK_ACCESS(nsi)->chroot;
+}
+
struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
{
struct nsinfo *nnsi;
@@ -222,6 +242,7 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
RC_CHK_ACCESS(nnsi)->inited = RC_CHK_ACCESS(nsi)->inited;
RC_CHK_ACCESS(nnsi)->exited = RC_CHK_ACCESS(nsi)->exited;
RC_CHK_ACCESS(nnsi)->need_setns = RC_CHK_ACCESS(nsi)->need_setns;
+ RC_CHK_ACCESS(nnsi)->in_chroot = RC_CHK_ACCESS(nsi)->in_chroot;
RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
if (nsinfo__mntns_path(nsi)) {
RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi));
@@ -230,6 +251,13 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
return NULL;
}
}
+ if (nsinfo__chroot(nsi)) {
+ RC_CHK_ACCESS(nnsi)->chroot = strdup(nsinfo__chroot(nsi));
+ if (!RC_CHK_ACCESS(nnsi)->chroot) {
+ nsinfo__put(nnsi);
+ return NULL;
+ }
+ }
return nnsi;
}
@@ -244,6 +272,7 @@ static void nsinfo__delete(struct nsinfo *nsi)
if (nsi) {
WARN_ONCE(refcount_read(nsinfo__refcnt(nsi)) != 0, "nsinfo refcnt unbalanced\n");
zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
+ zfree(&RC_CHK_ACCESS(nsi)->chroot);
RC_CHK_FREE(nsi);
}
}
@@ -272,7 +301,8 @@ bool nsinfo__need_setns(const struct nsinfo *nsi)
return false;
nsinfo__ensure_init((struct nsinfo *)nsi);
- return !RC_CHK_ACCESS(nsi)->exited && RC_CHK_ACCESS(nsi)->need_setns;
+ return !RC_CHK_ACCESS(nsi)->exited &&
+ (RC_CHK_ACCESS(nsi)->need_setns || RC_CHK_ACCESS(nsi)->in_chroot);
}
void nsinfo__clear_need_setns(struct nsinfo *nsi)
@@ -282,7 +312,9 @@ void nsinfo__clear_need_setns(struct nsinfo *nsi)
RC_CHK_ACCESS(nsi)->inited = true;
RC_CHK_ACCESS(nsi)->need_setns = false;
+ RC_CHK_ACCESS(nsi)->in_chroot = false;
zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
+ zfree(&RC_CHK_ACCESS(nsi)->chroot);
}
void nsinfo__set_exited(struct nsinfo *nsi)
@@ -362,6 +394,7 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
char curpath[PATH_MAX];
int oldns = -1;
int newns = -1;
+ int oldroot = -1;
char *oldcwd = NULL;
if (nc == NULL)
@@ -369,6 +402,7 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
nc->oldns = -1;
nc->newns = -1;
+ nc->oldroot = -1;
nc->oldcwd = NULL;
if (!nsi || !nsinfo__need_setns(nsi))
@@ -405,13 +439,30 @@ void nsinfo__mountns_enter(struct nsinfo *nsi,
goto errout;
}
+ if (RC_CHK_ACCESS(nsi)->in_chroot) {
+ oldroot = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (oldroot < 0)
+ goto errout_setns;
+
+ if (chroot(nsinfo__chroot(nsi)) < 0)
+ goto errout_setns;
+ }
+
nc->oldcwd = oldcwd;
nc->oldns = oldns;
nc->newns = newns;
+ nc->oldroot = oldroot;
return;
+errout_setns:
+ if (oldns > -1) {
+ setns(oldns, CLONE_NEWNS);
+ WARN_ON_ONCE(chdir(oldcwd));
+ }
errout:
free(oldcwd);
+ if (oldroot > -1)
+ close(oldroot);
if (oldns > -1)
close(oldns);
if (newns > -1)
@@ -423,6 +474,13 @@ void nsinfo__mountns_exit(struct nscookie *nc)
if (nc == NULL || !nc->oldcwd)
return;
+ if (nc->oldroot > -1) {
+ WARN_ON_ONCE(fchdir(nc->oldroot));
+ WARN_ON_ONCE(chroot("."));
+ close(nc->oldroot);
+ nc->oldroot = -1;
+ }
+
if (nc->oldns > -1) {
setns(nc->oldns, CLONE_NEWNS);
close(nc->oldns);
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 165e9c9994461595..640ce537295a88e5 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -39,14 +39,17 @@ DECLARE_RC_STRUCT(nsinfo) {
bool inited;
bool exited;
bool need_setns;
+ bool in_chroot;
bool in_pidns;
char *mntns_path;
+ char *chroot;
refcount_t refcnt;
};
struct nscookie {
int oldns;
int newns;
+ int oldroot;
char *oldcwd;
};
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 08/11] perf tools: Export filename__read_build_id_ns()
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (6 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 07/11] perf tools: Handle chroot inside nsinfo__mountns_enter() Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 09/11] perf tools: Remove redundant chroot handling Namhyung Kim
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Export filename__read_build_id_ns() and use it in the dsos callback
and perf-inject's dso__read_build_id() instead of open-coding
nsinfo__mountns_enter() and dso__filename_with_chroot() fallbacks.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/builtin-inject.c | 12 +-----------
tools/perf/util/build-id.c | 6 +++---
tools/perf/util/dsos.c | 17 +++--------------
tools/perf/util/symbol.h | 3 +++
4 files changed, 10 insertions(+), 28 deletions(-)
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index ddb21d888e00d143..2f044aef0bb32b94 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -894,24 +894,14 @@ static int perf_event__repipe_tracing_data(const struct perf_tool *tool,
static int dso__read_build_id(struct dso *dso)
{
- struct nscookie nsc;
struct build_id bid = { .size = 0, };
if (dso__has_build_id(dso))
return 0;
mutex_lock(dso__lock(dso));
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- if (filename__read_build_id(dso__long_name(dso), &bid) > 0)
+ if (filename__read_build_id_ns(dso__long_name(dso), &bid, dso__nsinfo(dso)) > 0)
dso__set_build_id(dso, &bid);
- else if (dso__nsinfo(dso)) {
- char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso));
-
- if (new_name && filename__read_build_id(new_name, &bid) > 0)
- dso__set_build_id(dso, &bid);
- free(new_name);
- }
- nsinfo__mountns_exit(&nsc);
mutex_unlock(dso__lock(dso));
return dso__has_build_id(dso) ? 0 : -1;
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 02233ef0eafff2c2..db5ba9e91fba4d2d 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -847,9 +847,9 @@ int build_id_cache__remove_s(const char *sbuild_id)
return err;
}
-static int filename__read_build_id_ns(const char *filename,
- struct build_id *bid,
- struct nsinfo *nsi)
+int filename__read_build_id_ns(const char *filename,
+ struct build_id *bid,
+ struct nsinfo *nsi)
{
struct nscookie nsc;
int ret;
diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c
index e927e707abace9c6..a97fed55286aefca 100644
--- a/tools/perf/util/dsos.c
+++ b/tools/perf/util/dsos.c
@@ -4,11 +4,11 @@
#include "dso.h"
#include "util.h"
#include "vdso.h"
-#include "namespaces.h"
#include <errno.h>
+#include <linux/perf_event.h>
#include <stdlib.h>
#include <string.h>
-#include <symbol.h> // filename__read_build_id
+#include <symbol.h> // filename__read_build_id_ns
#include <unistd.h>
void dsos__init(struct dsos *dsos)
@@ -70,7 +70,6 @@ struct dsos__read_build_ids_cb_args {
static int dsos__read_build_ids_cb(struct dso *dso, void *data)
{
struct dsos__read_build_ids_cb_args *args = data;
- struct nscookie nsc;
struct build_id bid = { .size = 0, };
if (args->with_hits && !dso__hit(dso) && !dso__is_vdso(dso))
@@ -79,20 +78,10 @@ static int dsos__read_build_ids_cb(struct dso *dso, void *data)
args->have_build_id = true;
return 0;
}
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
- if (filename__read_build_id(dso__long_name(dso), &bid) > 0) {
+ if (filename__read_build_id_ns(dso__long_name(dso), &bid, dso__nsinfo(dso)) > 0) {
dso__set_build_id(dso, &bid);
args->have_build_id = true;
- } else if (errno == ENOENT && dso__nsinfo(dso)) {
- char *new_name = dso__filename_with_chroot(dso, dso__long_name(dso));
-
- if (new_name && filename__read_build_id(new_name, &bid) > 0) {
- dso__set_build_id(dso, &bid);
- args->have_build_id = true;
- }
- free(new_name);
}
- nsinfo__mountns_exit(&nsc);
return 0;
}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index d0bac824c79c8283..9d86d2eb694298f7 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -28,6 +28,7 @@
struct dso;
struct map;
struct maps;
+struct nsinfo;
struct option;
struct build_id;
struct perf_env;
@@ -221,6 +222,8 @@ struct symbol *dso__next_symbol(struct symbol *sym);
enum dso_type dso__type_fd(int fd);
int filename__read_build_id(const char *filename, struct build_id *id);
+int filename__read_build_id_ns(const char *filename, struct build_id *bid,
+ struct nsinfo *nsi);
int sysfs__read_build_id(const char *filename, struct build_id *bid);
int modules__parse(const char *filename, void *arg,
int (*process_module)(void *arg, const char *name,
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 09/11] perf tools: Remove redundant chroot handling
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (7 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 08/11] perf tools: Export filename__read_build_id_ns() Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 10/11] perf tools: Only enter mount namespace if necessary Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 11/11] perf tools: Scope mount namespace entry in dso__load() Namhyung Kim
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Now that nsinfo__mountns_enter() enters the target process's chroot
alongside its mount namespace, callers inside a mountns context no
longer need to manually prepend the target's root directory path via
dso__filename_with_chroot() or filename_with_chroot().
Remove dso__filename_with_chroot() and filename_with_chroot() and use
the DSO path directly within the mountns scope.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/dso.c | 29 ++---------------------------
tools/perf/util/dso.h | 2 --
tools/perf/util/symbol.c | 8 --------
tools/perf/util/util.c | 31 -------------------------------
tools/perf/util/util.h | 2 --
5 files changed, 2 insertions(+), 70 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index ce98613868e7d371..4331b48505b2b10e 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -585,11 +585,6 @@ static int do_open(char *name) EXCLUSIVE_LOCKS_REQUIRED(_dso__data_open_lock)
return -1;
}
-char *dso__filename_with_chroot(const struct dso *dso, const char *filename)
-{
- return filename_with_chroot(nsinfo__pid(dso__nsinfo_const(dso)), filename);
-}
-
char *dso__get_filename(struct dso *dso, const char *root_dir, bool *decomp,
enum dso_binary_type type)
{
@@ -603,28 +598,8 @@ char *dso__get_filename(struct dso *dso, const char *root_dir, bool *decomp,
if (dso__read_binary_type_filename(dso, type, root_dir, name, PATH_MAX))
goto out;
- if (!is_regular_file(name)) {
- struct stat st;
- char *new_name;
-
- /*
- * errno only reflects the failure reason when stat() itself
- * failed: a successful stat() on a non-regular file (e.g. a
- * directory) leaves a stale errno, which a previous failed
- * iteration of the try_to_open_dso() fallback loop may have
- * set to ENOENT.
- */
- if (stat(name, &st) == 0 || errno != ENOENT ||
- dso__nsinfo(dso) == NULL)
- goto out;
-
- new_name = dso__filename_with_chroot(dso, name);
- if (!new_name)
- goto out;
-
- free(name);
- name = new_name;
- }
+ if (!is_regular_file(name))
+ goto out;
if (dso__needs_decompress(dso)) {
char newpath[KMOD_DECOMP_LEN];
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 3329f417810c25a4..e53c83c23c578918 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -826,8 +826,6 @@ static inline bool dso__has_symbols(const struct dso *dso)
return !RB_EMPTY_ROOT(&RC_CHK_ACCESS(dso)->symbols.rb_root);
}
-char *dso__filename_with_chroot(const struct dso *dso, const char *filename);
-
bool dso__sorted_by_name(const struct dso *dso);
void dso__set_sorted_by_name(struct dso *dso);
void dso__sort_by_name(struct dso *dso);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b61a574ce9a30721..807e433c770812a4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1902,14 +1902,6 @@ int dso__load(struct dso *dso, struct map *map)
nsinfo__mountns_exit(&nsc);
is_reg = is_regular_file(name);
- if (!is_reg && errno == ENOENT && dso__nsinfo(dso)) {
- char *new_name = dso__filename_with_chroot(dso, name);
- if (new_name) {
- is_reg = is_regular_file(new_name);
- strlcpy(name, new_name, PATH_MAX);
- free(new_name);
- }
- }
#ifdef HAVE_LIBBFD_SUPPORT
if (is_reg)
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 8f7cd32f524dc10e..443b1d2b4748bfe4 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -451,37 +451,6 @@ void perf_debuginfod_setup(struct perf_debuginfod *di)
#endif
}
-/*
- * Return a new filename prepended with task's root directory if it's in
- * a chroot. Callers should free the returned string.
- */
-char *filename_with_chroot(int pid, const char *filename)
-{
- char buf[PATH_MAX];
- char proc_root[32];
- char *new_name = NULL;
- int ret;
-
- scnprintf(proc_root, sizeof(proc_root), "/proc/%d/root", pid);
- ret = readlink(proc_root, buf, sizeof(buf) - 1);
- if (ret <= 0)
- return NULL;
-
- /* readlink(2) does not append a null byte to buf */
- buf[ret] = '\0';
-
- if (!strcmp(buf, "/"))
- return NULL;
-
- if (strstr(buf, "(deleted)"))
- return NULL;
-
- if (asprintf(&new_name, "%s/%s", buf, filename) < 0)
- return NULL;
-
- return new_name;
-}
-
/*
* Reallocate an array *arr of size *arr_sz so that it is big enough to contain
* x elements of size msz, initializing new entries to *init_val or zero if
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index f8f1ff603c728086..b9adb58df976f29e 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -84,8 +84,6 @@ void perf_debuginfod_setup(struct perf_debuginfod *di);
const char *perf_basename(const char *path);
-char *filename_with_chroot(int pid, const char *filename);
-
int do_realloc_array_as_needed(void **arr, size_t *arr_sz, size_t x,
size_t msz, const void *init_val);
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 10/11] perf tools: Only enter mount namespace if necessary
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (8 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 09/11] perf tools: Remove redundant chroot handling Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 11/11] perf tools: Scope mount namespace entry in dso__load() Namhyung Kim
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Add dso_binary_type__needs_ns() to distinguish DSO binary types that
reside inside the target process's mount namespace and chroot from
host-side paths.
Gate mount namespace entry in dso__data_file_size() and do_open() using
dso_binary_type__needs_ns(). And remove redundant outer mountns entry
in around dso__data_get_fd().
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/capstone.c | 6 ++++--
tools/perf/util/dso.c | 12 ++++++++----
tools/perf/util/dso.h | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 46 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/capstone.c b/tools/perf/util/capstone.c
index 95881f15324aa614..9a8646ff026d92a4 100644
--- a/tools/perf/util/capstone.c
+++ b/tools/perf/util/capstone.c
@@ -653,9 +653,11 @@ int symbol__disassemble_capstone_powerpc(const char *filename __maybe_unused,
if (args->options->objdump_path)
return -1;
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ if (dso_binary_type__needs_ns(dso__binary_type(dso)))
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
fd = open(filename, O_RDONLY);
- nsinfo__mountns_exit(&nsc);
+ if (dso_binary_type__needs_ns(dso__binary_type(dso)))
+ nsinfo__mountns_exit(&nsc);
if (fd < 0)
return -1;
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 4331b48505b2b10e..50dd215f81f6737c 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -682,14 +682,15 @@ static int open_dso(struct dso *dso, struct machine *machine)
{
int fd;
struct nscookie nsc;
+ bool nsenter = dso_binary_type__needs_ns(dso__binary_type(dso));
- if (dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE) {
+ if (nsenter) {
mutex_lock(dso__lock(dso));
nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
mutex_unlock(dso__lock(dso));
}
fd = __open_dso(dso, machine);
- if (dso__binary_type(dso) != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+ if (nsenter)
nsinfo__mountns_exit(&nsc);
if (fd >= 0) {
@@ -2081,11 +2082,14 @@ static const u8 *__dso__read_symbol(struct dso *dso, const char *symfs_filename,
};
u8 *code_buf = NULL;
int saved_errno;
+ bool nsenter = dso_binary_type__needs_ns(dso__binary_type(dso));
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ if (nsenter)
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
fd = open(symfs_filename, O_RDONLY | O_CLOEXEC);
saved_errno = errno;
- nsinfo__mountns_exit(&nsc);
+ if (nsenter)
+ nsinfo__mountns_exit(&nsc);
if (fd < 0) {
errno = saved_errno;
return NULL;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index e53c83c23c578918..c4ff19f575b88a9e 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -141,6 +141,40 @@ enum dso_binary_type {
DSO_BINARY_TYPE__NOT_FOUND,
};
+static inline bool dso_binary_type__needs_ns(enum dso_binary_type type)
+{
+ switch (type) {
+ case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE:
+ case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP:
+ case DSO_BINARY_TYPE__DEBUGLINK:
+ case DSO_BINARY_TYPE__GNU_DEBUGDATA:
+ return true;
+ case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
+ case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+ case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+ case DSO_BINARY_TYPE__FEDORA_DEBUGINFO:
+ case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO:
+ case DSO_BINARY_TYPE__BUILDID_DEBUGINFO:
+ case DSO_BINARY_TYPE__GUEST_KMODULE:
+ case DSO_BINARY_TYPE__GUEST_KMODULE_COMP:
+ case DSO_BINARY_TYPE__KCORE:
+ case DSO_BINARY_TYPE__GUEST_KCORE:
+ case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
+ case DSO_BINARY_TYPE__BPF_IMAGE:
+ case DSO_BINARY_TYPE__OOL:
+ case DSO_BINARY_TYPE__NOT_FOUND:
+ default:
+ return false;
+ }
+}
+
enum dso_space_type {
DSO_SPACE__USER = 0,
DSO_SPACE__KERNEL,
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread* [PATCH v1 11/11] perf tools: Scope mount namespace entry in dso__load()
2026-10-23 7:11 [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling Namhyung Kim
` (9 preceding siblings ...)
2026-10-23 7:11 ` [PATCH v1 10/11] perf tools: Only enter mount namespace if necessary Namhyung Kim
@ 2026-10-23 7:11 ` Namhyung Kim
10 siblings, 0 replies; 12+ messages in thread
From: Namhyung Kim @ 2026-10-23 7:11 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Ian Rogers, Jiri Olsa, Adrian Hunter, James Clark,
Peter Zijlstra, Ingo Molnar, LKML, linux-perf-users
Enter the target mount namespace only for the specific binary types
that need it.
Assisted-by: LLM
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
---
tools/perf/util/symbol.c | 43 +++++++++++++++++++---------------------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 807e433c770812a4..063e9da0878e93bc 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1811,17 +1811,6 @@ int dso__load(struct dso *dso, struct map *map)
const char *map_path = dso__long_name(dso);
mutex_lock(dso__lock(dso));
- perfmap = is_perf_pid_map_name(map_path);
-
- if (perfmap) {
- if (dso__nsinfo(dso) &&
- (dso__find_perf_map(newmapname, sizeof(newmapname),
- dso__nsinfo_ptr(dso)) == 0)) {
- map_path = newmapname;
- }
- }
-
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
/* check again under the dso->lock */
if (dso__loaded(dso)) {
@@ -1845,8 +1834,16 @@ int dso__load(struct dso *dso, struct map *map)
dso__set_adjust_symbols(dso, false);
+ perfmap = is_perf_pid_map_name(map_path);
if (perfmap) {
+ if (dso__nsinfo(dso) &&
+ (dso__find_perf_map(newmapname, sizeof(newmapname),
+ dso__nsinfo_ptr(dso)) == 0)) {
+ map_path = newmapname;
+ }
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
ret = dso__load_perf_map(map_path, dso);
+ nsinfo__mountns_exit(&nsc);
dso__set_binary_type(dso, DSO_BINARY_TYPE__JAVA_JIT);
dso__set_symtab_type(dso, ret > 0
? DSO_BINARY_TYPE__JAVA_JIT
@@ -1869,7 +1866,7 @@ int dso__load(struct dso *dso, struct map *map)
struct build_id bid = { .size = 0, };
__symbol__join_symfs(name, PATH_MAX, dso__long_name(dso));
- if (filename__read_build_id(name, &bid) > 0)
+ if (filename__read_build_id_ns(name, &bid, dso__nsinfo(dso)) > 0)
dso__set_build_id(dso, &bid);
}
@@ -1882,24 +1879,25 @@ int dso__load(struct dso *dso, struct map *map)
struct symsrc *ss = &ss_[ss_pos];
bool next_slot = false;
bool is_reg;
- bool nsexit;
+ bool nsenter;
int bfdrc = -1;
int sirc = -1;
enum dso_binary_type symtab_type = binary_type_symtab[i];
- nsexit = (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE ||
- symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO);
-
if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type))
continue;
+ nsenter = dso_binary_type__needs_ns(symtab_type);
+ if (nsenter)
+ nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+
if (dso__read_binary_type_filename(dso, symtab_type,
- root_dir, name, PATH_MAX))
+ root_dir, name, PATH_MAX)) {
+ if (nsenter)
+ nsinfo__mountns_exit(&nsc);
continue;
-
- if (nsexit)
- nsinfo__mountns_exit(&nsc);
+ }
is_reg = is_regular_file(name);
@@ -1910,8 +1908,8 @@ int dso__load(struct dso *dso, struct map *map)
if (is_reg && bfdrc < 0)
sirc = symsrc__init(ss, dso, name, symtab_type);
- if (nsexit)
- nsinfo__mountns_enter(dso__nsinfo(dso), &nsc);
+ if (nsenter)
+ nsinfo__mountns_exit(&nsc);
if (bfdrc > 0) {
dso__set_symtab_type(dso, symtab_type);
@@ -1978,7 +1976,6 @@ int dso__load(struct dso *dso, struct map *map)
out:
dso__set_loaded(dso);
mutex_unlock(dso__lock(dso));
- nsinfo__mountns_exit(&nsc);
return ret;
}
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread