[PATCH v6 0/3] kallsyms: Accelerate symbol name lookups by ~7x

From: Jim Cromie

Date: Sat Sep 26 2026 - 15:40:20 EST


As measured by kernel/kallsyms_selftest across ~184k symbols,
kallsyms_lookup_names() binary search takes ~6.1 us per lookup due to
two inner-loop costs:

0. Candidate symbols are fully decompressed into a 512-byte stack buffer
before calling strcmp(), even though non-matching steps could choose on the
first differing character (0..N-1).

1. Probes scan sequentially from 256:1 markers in kallsyms_names[],
decoding an average of 127.5 symbols per probe (~2,170 hops across a
17-step search).

This 3-patch series addresses both:

0. Patch 1 introduces kallsyms_strcmp_symbol() to compare ASCII queries
against compressed tokens on the fly, bailing out on first mismatch.
Drops the 512-byte stack buffer and saves ~530 ns.

1. Patch 2 increases marker density from 256:1 to 16:1, cutting average
scan distance from 127.5 to 7.5 hops and dropping lookup latency from
6,102 ns to 866 ns for +42.2 KiB of .rodata.

2. Patch 3 inlines and unrolls get_symbol_seq() 24-bit reconstruction.

Results (kernel/kallsyms_selftest across ~184k symbols):
- Baseline (256:1): 6,102 ns
- Patch 1 (strcmp): 5,572 ns (-530 ns)
- Patch 2 (16:1): 866 ns (7.0x faster)
- Memory: +42.2 KiB .rodata, 0 bytes dynamic RAM

What's Unchanged:
- Symbol table layout in address order remains identical.
- Streaming decompression for /proc/kallsyms and sprint_symbol() is
untouched.

Signed-off-by: Jim Cromie <jim.cromie@xxxxxxxxx>
---
Changes in v6:
- In patch 1, clarify token matching mechanics and early exit on char
0..N-1; cite CONFIG_KALLSYMS_SELFTEST across all ~184k symbols for the
~530 ns measurement (addresses BPF CI review).
- In patch 1, drop 0-2 list and fix repeated word typo ("the the").
- In patch 2, recast commit body around kallsyms storage trade-offs and
drop hunk-by-hunk numbered list (addresses BPF CI review).
- In patch 2, drop 0.002% percentage claim and state absolute .rodata
cost (+42 KiB for ~184k symbols).
- In patch 2, drop ephemeral benchmark comment from
kernel/kallsyms_internal.h (addresses BPF CI review).
- Link to v5: https://lore.kernel.org/r/20260925-ksyms-tune-v5-0-1f75ad0c321c@xxxxxxxxx

Changes in v5:
- Dismiss dynamic 1:1 batch table as unnecessary:
In v2-v4, a dynamic u32 lookup index allocated in transient RAM was
explored. While dynamic 1:1 breaks even against baseline 256:1 after
~1,200 queries, comparing dynamic 1:1 against static 16:1 markers
dismisses the dynamic approach entirely:
* Static 16:1 markers achieve 866 ns, capturing 97.4% of the maximum
latency savings of a 1:1 table (an 85.8% reduction from baseline).
* Dynamic 1:1 gains only an incremental 138 ns (the remaining 2.6%),
while paying ~6,419 us in allocation setup and synchronize_rcu()
teardown.
* Amortizing 6,419 us at 138 ns saved requires 46,514 queries just to
break even against 16:1 markers. For any workload under 46k
queries, dynamic allocation is slower overall.
* Drop kallsyms_lookup_batch_start/end APIs, mutexes, refcounts,
transient kvmalloc RAM allocations, and RCU synchronization.
- Drop lib/test_kallsyms_perf benchmark module and
CONFIG_TEST_KALLSYMS_PERF; existing in-tree CONFIG_KALLSYMS_SELFTEST
already benchmarks standard kallsyms_lookup_name() without adding
unmaintained test files in lib/.
- In patch 2, increase marker density to 16:1 via
KALLSYMS_MARKER_SHIFT 4 shared between scripts/kallsyms.c and
kernel/kallsyms_internal.h, and drop all references to dynamic tables
from the patch body.
- Link to v4: https://lore.kernel.org/r/20260922-ksyms-tune-v4-0-92acea84b911@xxxxxxxxx

Changes in v4:
- In patch 1, ignore early boot invocations in param_set_trigger() when
system_state < SYSTEM_RUNNING to prevent NULL pointer dereference in
ktime_get_ns() prior to timekeeping_init() (addresses Sashiko review).
- In patch 1, prevent sysfs TOCTOU divide-by-zero panic: reject
num_iters == 0 in param setter, snapshot iters locally via READ_ONCE,
and serialize runs with bench_lock mutex (addresses Sashiko review).
- In patch 1, eliminate multi-second boot stall: add run_on_boot
parameter (default false) so late_initcall only runs benchmark when
explicitly requested (addresses Sashiko review).
- In patch 1, chunk lookup loops in 4096-iter batches with
cond_resched() outside the timing bracket to prevent preemption sleep
time from inflating reported latency (addresses Sashiko review).
- In patch 3, annotate dyn_kallsyms_offsets declaration with __rcu to
satisfy sparse type checking and prevent address-space warnings across
rcu_assign_pointer() and rcu_dereference() (addresses Sashiko review).
- In patch 3, use rcu_replace_pointer() with lockdep_is_held() during
batch teardown to atomically read and clear the pointer while
satisfying sparse address-space constraints (addresses Sashiko
review).
- Link to v3: https://lore.kernel.org/r/20260922-ksyms-tune-v3-0-681a34ea05d9@xxxxxxxxx

Changes in v3:
- Reorder series: place on-the-fly token matching ahead of marker
density optimization, establishing an active proof of incremental
performance deltas across all steps (addresses David Laight review).
- Add inlined and unrolled get_symbol_seq() 24-bit sequence index
reconstruction into direct byte shifts (addresses David Laight
review).
- In test_kallsyms_perf, configure as a built-in test (bool) rather
than a module (tristate) and drop kallsyms iterator EXPORT_SYMBOL_GPL
exports to avoid exposing internal kernel symbol data (addresses
Sashiko review).
- In patch 1, optimize kallsyms_strcmp_symbol() by dropping
skipped_first tracking and checking len at the bottom of the token
loop (addresses David Laight review).
- Drop 'default m' from lib/Kconfig.debug.
- Fix soft lockup risks by adding cond_resched() every 16k iterations in
test_kallsyms_perf loops.
- Replace direct 64-bit integer divisions with div_u64() to fix 32-bit
builds.
- Guard against divide-by-zero when num_iters=0.
- Replace tcp_v4_rcv with panic in hit_symbols to prevent failures wo
CONFIG_INET.
- Move David Laight to series-wide Cc on cover letter, dropping trailer
from patch 3.
- Link to v2: https://lore.kernel.org/r/20260922-ksyms-tune-v2-0-a333ee31eac7@xxxxxxxxx

Changes in v2:
- Replaced static build-time 3-byte offset table with a dynamic u32
index bracketed by kallsyms_lookup_batch_start() and
kallsyms_lookup_batch_end().
- Dropped .rodata image footprint addition from +573 KiB to 0 KiB,
addressing Kees Cook's memory footprint objection.
- Native u32 loads in transient RAM eliminate 24-bit big-endian shifts
and unaligned loads, addressing David Laight's endianness critique.
- Direct O(1) table indexing provides 0 hops for all symbol lookups
without remainder logic or odd/even branching.
- Restored scripts/kallsyms.c and kernel/kallsyms_internal.h to pristine
state, leaving legacy kallsyms_markers[] as safety fallback.
- Rebased out Lorenzo Stoakes' kbuild series; this series is now
completely decoupled and applies cleanly directly onto mainline.
- Link to v1: https://lore.kernel.org/r/20260919-ksyms-tune-v1-0-d85c97da1a32@xxxxxxxxx

---
Jim Cromie (3):
kallsyms: Match compressed tokens on the fly during binary search
kallsyms: Increase marker density to 16:1 to accelerate lookups
kallsyms: Unroll 24-bit sequence reconstruction in get_symbol_seq()

kernel/kallsyms.c | 111 +++++++++++++++++++++++++++------------------
kernel/kallsyms_internal.h | 11 +++++
scripts/kallsyms.c | 14 ++++--
3 files changed, 86 insertions(+), 50 deletions(-)
---
base-commit: 93f51579e7df248780214094418f205253383cc5
change-id: 20260919-ksyms-tune-e22a42d8a31a

Best regards,
--
Jim Cromie <jim.cromie@xxxxxxxxx>