From: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>
To: "Mykyta Yatsenko" <mykyta.yatsenko5@gmail.com>,
<bpf@vger.kernel.org>, <andrii@kernel.org>,
<daniel@iogearbox.net>, <kernel-team@meta.com>,
<eddyz87@gmail.com>, <memxor@gmail.com>
Cc: "Mykyta Yatsenko" <yatsenko@meta.com>
Subject: Re: [PATCH bpf-next 1/2] bpf: Stop JIT passes only when no insn moves
Date: Wed, 21 Oct 2026 22:15:06 +0000 [thread overview]
Message-ID: <DMAV2RFTNVWP.26B5N5NZ3TCEL@gmail.com> (raw)
In-Reply-To: <20261021-jit_size_fix-v1-1-b89fd32cafd6@meta.com>
On Wed, Oct 21, 2026 at 07:21 AM Mykyta Yatsenko <mykyta.yatsenko5@gmail.com> wrote:
> A backward jump is sized from its target's current-pass address and its
> own previous-pass address, so it can grow while other jumps shrink. Two
> passes may then give the same image length with jumps still changing,
> and the image pass fails with "bpf_jit: fatal error".
can we fix the sizing of the backward jump instead ?
It's the third time it bites us.
commit 1612a981b766 ("bpf, x64: fix JIT emission for dead code")
special cased ja -1 and
commit c8831bdbfbab ("bpf, x64: Fix a jit convergence issue")
limited imm8 to 123 because of it.
With this patch jumps still grow, and the one that grows in a padding
pass still ends in "unexpected jmp_cond padding: -4 bytes".
do_jit() knows how far the current pass moved insn i. With that taken
into account a backward jump is sized by the insns it jumps over, the
same way as a forward one. Insn sizes start at 64 and only go down, so
the image shrinks with every pass like the comment in
bpf_int_jit_compile() says, and proglen == oldproglen means that
nothing moved.
Then no need for addrs_changed, for the ja -1 special case and
for is_imm8_jmp_offset().
Something like this (compile tested only):
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 6a2fe3d..c20d7d3 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -97,56 +97,6 @@ static bool is_imm8(int value)
return value <= 127 && value >= -128;
}
-/*
- * Let us limit the positive offset to be <= 123.
- * This is to ensure eventual jit convergence For the following patterns:
- * ...
- * pass4, final_proglen=4391:
- * ...
- * 20e: 48 85 ff test rdi,rdi
- * 211: 74 7d je 0x290
- * 213: 48 8b 77 00 mov rsi,QWORD PTR [rdi+0x0]
- * ...
- * 289: 48 85 ff test rdi,rdi
- * 28c: 74 17 je 0x2a5
- * 28e: e9 7f ff ff ff jmp 0x212
- * 293: bf 03 00 00 00 mov edi,0x3
- * Note that insn at 0x211 is 2-byte cond jump insn for offset 0x7d (-125)
- * and insn at 0x28e is 5-byte jmp insn with offset -129.
- *
- * pass5, final_proglen=4392:
- * ...
- * 20e: 48 85 ff test rdi,rdi
- * 211: 0f 84 80 00 00 00 je 0x297
- * 217: 48 8b 77 00 mov rsi,QWORD PTR [rdi+0x0]
- * ...
- * 28d: 48 85 ff test rdi,rdi
- * 290: 74 1a je 0x2ac
- * 292: eb 84 jmp 0x218
- * 294: bf 03 00 00 00 mov edi,0x3
- * Note that insn at 0x211 is 6-byte cond jump insn now since its offset
- * becomes 0x80 based on previous round (0x293 - 0x213 = 0x80).
- * At the same time, insn at 0x292 is a 2-byte insn since its offset is
- * -124.
- *
- * pass6 will repeat the same code as in pass4 and this will prevent
- * eventual convergence.
- *
- * To fix this issue, we need to break je (2->6 bytes) <-> jmp (5->2 bytes)
- * cycle in the above. In the above example je offset <= 0x7c should work.
- *
- * For other cases, je <-> je needs offset <= 0x7b to avoid no convergence
- * issue. For jmp <-> je and jmp <-> jmp cases, jmp offset <= 0x7c should
- * avoid no convergence issue.
- *
- * Overall, let us limit the positive offset for 8bit cond/uncond jmp insn
- * to maximum 123 (0x7b). This way, the jit pass can eventually converge.
- */
-static bool is_imm8_jmp_offset(int value)
-{
- return value <= 123 && value >= -128;
-}
-
static bool is_simm32(s64 value)
{
return value == (s64)(s32)value;
@@ -2298,6 +2248,17 @@ static void emit_priv_frame_ptr(u8 **pprog, void __percpu *priv_frame_ptr)
#define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp)))
+/*
+ * addrs[] below i are from this pass, addrs[i] and above are from the previous
+ * one. 'moved' is how far this pass moved the start of insn i. With it a jump
+ * in either direction is sized by the insns it jumps over. All insns start at
+ * 64 bytes, so none of them ever grows and the image shrinks with every pass.
+ */
+static int jmp_distance(const int *addrs, int i, int off, int moved)
+{
+ return addrs[i + off] - addrs[i] + (off < 0 ? moved : 0);
+}
+
#define __LOAD_TCC_PTR(off) \
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, off)
/* mov rax, qword ptr [rbp - rounded_stack_depth - 16] */
@@ -2588,7 +2549,7 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
u64 arena_vm_start = 0, user_vm_start = 0;
void __percpu *priv_stack_ptr;
int i, excnt = 0;
- int ilen, proglen = 0;
+ int ilen, moved, proglen = 0;
u8 *ip, *prog = temp;
u32 stack_depth;
int callee_saved_size;
@@ -2702,6 +2663,7 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
if (rw_image)
memcpy(rw_image + proglen, temp, ilen);
proglen += ilen;
+ moved = addrs[0] - proglen;
addrs[0] = proglen;
prog = temp;
@@ -3747,8 +3709,8 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
default: /* to silence GCC warning */
return -EFAULT;
}
- jmp_offset = addrs[i + insn->off] - addrs[i];
- if (is_imm8_jmp_offset(jmp_offset)) {
+ jmp_offset = jmp_distance(addrs, i, insn->off, moved);
+ if (is_imm8(jmp_offset)) {
if (jmp_padding) {
/* To keep the jmp_offset valid, the extra bytes are
* padded before the jump insn, so we subtract the
@@ -3788,23 +3750,10 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
break;
case BPF_JMP | BPF_JA:
case BPF_JMP32 | BPF_JA:
- if (BPF_CLASS(insn->code) == BPF_JMP) {
- if (insn->off == -1)
- /* -1 jmp instructions will always jump
- * backwards two bytes. Explicitly handling
- * this case avoids wasting too many passes
- * when there are long sequences of replaced
- * dead code.
- */
- jmp_offset = -2;
- else
- jmp_offset = addrs[i + insn->off] - addrs[i];
- } else {
- if (insn->imm == -1)
- jmp_offset = -2;
- else
- jmp_offset = addrs[i + insn->imm] - addrs[i];
- }
+ if (BPF_CLASS(insn->code) == BPF_JMP)
+ jmp_offset = jmp_distance(addrs, i, insn->off, moved);
+ else
+ jmp_offset = jmp_distance(addrs, i, insn->imm, moved);
if (!jmp_offset) {
/*
@@ -3833,7 +3782,7 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
break;
}
emit_jmp:
- if (is_imm8_jmp_offset(jmp_offset)) {
+ if (is_imm8(jmp_offset)) {
if (jmp_padding) {
/* To avoid breaking jmp_offset, the extra bytes
* are padded before the actual jmp insn, so
@@ -3869,7 +3818,7 @@ emit_jmp:
emit_unwind_ret_addr(&prog, ip))
return -EINVAL;
if (seen_exit) {
- jmp_offset = ctx->cleanup_addr - addrs[i];
+ jmp_offset = ctx->cleanup_addr - addrs[i] + moved;
goto emit_jmp;
}
seen_exit = true;
@@ -3930,6 +3879,7 @@ emit_jmp:
memcpy(rw_image + proglen, temp, ilen);
}
proglen += ilen;
+ moved = addrs[i] - proglen;
addrs[i] = proglen;
prog = temp;
}
pw-bot: cr
next prev parent reply other threads:[~2026-10-21 22:15 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-21 14:21 [PATCH bpf-next 0/2] bpf: Fix x86 JIT stopping before jumps converge Mykyta Yatsenko
2026-10-21 14:21 ` [PATCH bpf-next 1/2] bpf: Stop JIT passes only when no insn moves Mykyta Yatsenko
2026-10-21 22:15 ` Alexei Starovoitov [this message]
2026-10-21 22:52 ` Mykyta Yatsenko
2026-10-21 14:21 ` [PATCH bpf-next 2/2] selftests/bpf: Test JIT jumps which change at a repeated image length Mykyta Yatsenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DMAV2RFTNVWP.26B5N5NZ3TCEL@gmail.com \
--to=alexei.starovoitov@gmail.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=kernel-team@meta.com \
--cc=memxor@gmail.com \
--cc=mykyta.yatsenko5@gmail.com \
--cc=yatsenko@meta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox