* [PATCH v1 00/11] perf tools: Enhance namespace and chroot handling
@ 2026-10-23 7:11 Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 01/11] perf tools: Add HEADER_BOOT_ID feature Namhyung Kim
` (10 more replies)
0 siblings, 11 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
Hello,
This patchset improves namespace and chroot handling with regards to
file access. Basically it reads the information from the proc file
system but it's only meaningful when you run the perf command on the
same machine and the target thread is still alive.
Also some file types don't need to namespace and chroot translation
because they are located in a global directory and expected to be in
the same namespace and root directory with the perf command itself.
Thanks,
Namhyung
Namhyung Kim (11):
perf tools: Add HEADER_BOOT_ID feature
perf tools: Add perf_env__is_same_boot()
perf tools: Add nsinfo__new_thread() to track tid
perf tools: Defer /proc lookup until nsinfo is used
perf tools: Verify thread status and mount namespace inode
perf tools: Check thread liveness when accessing /proc
perf tools: Handle chroot inside nsinfo__mountns_enter()
perf tools: Export filename__read_build_id_ns()
perf tools: Remove redundant chroot handling
perf tools: Only enter mount namespace if necessary
perf tools: Scope mount namespace entry in dso__load()
.../Documentation/perf.data-file-format.txt | 5 +
tools/perf/builtin-inject.c | 13 +-
tools/perf/builtin-trace.c | 2 +-
tools/perf/util/build-id.c | 6 +-
tools/perf/util/capstone.c | 6 +-
tools/perf/util/dso.c | 41 +---
tools/perf/util/dso.h | 36 ++-
tools/perf/util/dsos.c | 17 +-
tools/perf/util/env.c | 65 +++++
tools/perf/util/env.h | 3 +
tools/perf/util/header.c | 28 +++
tools/perf/util/header.h | 1 +
tools/perf/util/machine.c | 12 +-
tools/perf/util/namespaces.c | 230 +++++++++++++++---
tools/perf/util/namespaces.h | 12 +
tools/perf/util/symbol.c | 51 ++--
tools/perf/util/symbol.h | 3 +
tools/perf/util/thread.c | 39 ++-
tools/perf/util/thread.h | 7 +-
tools/perf/util/util.c | 31 ---
tools/perf/util/util.h | 2 -
21 files changed, 436 insertions(+), 174 deletions(-)
--
2.56.0.257.gf7497c7d27-goog
^ permalink raw reply [flat|nested] 12+ messages in thread
* [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
end of thread, other threads:[~2026-10-23 7:11 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH v1 03/11] perf tools: Add nsinfo__new_thread() to track tid Namhyung Kim
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 ` [PATCH v1 05/11] perf tools: Verify thread status and mount namespace inode Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 06/11] perf tools: Check thread liveness when accessing /proc Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 07/11] perf tools: Handle chroot inside nsinfo__mountns_enter() Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 08/11] perf tools: Export filename__read_build_id_ns() Namhyung Kim
2026-10-23 7:11 ` [PATCH v1 09/11] perf tools: Remove redundant chroot handling 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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®