From: Emil Tsalapatis <emil@etsalapatis.com>
To: bpf@vger.kernel.org
Cc: ast@kernel.org, andrii@kernel.org, eddyz87@gmail.com,
memxor@gmail.com, daniel@iogearbox.net,
Emil Tsalapatis <emil@etsalapatis.com>
Subject: [RESEND PATCH bpf-next v6 6/7] bpf: Support per-call-site kfunc specialization
Date: Fri, 2 Oct 2026 10:52:17 +0000 [thread overview]
Message-ID: <20261002105218.6171-7-emil@etsalapatis.com> (raw)
In-Reply-To: <20261002105218.6171-1-emil@etsalapatis.com>
specialize_kfunc() currently updates the canonical kfunc descriptor in
place. Different call sites therefore cannot select different
specializations of the same kfunc, and the selected target depends on
verification order.
Keep canonical descriptors unchanged for verifier lookups. Specialize a
copy for each call site, then reuse or append an immutable target
descriptor and store its index in the finalized call instruction.
Allow space for one canonical and one specialized target per kfunc. This
is conservative because most kfuncs do not specialize. Adding specialized
kfunc versions does not require resorting the array because lookups are
only used for deduplication and func_model lookup. Deduplication for
specialized kfuncs is handled by the specialization process itself,
while all specializations of a function share the same proto and
func_model.
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
---
include/linux/bpf_verifier.h | 12 +++--
kernel/bpf/verifier.c | 88 +++++++++++++++++++++++++++++++++---
2 files changed, 91 insertions(+), 9 deletions(-)
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 5cbae5d05fe7..7bdbda7764c7 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -1784,7 +1784,9 @@ enum bpf_reg_arg_type {
};
#define MAX_KFUNC_DESCS 256
-static_assert(MAX_KFUNC_DESCS <= S16_MAX + 1);
+/* Each kfunc can have its canonical and one specialized call target. */
+#define MAX_KFUNC_CALL_DESCS (MAX_KFUNC_DESCS * 2)
+static_assert(MAX_KFUNC_CALL_DESCS <= S16_MAX + 1);
struct bpf_kfunc_desc {
struct btf_func_model func_model;
@@ -1796,11 +1798,15 @@ struct bpf_kfunc_desc {
struct bpf_kfunc_desc_tab {
u32 nr_descs;
+ u32 nr_base_descs;
/* Sorted by func_id (BTF ID) and offset (fd_array offset) during
* verification. JITs use the descriptor index stored in the finalized
- * call's off field.
+ * call's off field. The first nr_base_descs entries are the canonical
+ * descriptors used for verifier lookups. Call specialization may append
+ * immutable descriptors for additional targets.
*
- * Grown one entry at a time by bpf_add_kfunc_call().
+ * Grown one entry at a time by bpf_add_kfunc_call() and during
+ * call specialization.
*/
struct bpf_kfunc_desc descs[];
};
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8183a8f22ed5..3bddfff416ca 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2579,7 +2579,7 @@ find_kfunc_desc(const struct bpf_prog *prog, u32 func_id, u16 offset)
struct bpf_kfunc_desc_tab *tab;
tab = prog->aux->kfunc_tab;
- return bsearch(&desc, tab->descs, tab->nr_descs,
+ return bsearch(&desc, tab->descs, tab->nr_base_descs,
sizeof(tab->descs[0]), kfunc_desc_cmp_by_id_off);
}
@@ -2933,10 +2933,15 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
if (find_kfunc_desc(env->prog, func_id, offset))
return 0;
- if (tab->nr_descs == MAX_KFUNC_DESCS) {
+ if (tab->nr_base_descs == MAX_KFUNC_DESCS) {
verbose(env, "too many different kernel function calls\n");
return -E2BIG;
}
+ if (tab->nr_descs != tab->nr_base_descs) {
+ verifier_bug(env, "unexpected specialized func desc found (%d descs, %d base descs)",
+ tab->nr_descs, tab->nr_base_descs);
+ return -EFAULT;
+ }
err = fetch_kfunc_meta(env, func_id, offset, &kfunc);
if (err)
@@ -2994,7 +2999,8 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, u16 offset)
desc->addr = addr;
desc->func_model = func_model;
tab->nr_descs++;
- sort(tab->descs, tab->nr_descs, sizeof(tab->descs[0]),
+ tab->nr_base_descs++;
+ sort(tab->descs, tab->nr_base_descs, sizeof(tab->descs[0]),
kfunc_desc_cmp_by_id_off, NULL);
return 0;
}
@@ -22062,6 +22068,66 @@ static int specialize_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_desc
return 0;
}
+static int add_kfunc_desc_target(struct bpf_verifier_env *env,
+ const struct bpf_kfunc_desc *target_desc,
+ u16 base_desc_idx, u16 *desc_idx)
+{
+ struct bpf_kfunc_desc desc = *target_desc;
+ struct bpf_kfunc_desc_tab *new_tab;
+ struct bpf_kfunc_desc_tab *tab;
+ struct bpf_prog_aux *prog_aux;
+ u32 i;
+
+ prog_aux = env->prog->aux;
+ tab = prog_aux->kfunc_tab;
+
+ if (base_desc_idx >= tab->nr_base_descs) {
+ verifier_bug(env, "kfunc desc_idx %d out of bounds (%d descriptors)",
+ base_desc_idx, tab->nr_base_descs);
+ return -EFAULT;
+ }
+ if (tab->descs[base_desc_idx].func_id != desc.func_id) {
+ verifier_bug(env, "kfunc desc %d has invalid id (expected %d, got %d)",
+ base_desc_idx, tab->descs[base_desc_idx].func_id, desc.func_id);
+ return -EFAULT;
+ }
+ if (tab->descs[base_desc_idx].offset != desc.offset) {
+ verifier_bug(env, "kfunc desc %d has invalid offset (expected %d, got %d)",
+ base_desc_idx, tab->descs[base_desc_idx].offset, desc.offset);
+ return -EFAULT;
+ }
+
+ if (tab->descs[base_desc_idx].addr == desc.addr) {
+ *desc_idx = base_desc_idx;
+ return 0;
+ }
+
+ for (i = tab->nr_base_descs; i < tab->nr_descs; i++) {
+ if (tab->descs[i].func_id == desc.func_id &&
+ tab->descs[i].offset == desc.offset &&
+ tab->descs[i].addr == desc.addr) {
+ *desc_idx = i;
+ return 0;
+ }
+ }
+
+ if (tab->nr_descs == MAX_KFUNC_CALL_DESCS) {
+ verbose(env, "too many different kernel function call targets\n");
+ return -E2BIG;
+ }
+
+ new_tab = krealloc(tab, struct_size(tab, descs, tab->nr_descs + 1),
+ GFP_KERNEL_ACCOUNT);
+ if (!new_tab)
+ return -ENOMEM;
+ tab = new_tab;
+ prog_aux->kfunc_tab = tab;
+
+ *desc_idx = tab->nr_descs;
+ tab->descs[tab->nr_descs++] = desc;
+ return 0;
+}
+
static void __fixup_collection_insert_kfunc(struct bpf_insn_aux_data *insn_aux,
u16 struct_meta_reg,
u16 node_offset_reg,
@@ -22082,9 +22148,11 @@ static void __fixup_collection_insert_kfunc(struct bpf_insn_aux_data *insn_aux,
int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
struct bpf_insn *insn_buf, int insn_idx, int *cnt)
{
+ struct bpf_kfunc_desc desc_copy;
struct bpf_kfunc_desc *desc;
unsigned long call_imm;
- u16 desc_idx;
+ u16 base_desc_idx, desc_idx;
+ bool near_call;
int err;
if (!insn->imm) {
@@ -22104,13 +22172,17 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
insn->imm);
return -EFAULT;
}
- desc_idx = desc - env->prog->aux->kfunc_tab->descs;
+ base_desc_idx = desc - env->prog->aux->kfunc_tab->descs;
+
+ near_call = !bpf_jit_supports_far_kfunc_call();
+ desc_copy = *desc;
+ desc = &desc_copy;
err = specialize_kfunc(env, desc, insn_idx);
if (err)
return err;
- if (!bpf_jit_supports_far_kfunc_call()) {
+ if (near_call) {
call_imm = BPF_CALL_IMM(desc->addr);
if ((unsigned long)(s32)call_imm != call_imm) {
verbose(env, "address of kernel func_id %u is out of range\n",
@@ -22119,6 +22191,10 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
insn->imm = call_imm;
}
+
+ err = add_kfunc_desc_target(env, desc, base_desc_idx, &desc_idx);
+ if (err)
+ return err;
insn->off = desc_idx;
if (is_bpf_obj_new_kfunc(desc->func_id) || is_bpf_percpu_obj_new_kfunc(desc->func_id)) {
--
2.52.0
next prev parent reply other threads:[~2026-10-02 10:52 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-02 10:52 [RESEND PATCH bpf-next v6 0/7] Make sleepable arena paths use sleepable alloc_pages Emil Tsalapatis
2026-10-02 10:52 ` [RESEND PATCH bpf-next v6 1/7] bpf: Use an llist for page allocations Emil Tsalapatis
2026-10-02 10:52 ` [RESEND PATCH bpf-next v6 2/7] bpf: Add sleepable argument to bpf_alloc_pages() Emil Tsalapatis
2026-10-02 10:52 ` [RESEND PATCH bpf-next v6 3/7] bpf: Add sleepable arena page allocation path Emil Tsalapatis
2026-10-02 10:52 ` [RESEND PATCH bpf-next v6 4/7] selftests/bpf: Test large allocations for both sleepable/nonsleepable arena users Emil Tsalapatis
2026-10-02 10:52 ` [RESEND PATCH bpf-next v6 5/7] bpf: Directly store kfunc desc index in instruction off field Emil Tsalapatis
2026-10-02 11:06 ` sashiko-bot
2026-10-02 11:47 ` bot+bpf-ci
2026-10-02 10:52 ` Emil Tsalapatis [this message]
2026-10-02 10:52 ` [RESEND PATCH bpf-next v6 7/7] selftests/bpf: Test per-call site function specialization Emil Tsalapatis
2026-10-02 13:10 ` [RESEND PATCH bpf-next v6 0/7] Make sleepable arena paths use sleepable alloc_pages patchwork-bot+netdevbpf
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=20261002105218.6171-7-emil@etsalapatis.com \
--to=emil@etsalapatis.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=memxor@gmail.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