[RFC PATCH bpf-next 3/7] bpf: Enable BPF_LINK_UPDATE for fentry/fexit/fmod_ret links
Jordan Rife
jordan at jrife.io
Mon Nov 17 16:52:55 PST 2025
Implement program update for other link types supported by
bpf_tracing_link_lops (fentry, fexit, fmod_ret, ...). I wanted to make
the implementation generic so that no architecture-specific support
would be required, but couldn't think of a good way to do this:
* I considered updating link.prog before calling bpf_trampoline_update
and reverting it back to the old prog if the update fails, but this
could create inconsistencies with concurrent operations that read
links where they see the uncommitted program associated with the link.
* I considered making a deep copy of the link whose program is being
updated and putting a pointer to that copy into tlinks when calling
bpf_trampoline_get_progs where the copy references the new program
instead of the current program. This would avoid updating the original
link.prog before the update was committed; however, this seemed
slightly hacky and I wasn't sure if this was better than just making
the architecture-specific layer aware of the intent to update one of
the link programs.
This patch sets up the scaffolding for trampoline program updates while
subsequent patches enable this for various architectures. For now, only
x86, arm64, and s390 are implemented since that's what I could test in
CI.
Add update_link and update_prog to bpf_tramp_links. When these are
set, arch_bpf_trampoline_size() and arch_prepare_bpf_trampoline() use
update_prog in place of update_link->link.prog when calculating the
trampoline size and constructing a new trampoline image. link.prog is
only updated after the trampoline update is successfully committed. If
the current architecture does not support program updates (i.e.
bpf_trampoline_supports_update_prog() is not implemented) then the
BPF_LINK_UPDATE operation will return -ENOTSUPP.
Signed-off-by: Jordan Rife <jordan at jrife.io>
---
include/linux/bpf.h | 10 ++++++++++
kernel/bpf/trampoline.c | 33 +++++++++++++++++++++++++++++----
2 files changed, 39 insertions(+), 4 deletions(-)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 23fcbcd26aa4..7daf40cbdd9b 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1215,6 +1215,8 @@ enum {
struct bpf_tramp_links {
struct bpf_tramp_link *links[BPF_MAX_TRAMP_LINKS];
+ struct bpf_tramp_link *update_link;
+ struct bpf_prog *update_prog;
int nr_links;
};
@@ -1245,6 +1247,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
const struct btf_func_model *m, u32 flags,
struct bpf_tramp_links *tlinks,
void *func_addr);
+bool bpf_trampoline_supports_update_prog(void);
void *arch_alloc_bpf_trampoline(unsigned int size);
void arch_free_bpf_trampoline(void *image, unsigned int size);
int __must_check arch_protect_bpf_trampoline(void *image, unsigned int size);
@@ -1840,6 +1843,13 @@ struct bpf_tramp_link {
u64 cookie;
};
+static inline struct bpf_prog *
+bpf_tramp_links_prog(struct bpf_tramp_links *tl, int i)
+{
+ return tl->links[i] == tl->update_link ? tl->update_prog :
+ tl->links[i]->link.prog;
+}
+
struct bpf_shim_tramp_link {
struct bpf_tramp_link link;
struct bpf_trampoline *trampoline;
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 0b6a5433dd42..0c0373b76816 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -230,7 +230,10 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)
}
static struct bpf_tramp_links *
-bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total, bool *ip_arg)
+bpf_trampoline_get_progs(const struct bpf_trampoline *tr,
+ struct bpf_tramp_link *update_link,
+ struct bpf_prog *update_prog,
+ int *total, bool *ip_arg)
{
struct bpf_tramp_link *link;
struct bpf_tramp_links *tlinks;
@@ -250,6 +253,11 @@ bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total, bool *ip_a
hlist_for_each_entry(link, &tr->progs_hlist[kind], tramp_hlist) {
*ip_arg |= link->link.prog->call_get_func_ip;
*links++ = link;
+ if (link == update_link) {
+ *ip_arg |= update_prog->call_get_func_ip;
+ tlinks[kind].update_link = update_link;
+ tlinks[kind].update_prog = update_prog;
+ }
}
}
return tlinks;
@@ -395,7 +403,10 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size)
return ERR_PTR(err);
}
-static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex)
+static int __bpf_trampoline_update(struct bpf_trampoline *tr,
+ struct bpf_tramp_link *update_link,
+ struct bpf_prog *update_prog,
+ bool lock_direct_mutex)
{
struct bpf_tramp_image *im;
struct bpf_tramp_links *tlinks;
@@ -403,7 +414,11 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
bool ip_arg = false;
int err, total, size;
- tlinks = bpf_trampoline_get_progs(tr, &total, &ip_arg);
+ if (update_link && !bpf_trampoline_supports_update_prog())
+ return -ENOTSUPP;
+
+ tlinks = bpf_trampoline_get_progs(tr, update_link, update_prog,
+ &total, &ip_arg);
if (IS_ERR(tlinks))
return PTR_ERR(tlinks);
@@ -506,6 +521,11 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
goto out;
}
+static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex)
+{
+ return __bpf_trampoline_update(tr, NULL, NULL, lock_direct_mutex);
+}
+
static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
{
switch (prog->expected_attach_type) {
@@ -629,7 +649,7 @@ static int __bpf_trampoline_update_prog(struct bpf_tramp_link *link,
return 0;
}
- return -ENOTSUPP;
+ return __bpf_trampoline_update(tr, link, new_prog, true);
}
int bpf_trampoline_update_prog(struct bpf_tramp_link *link,
@@ -1139,6 +1159,11 @@ arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *image
return -ENOTSUPP;
}
+bool __weak bpf_trampoline_supports_update_prog(void)
+{
+ return false;
+}
+
void * __weak arch_alloc_bpf_trampoline(unsigned int size)
{
void *image;
--
2.43.0
More information about the linux-arm-kernel
mailing list