Files
linux-stable-mirror/tools/perf/util/maps.h
T
Ian RogersandArnaldo Carvalho de Melo 75a4888b70 perf maps: Add maps__mutate_mapping
During kernel ELF symbol parsing (dso__process_kernel_symbol), proc
kallsyms image loading (dso__load_kernel_sym,
dso__load_guest_kernel_sym), and dynamic kernel memory map alignment
updates (machine__update_kernel_mmap), the loader directly modifies live
virtual address boundary keys fields on map objects.

If these boundaries are mutated while the map pointer actively resides
inside the parent maps cache array list (kmaps) outside of any lock
closure, an unsafe concurrent window is exposed where parallel worker
lookup threads (e.g., inside perf top) can mistakenly assume the cache
remains sorted based on stale parameters, executing binary search
queries (bsearch) across an unsorted range and triggering lookup
failures.

Fix this by introducing maps__mutate_mapping() that explicitly acquires
the parent maps write semaphore lock, executes an incoming mutation
callback block to perform the field updates under lock protection, and
invalidates the sorted tracking flags prior to releasing the write lock.

This guarantees synchronization invariants, closing the concurrent
lookup race window. The adjacent module alignment pass inside
machine__create_kernel_maps() is safely preserved as a high-performance
lockless pass, as its invocation lifecycle bounds remain strictly
single-threaded by contract during session initialization construction.

To safely support this unconditional down_write write lock mutator
without recursive read-to-write self-deadlock upgrades during lazy
symbol loading, we introduce a public maps__load_maps() API.

It copies map pointers under a brief read lock and force-loads all
modules locklessly outside the lock. Callers (such as perf inject) must
pre-load all kernel symbol maps up front at startup using
maps__load_maps(), completely bypassing dynamic runtime mutations.

Fixes: 39b12f7812 ("perf tools: Make it possible to read object code from vmlinux")

Assisted-by: Antigravity:gemini-3.1-pro
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: James Clark <james.clark@linaro.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Gabriel Marin <gmx@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2026-06-15 09:01:27 -03:00

89 lines
2.7 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_MAPS_H
#define __PERF_MAPS_H
#include <linux/refcount.h>
#include <stdio.h>
#include <stdbool.h>
#include <linux/types.h>
struct ref_reloc_sym;
struct machine;
struct map;
struct maps;
#define KMAP_NAME_LEN 256
struct kmap {
struct ref_reloc_sym *ref_reloc_sym;
struct maps *kmaps;
char name[KMAP_NAME_LEN];
};
struct maps *maps__new(struct machine *machine);
bool maps__empty(struct maps *maps);
int maps__copy_from(struct maps *maps, struct maps *parent);
struct maps *maps__get(struct maps *maps);
void maps__put(struct maps *maps);
static inline void __maps__zput(struct maps **map)
{
maps__put(*map);
*map = NULL;
}
#define maps__zput(map) __maps__zput(&map)
bool maps__equal(struct maps *a, struct maps *b);
/* Iterate over map calling cb for each entry. */
int maps__for_each_map(struct maps *maps, int (*cb)(struct map *map, void *data), void *data);
/* Iterate over map removing an entry if cb returns true. */
void maps__remove_maps(struct maps *maps, bool (*cb)(struct map *map, void *data), void *data);
struct machine *maps__machine(const struct maps *maps);
unsigned int maps__nr_maps(const struct maps *maps); /* Test only. */
refcount_t *maps__refcnt(struct maps *maps); /* Test only. */
#ifdef HAVE_LIBUNWIND_SUPPORT
void *maps__addr_space(const struct maps *maps);
void maps__set_addr_space(struct maps *maps, void *addr_space);
uint16_t maps__e_machine(const struct maps *maps);
void maps__set_e_machine(struct maps *maps, uint16_t e_machine);
#endif
#ifdef HAVE_LIBDW_SUPPORT
void *maps__libdw_addr_space_dwfl(const struct maps *maps);
void maps__set_libdw_addr_space_dwfl(struct maps *maps, void *dwfl);
#endif
size_t maps__fprintf(struct maps *maps, FILE *fp);
int maps__load_maps(struct maps *maps);
int maps__insert(struct maps *maps, struct map *map);
void maps__remove(struct maps *maps, struct map *map);
int maps__mutate_mapping(struct maps *maps, struct map *map,
int (*mutate_cb)(struct map *map, void *data), void *data);
struct map *maps__find(struct maps *maps, u64 addr);
struct symbol *maps__find_symbol(struct maps *maps, u64 addr, struct map **mapp);
struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name, struct map **mapp);
struct addr_map_symbol;
int maps__find_ams(struct maps *maps, struct addr_map_symbol *ams);
int maps__fixup_overlap_and_insert(struct maps *maps, struct map *new);
struct map *maps__find_by_name(struct maps *maps, const char *name);
struct map *maps__find_next_entry(struct maps *maps, struct map *map);
int maps__merge_in(struct maps *kmaps, struct map *new_map);
void maps__fixup_end(struct maps *maps);
void maps__load_first(struct maps *maps);
#endif // __PERF_MAPS_H