linux-next: manual merge of the bpf-next tree with the bpf tree

From: Mark Brown

Date: Tue Nov 03 2026 - 10:12:09 EST


Hi all,

Today's linux-next merge of the bpf-next tree got a conflict in:

kernel/bpf/fixups.c

between commits:

ec274bb4665c2 ("bpf: One may_goto time slice per prog invocation")
1ddffb57b3623 ("bpf: Emit timed may_goto for JITed progs only")
1ddffb57b3623 ("bpf: Emit timed may_goto for JITed progs only")

from the bpf tree and commit:

a7a767c66a2e2 ("bpf: Queue the rewrites of bpf_do_misc_fixups()")

and likely others from the bpf-next tree. Given the nature of the
conflict and my total lack of familiarity with this code it seems
vanishingly unlikely that I'd be able to do a sensible merge here,
instead I will hold the bpf-next tree at the version from next-20261102.
If someone could help me out with the merge here that would be very much
appreciated.

The conflict is:

diff --cc kernel/bpf/fixups.c
index a656634fa9e46,ead78713e04f4..0000000000000
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@@ -1804,47 -1776,24 +1776,68 @@@ static int may_goto_expand(struct bpf_i
}

/*
++<<<<<<< HEAD
+ * Timed may_goto. All may_goto of a prog use one count and one timestamp: per-cpu memory of the
+ * prog, set in the first insns of the main prog, and by the callers of async callbacks.
+ * @reg holds the address. It is a register that is dead at the may_goto, or R0 that is saved at
+ * @save_off in the stack.
+ */
+static int may_goto_expand_timed(struct bpf_insn *insn_buf, int off,
+ struct bpf_timed_may_goto __percpu *tmg, int reg, int save_off)
+{
+ struct bpf_insn ld_tmg[] = { BPF_LD_IMM64(reg, (__force unsigned long)tmg) };
+ int cnt = 0, jmp, tail_cnt = save_off ? 6 : 5;
+
+ if (save_off)
+ insn_buf[cnt++] = BPF_STX_MEM(BPF_DW, BPF_REG_10, reg, save_off);
+ insn_buf[cnt++] = ld_tmg[0];
+ insn_buf[cnt++] = ld_tmg[1];
+ insn_buf[cnt++] = BPF_MOV64_PERCPU_REG(reg, reg);
+ insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, BPF_REG_AX, reg, 0);
+ if (save_off) {
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 2);
+ insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, reg, BPF_REG_10, save_off);
+ jmp = cnt++;
+ insn_buf[jmp] = BPF_JMP32_A(off >= 0 ? off + tail_cnt : off - jmp);
+ } else {
+ jmp = cnt;
+ off = off >= 0 ? off + tail_cnt : off - jmp;
+ if (off == (s16)off) {
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JEQ, BPF_REG_AX, 0, off);
+ } else {
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 1);
+ insn_buf[cnt++] = BPF_JMP32_A(off >= 0 ? off : off - 1);
+ }
+ }
+ insn_buf[cnt++] = BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1);
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 2);
+ /* AX is the argument and the return value */
+ insn_buf[cnt++] = BPF_MOV64_REG(BPF_REG_AX, reg);
+ insn_buf[cnt++] = BPF_EMIT_CALL(arch_bpf_timed_may_goto);
+ insn_buf[cnt++] = BPF_STX_MEM(BPF_DW, reg, BPF_REG_AX, 0);
+ if (save_off)
+ insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, reg, BPF_REG_10, save_off);
+ return cnt;
++=======
+ * The offset of may_goto at 'idx' once the patches queued in front are applied.
+ * The patches that a forward may_goto jumps over aren't queued yet, so its
+ * offset stays as it is.
+ */
+ static int may_goto_off(struct bpf_verifier_env *env, const struct bpf_insn *insn,
+ u32 idx)
+ {
+ struct bpf_patch_list *pl = &env->patch_list;
+ u32 tgt = idx + insn->off + 1;
+ int off = insn->off;
+ u32 k = pl->cnt;
+
+ if (off >= 0)
+ return off;
+ /* all queued patches are in front of idx */
+ while (k > 0 && pl->patches[k - 1].off >= tgt)
+ off -= pl->patches[--k].len - 1;
+ return off;
++>>>>>>> bpf-next/for-next
}

/* Do various post-verification rewrites in a single program pass.
@@@ -2125,36 -2050,43 +2097,53 @@@ int bpf_do_misc_fixups(struct bpf_verif
goto next_insn;
}

- if (bpf_is_may_goto_insn(insn) && bpf_jit_supports_timed_may_goto()) {
- int stack_off_cnt = -stack_depth - 16;
- /*
- * AX is used as an argument to pass in stack_off_cnt
- * (to add to r10/fp), and also as the return value of
- * the call to arch_bpf_timed_may_goto.
- */
- struct bpf_insn tail[] = {
- BPF_ALU64_IMM(BPF_SUB, BPF_REG_AX, 1),
- BPF_JMP_IMM(BPF_JNE, BPF_REG_AX, 0, 2),
- BPF_MOV64_IMM(BPF_REG_AX, stack_off_cnt),
- BPF_EMIT_CALL(arch_bpf_timed_may_goto),
- BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off_cnt),
- };
+ if (bpf_is_may_goto_insn(insn) && timed_may_goto) {
+ u16 live = env->insn_aux_data[i + delta].live_regs_before;
+ int reg, save_off = 0;

++<<<<<<< HEAD
+ /* a register that is dead here holds the address of the count */
+ for (reg = BPF_REG_0; reg <= BPF_REG_9; reg++)
+ if (!(live & BIT(reg)))
+ break;
+ if (reg > BPF_REG_9) {
+ reg = BPF_REG_0;
+ save_off = -stack_depth - 8;
+ stack_depth_extra = 8;
+ }
+ if (!tmg) {
+ tmg = alloc_percpu_gfp(struct bpf_timed_may_goto,
+ GFP_KERNEL_ACCOUNT);
+ if (!tmg)
+ return -ENOMEM;
+ prog->aux->timed_may_goto = tmg;
+ }
+ prog->jit_required = 1;
+ cnt = may_goto_expand_timed(insn_buf, insn->off, tmg, reg, save_off);
++=======
+ /*
+ * Two 8 byte slots, depth-16 stores the count, and
+ * depth-8 stores the start timestamp of the loop.
+ *
+ * The starting value of count is BPF_MAX_TIMED_LOOPS
+ * (0xffff). Every iteration loads it and subs it by 1,
+ * until the value becomes 0 in AX (thus, 1 in stack),
+ * after which we call arch_bpf_timed_may_goto, which
+ * either sets AX to 0xffff to keep looping, or to 0
+ * upon timeout. AX is then stored into the stack. In
+ * the next iteration, we either see 0 and break out, or
+ * continue iterating until the next time value is 0
+ * after subtraction, rinse and repeat.
+ */
+ stack_depth_extra = 16;
+ cnt = may_goto_expand(insn_buf, insn->off,
+ may_goto_off(env, insn, i),
+ stack_off_cnt, tail, ARRAY_SIZE(tail));
++>>>>>>> bpf-next/for-next

- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
- if (!new_prog)
- return -ENOMEM;
-
- delta += cnt - 1;
- env->prog = prog = new_prog;
- insn = new_prog->insnsi + i + delta;
+ insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+ if (IS_ERR(insn))
+ return PTR_ERR(insn);
goto next_insn;
} else if (bpf_is_may_goto_insn(insn)) {
int stack_off = -stack_depth - 8;
@@@ -2762,86 -2630,49 +2687,102 @@@ next_insn
stack_depth_extra = 0;
}
i++;
- insn++;
+ insn = &prog->insnsi[i];
}

+ ret = bpf_patch_list_commit(env);
+ if (ret)
+ return ret;
+ prog = env->prog;
+
env->prog->aux->stack_depth = subprogs[0].stack_depth;
for (i = 0; i < env->subprog_cnt; i++) {
++<<<<<<< HEAD
+ int subprog_start = subprogs[i].start;
+ int stack_slots = subprogs[i].stack_extra / 8;
+ /* an invocation begins, see bpf_timed_may_goto_save() for async callbacks */
+ bool main_prog = tmg && !i;
+ /* only a helper calls it, in a loop that ends when it returns 1 */
+ bool loop_cb = tmg && i && subprogs[i].is_loop_cb && !subprogs[i].is_called &&
+ !subprogs[i].is_async_cb;
+ int cnt = 0;
++=======
+ int slots = bpf_jit_supports_timed_may_goto() ? 2 : 1;
+ int stack_slots = subprogs[i].stack_extra / 8;
++>>>>>>> bpf-next/for-next

- if (!stack_slots)
+ if (timed_may_goto ? !main_prog && !loop_cb : !stack_slots)
continue;
- /* We need two slots in case timed may_goto is supported. */
- if (stack_slots > slots) {
+ if (stack_slots > 1) {
verifier_bug(env, "stack_slots supports may_goto only");
return -EFAULT;
}

+ cnt = 0;
stack_depth = subprogs[i].stack_depth;
- if (bpf_jit_supports_timed_may_goto()) {
- insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_FP, -stack_depth,
- BPF_MAX_TIMED_LOOPS);
- insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_FP, -stack_depth + 8, 0);
+ if (timed_may_goto) {
+ struct bpf_insn ld_tmg[] = {
+ BPF_LD_IMM64(BPF_REG_0, (__force unsigned long)tmg)
+ };
+ int count_off = offsetof(struct bpf_timed_may_goto, count);
+ int timestamp_off = offsetof(struct bpf_timed_may_goto, timestamp);
+ int owner_off = offsetof(struct bpf_timed_may_goto, owner);
+
+ /* R0 has nothing at the entry */
+ insn_buf[cnt++] = ld_tmg[0];
+ insn_buf[cnt++] = ld_tmg[1];
+ insn_buf[cnt++] = BPF_MOV64_PERCPU_REG(BPF_REG_0, BPF_REG_0);
+ if (main_prog) {
+ insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_0, count_off,
+ BPF_MAX_TIMED_LOOPS);
+ insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_0, timestamp_off, 0);
+ insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_0, owner_off, 0);
+ } else {
+ /*
+ * The time is up, but the helper would call the function again and
+ * again. End its loop. The verifier has seen that: the loop can end
+ * after each call.
+ */
+ insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0,
+ count_off);
+ insn_buf[cnt++] = BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2);
+ insn_buf[cnt++] = BPF_MOV64_IMM(BPF_REG_0, 1);
+ insn_buf[cnt++] = BPF_EXIT_INSN();
+ }
} else {
/* Add ST insn to subprog prologue to init extra stack */
insn_buf[cnt++] = BPF_ST_MEM(BPF_DW, BPF_REG_FP, -stack_depth,
BPF_MAX_LOOPS);
}
- /* Copy first actual insn to preserve it */
- insn_buf[cnt] = env->prog->insnsi[subprog_start];
- ret = bpf_adj_moved_insn(&insn_buf[cnt], cnt);
- if (ret) {
- verbose(env, "insn %d cannot be patched due to 16-bit range\n",
- env->insn_aux_data[subprog_start].orig_idx);
+ /* jumps to the first insn of the subprog skip the setup */
+ ret = bpf_patch_list_insert(env, subprogs[i].start, insn_buf, cnt);
+ if (ret)
return ret;
++<<<<<<< HEAD
+ }
+ cnt++;
+
+ new_prog = __bpf_patch_insn_data(env, subprog_start, insn_buf, cnt,
+ BPF_PATCH_MOVE_TARGET);
+ if (!new_prog)
+ return -ENOMEM;
+ env->prog = prog = new_prog;
+ /*
+ * If may_goto is a first insn of a prog there could be a jmp
+ * insn that points to it, hence adjust all such jmps to point
+ * to insn after BPF_ST that inits may_goto count.
+ * Adjustment will succeed because bpf_patch_insn_data() didn't fail.
+ */
+ WARN_ON(adjust_jmp_off(env->prog, subprog_start, cnt - 1));
++=======
++>>>>>>> bpf-next/for-next
}

+ ret = bpf_patch_list_commit(env);
+ if (ret)
+ return ret;
+ prog = env->prog;
+
/* Since poke tab is now finalized, publish aux to tracker. */
for (i = 0; i < prog->aux->size_poke_tab; i++) {
map_ptr = prog->aux->poke_tab[i].tail_call.map;

Attachment: signature.asc
Description: PGP signature