[RFC PATCH bpf-next 1/7] bpf: Set up update_prog scaffolding for bpf_tracing_link_lops
Jordan Rife
jordan at jrife.io
Mon Nov 17 16:52:53 PST 2025
The type and expected_attach_type of the new program must match the
original program and the new program must be compatible with the
attachment target. Use a global mutex, trace_link_mutex, to synchronize
parallel update operations similar to other link types (sock_map, xdp,
etc.) that use a global mutex. Contention should be low, so this should
be OK. Subsequent patches fill in the program update logic for
freplace/fentry/fmod_ret/fexit links.
Signed-off-by: Jordan Rife <jordan at jrife.io>
---
include/linux/bpf.h | 11 +++++++
kernel/bpf/syscall.c | 68 +++++++++++++++++++++++++++++++++++++++++
kernel/bpf/trampoline.c | 29 ++++++++++++++++++
3 files changed, 108 insertions(+)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 09d5dc541d1c..23fcbcd26aa4 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1420,6 +1420,9 @@ static inline int bpf_dynptr_check_off_len(const struct bpf_dynptr_kern *ptr, u6
int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr,
struct bpf_prog *tgt_prog);
+int bpf_trampoline_update_prog(struct bpf_tramp_link *link,
+ struct bpf_prog *new_prog,
+ struct bpf_trampoline *tr);
int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr,
struct bpf_prog *tgt_prog);
@@ -1509,6 +1512,14 @@ static inline int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
{
return -ENOTSUPP;
}
+
+static inline int bpf_trampoline_update_prog(struct bpf_tramp_link *link,
+ struct bpf_prog *new_prog,
+ struct bpf_trampoline *tr)
+{
+ return -ENOTSUPP;
+}
+
static inline int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr,
struct bpf_prog *tgt_prog)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index f62d61b6730a..b0da7c428a65 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -63,6 +63,8 @@ static DEFINE_IDR(map_idr);
static DEFINE_SPINLOCK(map_idr_lock);
static DEFINE_IDR(link_idr);
static DEFINE_SPINLOCK(link_idr_lock);
+/* Synchronizes access to prog between link update operations. */
+static DEFINE_MUTEX(trace_link_mutex);
int sysctl_unprivileged_bpf_disabled __read_mostly =
IS_BUILTIN(CONFIG_BPF_UNPRIV_DEFAULT_OFF) ? 2 : 0;
@@ -3562,11 +3564,77 @@ static int bpf_tracing_link_fill_link_info(const struct bpf_link *link,
return 0;
}
+static int bpf_tracing_link_update_prog(struct bpf_link *link,
+ struct bpf_prog *new_prog,
+ struct bpf_prog *old_prog)
+{
+ struct bpf_tracing_link *tr_link =
+ container_of(link, struct bpf_tracing_link, link.link);
+ struct bpf_attach_target_info tgt_info = {0};
+ int err = 0;
+ u32 btf_id;
+
+ mutex_lock(&trace_link_mutex);
+
+ if (old_prog && link->prog != old_prog) {
+ err = -EPERM;
+ goto out;
+ }
+ old_prog = link->prog;
+ if (old_prog->type != new_prog->type ||
+ old_prog->expected_attach_type != new_prog->expected_attach_type) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&new_prog->aux->dst_mutex);
+
+ if (!new_prog->aux->dst_trampoline ||
+ new_prog->aux->dst_trampoline->key != tr_link->trampoline->key) {
+ bpf_trampoline_unpack_key(tr_link->trampoline->key, NULL,
+ &btf_id);
+ /* If there is no saved target, or the target associated with
+ * this link is different from the destination specified at
+ * load time, we need to check for compatibility.
+ */
+ err = bpf_check_attach_target(NULL, new_prog, tr_link->tgt_prog,
+ btf_id, &tgt_info);
+ if (err)
+ goto out_unlock;
+ }
+
+ err = bpf_trampoline_update_prog(&tr_link->link, new_prog,
+ tr_link->trampoline);
+ if (err)
+ goto out_unlock;
+
+ /* Clear the trampoline, mod, and target prog from new_prog->aux to make
+ * sure the original attach destination is not kept alive after a
+ * program is (re-)attached to another target.
+ */
+ if (new_prog->aux->dst_prog)
+ bpf_prog_put(new_prog->aux->dst_prog);
+ bpf_trampoline_put(new_prog->aux->dst_trampoline);
+ module_put(new_prog->aux->mod);
+
+ new_prog->aux->dst_prog = NULL;
+ new_prog->aux->dst_trampoline = NULL;
+ new_prog->aux->mod = tgt_info.tgt_mod;
+ tgt_info.tgt_mod = NULL; /* Make module_put() below do nothing. */
+out_unlock:
+ mutex_unlock(&new_prog->aux->dst_mutex);
+out:
+ mutex_unlock(&trace_link_mutex);
+ module_put(tgt_info.tgt_mod);
+ return err;
+}
+
static const struct bpf_link_ops bpf_tracing_link_lops = {
.release = bpf_tracing_link_release,
.dealloc = bpf_tracing_link_dealloc,
.show_fdinfo = bpf_tracing_link_show_fdinfo,
.fill_link_info = bpf_tracing_link_fill_link_info,
+ .update_prog = bpf_tracing_link_update_prog,
};
static int bpf_tracing_prog_attach(struct bpf_prog *prog,
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 5949095e51c3..010bcba0db65 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -610,6 +610,35 @@ int bpf_trampoline_link_prog(struct bpf_tramp_link *link,
return err;
}
+static int __bpf_trampoline_update_prog(struct bpf_tramp_link *link,
+ struct bpf_prog *new_prog,
+ struct bpf_trampoline *tr)
+{
+ return -ENOTSUPP;
+}
+
+int bpf_trampoline_update_prog(struct bpf_tramp_link *link,
+ struct bpf_prog *new_prog,
+ struct bpf_trampoline *tr)
+{
+ struct bpf_prog *old_prog;
+ int err;
+
+ mutex_lock(&tr->mutex);
+ err = __bpf_trampoline_update_prog(link, new_prog, tr);
+ if (!err) {
+ /* If a program update was successful, switch the program
+ * in the link before releasing tr->mutex; otherwise, another
+ * operation could come along and update the trampoline with
+ * the link still pointing at the old program.
+ */
+ old_prog = xchg(&link->link.prog, new_prog);
+ bpf_prog_put(old_prog);
+ }
+ mutex_unlock(&tr->mutex);
+ return err;
+}
+
static int __bpf_trampoline_unlink_prog(struct bpf_tramp_link *link,
struct bpf_trampoline *tr,
struct bpf_prog *tgt_prog)
--
2.43.0
More information about the linux-arm-kernel
mailing list