[PATCH v7 08/17] perf dso: Pass dso_data to close_data_fd() and dso__list_del()

From: Ian Rogers

Date: Wed Oct 21 2026 - 18:43:10 EST


close_data_fd() and dso__list_del() only operate on struct dso_data
fields, while close_first_dso() retrieves a struct dso_data * from
dso__data_open. Pass struct dso_data * directly to close_data_fd() and
dso__list_del() so close_first_dso() does not need to recover a
struct dso * from struct dso_data *.

This removes the REFCNT_CHECKING dso pointer from struct dso_data and
avoids dropping _dso__data_open_lock in dso__list_del() to call
dso__put(), preventing lock-order inversions and races during EMFILE
eviction.

Also place the DSOs created in addr_filter__resolve_syms(),
machine__process_ksymbol_register(), and dso__split_kallsyms() into a
dsos collection so dso__dsos(dso) is populated when dso__list_add()
is called.

Signed-off-by: Ian Rogers <irogers@xxxxxxxxxx>
---
tools/perf/util/auxtrace.c | 16 +++++++++++---
tools/perf/util/dso.c | 44 +++++++++++---------------------------
tools/perf/util/dso.h | 3 ---
tools/perf/util/machine.c | 14 +++++++-----
tools/perf/util/symbol.c | 4 ++++
5 files changed, 38 insertions(+), 43 deletions(-)

diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index f30f86156a3a..5fdbd77eb451 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -29,6 +29,7 @@
#include "config.h"
#include "evlist.h"
#include "dso.h"
+#include "dsos.h"
#include "map.h"
#include "pmu.h"
#include "evsel.h"
@@ -2684,7 +2685,7 @@ static int addr_filter__resolve_kernel_syms(struct addr_filter *filt)
return 0;
}

-static struct dso *load_dso(const char *name)
+static struct dso *load_dso(const char *name, struct dsos *dsos)
{
struct map *map;
struct dso *dso;
@@ -2693,6 +2694,11 @@ static struct dso *load_dso(const char *name)
if (!map)
return NULL;

+ if (dsos__add(dsos, map__dso(map))) {
+ map__put(map);
+ return NULL;
+ }
+
if (map__load(map) < 0)
pr_err("File '%s' not found or has no symbols.\n", name);

@@ -2794,6 +2800,7 @@ static int addr_filter__resolve_syms(struct addr_filter *filt)
{
u64 start, size;
struct dso *dso;
+ struct dsos dsos;
int err = 0;

if (!filt->sym_from && !filt->sym_to)
@@ -2802,9 +2809,11 @@ static int addr_filter__resolve_syms(struct addr_filter *filt)
if (!filt->filename)
return addr_filter__resolve_kernel_syms(filt);

- dso = load_dso(filt->filename);
+ dsos__init(&dsos);
+ dso = load_dso(filt->filename, &dsos);
if (!dso) {
pr_err("Failed to load symbols from: %s\n", filt->filename);
+ dsos__exit(&dsos);
return -EINVAL;
}

@@ -2831,13 +2840,14 @@ static int addr_filter__resolve_syms(struct addr_filter *filt)

err = check_end_after_start(filt, start, size);
if (err)
- return err;
+ goto put_dso;

filt->size = start + size - filt->addr;
}

put_dso:
dso__put(dso);
+ dsos__exit(&dsos);

return err;
}
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 24ff066b9878..cbc4f30e7f23 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include "dso.h"

+#include <assert.h>
#include <errno.h>
#include <stdlib.h>

@@ -544,22 +545,14 @@ static struct mutex *dso__data_open_lock(void) __returns_ctx_lock(&_dso__data_op
static void dso__list_add(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
list_add_tail(&dso__data(dso)->open_entry, &dso__data_open);
-#ifdef REFCNT_CHECKING
- dso__data(dso)->dso = dso__get(dso);
-#endif
- /* Assume the dso is part of dsos, hence the optional reference count above. */
+ /* Assume the dso is part of dsos, holding it live while on dso__data_open. */
assert(dso__dsos(dso));
dso__data_open_cnt++;
}

-static void dso__list_del(struct dso *dso) __must_hold(&_dso__data_open_lock)
+static void dso__list_del(struct dso_data *dso_data) __must_hold(&_dso__data_open_lock)
{
- list_del_init(&dso__data(dso)->open_entry);
-#ifdef REFCNT_CHECKING
- mutex_unlock(dso__data_open_lock());
- dso__put(dso__data(dso)->dso);
- mutex_lock(dso__data_open_lock());
-#endif
+ list_del_init(&dso_data->open_entry);
WARN_ONCE(dso__data_open_cnt <= 0,
"DSO data fd counter out of bounds.");
dso__data_open_cnt--;
@@ -729,13 +722,13 @@ static int open_dso(struct dso *dso, struct machine *machine)
return fd;
}

-static void close_data_fd(struct dso *dso) __must_hold(&_dso__data_open_lock)
+static void close_data_fd(struct dso_data *dso_data) __must_hold(&_dso__data_open_lock)
{
- if (dso__data(dso)->fd >= 0) {
- close(dso__data(dso)->fd);
- dso__data(dso)->fd = -1;
- dso__data(dso)->file_size = 0;
- dso__list_del(dso);
+ if (dso_data->fd >= 0) {
+ close(dso_data->fd);
+ dso_data->fd = -1;
+ dso_data->file_size = 0;
+ dso__list_del(dso_data);
}
}

@@ -748,21 +741,15 @@ static void close_data_fd(struct dso *dso) __must_hold(&_dso__data_open_lock)
*/
static void close_dso(struct dso *dso) __must_hold(&_dso__data_open_lock)
{
- close_data_fd(dso);
+ close_data_fd(dso__data(dso));
}

static void close_first_dso(void) __must_hold(&_dso__data_open_lock)
{
struct dso_data *dso_data;
- struct dso *dso;

dso_data = list_first_entry(&dso__data_open, struct dso_data, open_entry);
-#ifdef REFCNT_CHECKING
- dso = dso_data->dso;
-#else
- dso = container_of(dso_data, struct dso, data);
-#endif
- close_dso(dso);
+ close_data_fd(dso_data);
}

static rlim_t get_fd_limit(void)
@@ -1858,9 +1845,6 @@ struct dso *dso__new_id(const char *name, const struct dso_id *id)
data->fd = -1;
data->status = DSO_DATA_STATUS_UNKNOWN;
INIT_LIST_HEAD(&data->open_entry);
-#ifdef REFCNT_CHECKING
- data->dso = NULL; /* Set when on the open_entry list. */
-#endif
}
return res;
}
@@ -1918,10 +1902,6 @@ struct dso *dso__get(struct dso *dso)

void dso__put(struct dso *dso)
{
-#ifdef REFCNT_CHECKING
- if (dso && dso__data(dso) && refcount_read(&RC_CHK_ACCESS(dso)->refcnt) == 2)
- dso__data_close(dso);
-#endif
if (dso && refcount_dec_and_test(&RC_CHK_ACCESS(dso)->refcnt))
dso__delete(dso);
else
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index fa5669709eb3..d6b0a1a4dac9 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -261,9 +261,6 @@ struct dso_cache {
struct dso_data {
struct rb_root cache;
struct list_head open_entry;
-#ifdef REFCNT_CHECKING
- struct dso *dso;
-#endif
int fd;
int status;
u32 status_seen;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 1d9d3bf57720..4507f82548e3 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -771,6 +771,15 @@ static int machine__process_ksymbol_register(struct machine *machine,
dso__set_loaded(dso);
}

+ if (is_bpf_image(event->ksymbol.name)) {
+ dso__set_binary_type(dso, DSO_BINARY_TYPE__BPF_IMAGE);
+ dso__set_long_name(dso, "", false);
+ }
+
+ err = dsos__add(&machine->dsos, dso);
+ if (err)
+ goto out;
+
map__set_start(map, event->ksymbol.addr);
map__set_end(map, map__start(map) + event->ksymbol.len);
err = maps__fixup_overlap_and_insert(machine__kernel_maps(machine), map);
@@ -780,11 +789,6 @@ static int machine__process_ksymbol_register(struct machine *machine,
}

dso__set_loaded(dso);
-
- if (is_bpf_image(event->ksymbol.name)) {
- dso__set_binary_type(dso, DSO_BINARY_TYPE__BPF_IMAGE);
- dso__set_long_name(dso, "", false);
- }
} else {
dso = dso__get(map__dso(map));
}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index b61a574ce9a3..686fc48841c6 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1090,6 +1090,10 @@ static int maps__split_kallsyms(struct maps *kmaps, struct dso *dso, u64 delta,
dso__set_symtab_type(ndso, DSO_BINARY_TYPE__KALLSYMS);
}
dso__set_loaded(ndso);
+ if (dsos__add(&machine->dsos, ndso)) {
+ dso__put(ndso);
+ return -1;
+ }

curr_map = map__new2(pos->start, ndso);
if (curr_map == NULL) {
--
2.56.0.218.g1ec904b173-goog