All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next 00/21] bpf: Various arm64 BPF JIT improvements
@ 2026-10-27 18:24 Daniel Borkmann
  2026-10-27 18:24 ` [PATCH bpf-next 01/21] bpf, arm64: Free the offset table of a subprog in bpf_jit_free Daniel Borkmann
                   ` (20 more replies)
  0 siblings, 21 replies; 31+ messages in thread
From: Daniel Borkmann @ 2026-10-27 18:24 UTC (permalink / raw)
  To: puranjay; +Cc: memxor, ast, bpf

This series changes the arm64 BPF JIT to emit shorter and faster native
code for the instruction patterns that LLVM-compiled programs hit in the
real world. Every change stays within the lowering of a single BPF
instruction. This is a similar effort as done recently for x86-64 BPF
JIT [0].

On arm64 under vmtest.sh (qemu TCG), test_verifier, test_maps, jit_insns
and jit_insns_blinded pass in full, and test_progs and its cpuv4 and
no_alu32 flavors only fail tests that fail without the series as well
or only under TCG. The encodings were checked against Arm's machine-readable
A64 ISA XML: every A64_* macro and directly called encoder, run with
boundary and invalid operands (39028 cases), gives the encoding and
fields the Arm ARM expects or AARCH64_BREAK_FAULT for what it cannot
encode.

To see what the series does to code that is actually deployed, the BPF
objects of Cilium (7c11a349512b), Tetragon (3f20b90ee580), Katran
(2cd783e43a07) and the sched_ext schedulers (scx at fead94e15184 plus
tools/sched_ext) were built with clang 20 for arm64, and every program
of them that loads (817, another 121 fail on both kernels) was loaded
with veristat in the arm64 VM of vmtest.sh, on a kernel before and one
after this series. The size is jited_prog_len, the cycles and uops are
those of the llvm-mca 20 models of the Neoverse N1 (Graviton2, Ampere
Altra), N2 (Cobalt 100), V1 (Graviton3) and V2 (Graviton4, Grace, Axion)
over the JITed images as straight-line code, without the helper calls,
which llvm-mca cannot model, and the LSE atomics, which the models have
no scheduling information for. This is a static model without the path
taken, branch prediction or cache misses, but both sides get the same
program, so the delta is what the series rewrote. All numbers are
averaged over two boots of each kernel, as a map address with a zero
16-bit halfword takes one move less.

Project    Progs     Size  N1 cycles  N2 cycles  V1 cycles  V2 cycles  N1 uOps  N2 uOps  V1 uOps  V2 uOps
---------- -----  -------  ---------  ---------  ---------  ---------  -------  -------  -------  -------
cilium       160   -3.37%     -7.69%    -12.10%     -6.81%     -9.58%   -3.10%   -3.47%   -3.11%   -3.11%
katran         5   -6.41%     -6.78%     -8.54%     -7.48%     -0.18%   -5.62%   -7.48%   -5.65%   -5.64%
scx          391   -6.67%     -8.43%     -5.62%     -5.54%     -4.19%   -5.81%   -4.39%   -5.93%   -5.90%
tetragon     261   -2.62%     -3.19%     -3.76%     -3.79%     -2.13%   -2.54%   -2.61%   -2.54%   -2.54%
all          817   -3.22%     -4.70%     -5.54%     -4.58%     -4.16%   -3.06%   -3.00%   -3.07%   -3.07%

Cilium gains the most cycles from its 417 divisions by NSEC_PER_SEC in
89 programs, which come from the monotonic clock helper that conntrack,
NAT and the load balancer run for every entry: all four models put a
64-bit udiv at 20 cycles against 11 to 13 for the magic number sequence,
and the N2 and V2 models do not pipeline the divider at all. The
schedulers have 78 divisions in 35 programs, mostly by 100 and 1000000.
The rest of the cycles and most of the uops come from the branches:
38341 compares against zero and their branch become a cbz or cbnz, and
6173 sign and bit tests a tbz or tbnz, a uop and a cycle each. Then 2698
stores of zero take the zero register, 1171 loads and stores the
unscaled form, 707 xors with all ones become a mvn, 316 multiplications
shifts and adds, and 222 adds, subs and compares take an immediate
shifted by 12 in place of a move. Katran stays flat on V2, whose model
dispatches 16 wide.

For a feel of the native code, the tail call of Katran's xdp_root
program, from the bounds checks to the test of the program slot.

Before:

  3c:    5280088a     mov    w10, #68
  40:    b86a682a     ldr    w10, [x1, x10]
  44:    2a0203e2     mov    w2, w2
  48:    6b0a005f     cmp    w2, w10
  4c:    54000222     b.hs   #68
  50:    5280042a     mov    w10, #33
  54:    f940034c     ldr    x12, [x26]
  58:    eb0a019f     cmp    x12, x10
  5c:    540001a2     b.hs   #52
  60:    9100058c     add    x12, x12, #1
  64:    5280440a     mov    w10, #544
  68:    8b0a002a     add    x10, x1, x10
  6c:    d37df04b     lsl    x11, x2, #3
  70:    f86b694b     ldr    x11, [x10, x11]
  74:    b40000eb     cbz    x11, #28

After:

  3c:    b940442a     ldr    w10, [x1, #68]
  40:    6b0a005f     cmp    w2, w10
  44:    540001c2     b.hs   #56
  48:    f940034c     ldr    x12, [x26]
  4c:    f100859f     cmp    x12, #33
  50:    54000162     b.hs   #44
  54:    9100058c     add    x12, x12, #1
  58:    9108802a     add    x10, x1, #544
  5c:    d37d7c4b     ubfiz  x11, x2, #3, #32
  60:    f86b694b     ldr    x11, [x10, x11]
  64:    b40000cb     cbz    x11, #24

Of the 817 programs, 715 get smaller, and 640, 601, 587 and 467 faster
on N1, N2, V1 and V2. The best cases:

  Impact     Largest decrease                                    Largest relative decrease
  ---------  --------------------------------------------------  ------------------------------------
  size       generic_sleepable_preload    30192 > 28292  -6.29%  pandemonium_enable 280 > 224 -20.00%
  N1 cycles  cil_to_netdev                  1331 > 1172 -11.95%  flash_select_cpu     73 > 53 -27.40%
  N2 cycles  tail_handle_ipv6_from_netdev    1070 > 828 -22.62%  flash_enqueue      202 > 118 -41.58%
  V1 cycles  generic_sleepable_preload      1118 > 1000 -10.60%  flash_stopping       65 > 49 -24.62%
  V2 cycles  tail_handle_ipv4_from_netdev     713 > 495 -30.58%  flash_enqueue       187 > 85 -54.55%

The largest gains in cycles are in Cilium's bpf_host.o, whose programs
above hold 14 to 21 of the divisions by NSEC_PER_SEC each, and in
Tetragon's generic_sleepable_preload, which turns 451 of its 667
conditional branches into a cbz, cbnz, tbz or tbnz. The scx_flash
programs gain the most relative to their length, from their divisions by
100.

No project gets bigger or slower on any model. The worst cases per
program, which for the cycles are the same in every pairing of a boot
before with one after the series:

  Impact     Worse  Largest increase                      Largest relative increase
  ---------  -----  ------------------------------------  ----------------------------------------
  size           3  flash_select_cpu  616 > 640  +3.90%   the same
  N1 cycles      5  rusty_stopping    133 > 135  +1.50%   pandemonium_cpu_offline   17 > 18  +5.88%
  N2 cycles     15  bpfland_stopping   47 > 50   +6.38%   the same
  V1 cycles     16  lavd_running      746 > 751  +0.67%   scx_pmu_switch_tc         76 > 80  +5.26%
  V2 cycles     30  fcg_runnable      114 > 119  +4.39%   mitosis_set_cpumask       53 > 57  +7.55%

Of the three programs that grow, flash_select_cpu and flash_enqueue of
scx_flash do so by 24 and 16 bytes for their divisions by 100, and
simple_enable only in one pairing of boots. None of the 51 programs that
some model rates slower has more instructions; nearly all their changes
are compares and branches that became a cbz or cbnz, which the model
then issues in another order. The uops grow in 3 programs, 7 on N2, by
at most 4.12%.

The rewrites on their own, in llvm-mca for one instance of each sequence
as the JIT emits it, in a single pass with the pipeline fill included.
An arena access at an immediate offset keeps its cycles, the add of the
offset now off the path of the offset register, at a uop more on N2.

  cycles, uops                N1          N2          V1          V2
  r2 /= 1000000000            25>13 3>7   25>11 3>7   25>11 3>7   25>11 3>7
  r3 /= 100                   24>13 2>7   24>11 2>7   24>11 2>7   24>11 2>7
  if r6 == 0 goto               5>4 2>1     5>4 2>1     5>4 2>1     5>4 2>1
  if w0 s< 0 goto               5>4 2>1     5>4 2>1     5>4 2>1     5>4 2>1
  w2 = *(u32 *)(r8 + 6)         8>7 2>1     8>7 3>1     8>7 2>1     8>7 2>1
  arena r0 = *(u64 *)(r9 + 0)   8>7 2>1     8>7 2>2     8>7 2>1     8>7 2>1
  *(u64 *)(r1 + 0) = 0          5>4 3>2     5>4 3>2     5>4 3>2     4>4 3>2
  w1 ^= -1                      5>4 2>1     5>4 2>1     5>4 2>1     5>4 2>1
  r0 *= 24                      8>5 2>2     6>5 2>2     6>5 2>2     6>5 2>2
  w1 *= 65537                   7>5 3>1     7>5 3>1     7>5 3>1     7>5 3>1
  if w2 > 0xffff goto           6>5 3>2     6>5 3>2     6>5 3>2     6>5 3>2
  tail call slot of w3          9>8 4>3     9>8 5>4     9>8 4>3     9>8 4>3

Thanks,
Daniel

  [0] https://lore.kernel.org/bpf/20261013171230.523677-1-daniel@iogearbox.net/

Daniel Borkmann (21):
  bpf, arm64: Free the offset table of a subprog in bpf_jit_free
  bpf, arm64: Free the private stack when a pass over the image fails
  bpf, arm64: Emit ISB SY in the speculation barriers
  arm64, insn: Reject sign extending loads of 64 bits
  bpf, arm64: Keep the ll/sc atomics address apart from the old value
  arm64, insn: Add the LDUR/STUR, UMULH/SMULH and TBZ/TBNZ encodings
  bpf: Move the division magic number computation out of the x86 JIT
  bpf, arm64: Consolidate the load and store emission into a helper
  bpf, arm64: Use the unscaled forms for negative and unaligned offsets
  bpf, arm64: Store zero immediates from the zero register
  bpf, arm64: Zero extend the arena offset in register offset accesses
  bpf, arm64: Adjust compares with unencodable immediates by one
  bpf, arm64: Materialize immediates with fewer instructions
  bpf, arm64: Shorten the tail call sequence
  bpf, arm64: Divide by constants via multiplication with a magic number
  bpf, arm64: Use a move or mvn for the and and xor with all ones
  bpf, arm64: Use cbz, cbnz, tbz and tbnz for zero, sign and bit tests
  bpf, arm64: Multiply by constants with shifts, adds and subs
  bpf, arm64: Split 24-bit immediates of add, sub and equality compares
  selftests/bpf: Let the arm64 private stack check skip the BTI pad
  selftests/bpf: Extend the JIT instruction tests for arm64 encodings

 arch/arm64/include/asm/insn.h                 |   33 +-
 arch/arm64/lib/insn.c                         |  147 +++
 arch/arm64/net/bpf_jit.h                      |   38 +-
 arch/arm64/net/bpf_jit_comp.c                 | 1018 +++++++++++++----
 arch/x86/net/bpf_jit_comp.c                   |   97 --
 include/linux/filter.h                        |   11 +
 kernel/bpf/core.c                             |   93 ++
 .../selftests/bpf/prog_tests/jit_insns.c      |  418 ++++++-
 .../selftests/bpf/progs/jit_insns_arena.c     |  381 +++++-
 .../selftests/bpf/progs/verifier_ldsx.c       |   24 +-
 .../bpf/progs/verifier_private_stack.c        |    1 +
 11 files changed, 1865 insertions(+), 396 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2026-10-29 11:43 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 18:24 [PATCH bpf-next 00/21] bpf: Various arm64 BPF JIT improvements Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 01/21] bpf, arm64: Free the offset table of a subprog in bpf_jit_free Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 02/21] bpf, arm64: Free the private stack when a pass over the image fails Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 03/21] bpf, arm64: Emit ISB SY in the speculation barriers Daniel Borkmann
2026-10-28 13:29   ` Luis Gerhorst
2026-10-28 13:38     ` Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 04/21] arm64, insn: Reject sign extending loads of 64 bits Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 05/21] bpf, arm64: Keep the ll/sc atomics address apart from the old value Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 06/21] arm64, insn: Add the LDUR/STUR, UMULH/SMULH and TBZ/TBNZ encodings Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 07/21] bpf: Move the division magic number computation out of the x86 JIT Daniel Borkmann
2026-10-28 18:38   ` Alexei Starovoitov
2026-10-27 18:24 ` [PATCH bpf-next 08/21] bpf, arm64: Consolidate the load and store emission into a helper Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 09/21] bpf, arm64: Use the unscaled forms for negative and unaligned offsets Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 10/21] bpf, arm64: Store zero immediates from the zero register Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 11/21] bpf, arm64: Zero extend the arena offset in register offset accesses Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 12/21] bpf, arm64: Adjust compares with unencodable immediates by one Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 13/21] bpf, arm64: Materialize immediates with fewer instructions Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 14/21] bpf, arm64: Shorten the tail call sequence Daniel Borkmann
2026-10-29 11:43   ` Xu Kuohai
2026-10-27 18:24 ` [PATCH bpf-next 15/21] bpf, arm64: Divide by constants via multiplication with a magic number Daniel Borkmann
2026-10-27 19:08   ` bot+bpf-ci
2026-10-27 18:24 ` [PATCH bpf-next 16/21] bpf, arm64: Use a move or mvn for the and and xor with all ones Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 17/21] bpf, arm64: Use cbz, cbnz, tbz and tbnz for zero, sign and bit tests Daniel Borkmann
2026-10-28 18:39   ` Alexei Starovoitov
2026-10-27 18:24 ` [PATCH bpf-next 18/21] bpf, arm64: Multiply by constants with shifts, adds and subs Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 19/21] bpf, arm64: Split 24-bit immediates of add, sub and equality compares Daniel Borkmann
2026-10-28 18:41   ` Alexei Starovoitov
2026-10-27 18:24 ` [PATCH bpf-next 20/21] selftests/bpf: Let the arm64 private stack check skip the BTI pad Daniel Borkmann
2026-10-27 18:24 ` [PATCH bpf-next 21/21] selftests/bpf: Extend the JIT instruction tests for arm64 encodings Daniel Borkmann
2026-10-27 19:08   ` bot+bpf-ci
2026-10-28 12:38     ` Daniel Borkmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.