Linux Trace Kernel
 help / color / mirror / Atom feed
* [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs
@ 2026-08-09 15:00 Leon Hwang
  2026-08-09 15:00 ` [PATCH bpf-next 01/13] bpf: Initialize ftrace_managed in bpf_trampoline_get Leon Hwang
                   ` (12 more replies)
  0 siblings, 13 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:00 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Similar to the tracing_multi link support for kernel functions [1], add
support for bpf progs.

When attaching to bpf progs, it must attaches to the target by text poke
way.

[1] https://lore.kernel.org/bpf/20260606123955.345967-1-jolsa@kernel.org/

Leon Hwang (13):
  bpf: Initialize ftrace_managed in bpf_trampoline_get
  bpf: Factor out update_fentry_multi helper
  bpf: Drop unnecessary ftrace_location() in update_fentry_multi()
  bpf: Add tracing_multi link support for bpf progs
  libbpf: Add tracing_multi link support for bpf progs
  bpf: Add tracing_multi link fdinfo support for bpf progs
  bpf: Add tracing_multi link info support for bpf progs
  selftests/bpf: Add tracing_multi bpf prog attach test
  selftests/bpf: Add tracing_multi bpf prog attach failure tests
  selftests/bpf: Add tracing_multi bpf prog cookie test
  selftests/bpf: Add tracing_multi bpf prog rollback test
  selftests/bpf: Add tracing_multi bpf prog link info test
  selftests/bpf: Test tailcall with fentry.multi

 include/linux/bpf.h                           |   3 +
 include/uapi/linux/bpf.h                      |   6 +-
 kernel/bpf/trampoline.c                       |  87 +++---
 kernel/bpf/verifier.c                         |  22 +-
 kernel/trace/bpf_trace.c                      | 212 ++++++++++++---
 tools/bpf/bpftool/link.c                      |  59 +++-
 tools/include/uapi/linux/bpf.h                |   6 +-
 tools/lib/bpf/bpf.c                           |   1 +
 tools/lib/bpf/bpf.h                           |   2 +
 tools/lib/bpf/libbpf.c                        |  39 ++-
 tools/lib/bpf/libbpf.h                        |   4 +-
 tools/lib/bpf/libbpf_internal.h               |   1 +
 .../selftests/bpf/prog_tests/fill_link_info.c | 170 ++++++++++--
 .../selftests/bpf/prog_tests/tailcalls.c      |  62 +++++
 .../selftests/bpf/prog_tests/tracing_multi.c  | 254 ++++++++++++++++++
 .../selftests/bpf/progs/tracing_multi_bpf.c   |  61 +++++
 16 files changed, 869 insertions(+), 120 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/tracing_multi_bpf.c

--
2.55.0

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 01/13] bpf: Initialize ftrace_managed in bpf_trampoline_get
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
@ 2026-08-09 15:00 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 02/13] bpf: Factor out update_fentry_multi helper Leon Hwang
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:00 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

A trampoline managed by ftrace is determined by its target, that said
ftrace_managed is already known when the trampoline is initialized. so,
set 'ftrace_managed = tr->ip != 0' in bpf_trampoline_get() instead of
relying on the first regular or tracing_multi attachment to set it.

This makes the backend selection independent of attachment order and keeps
the registration callbacks focused on installing the trampoline.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 kernel/bpf/trampoline.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index e07af35ed040..8c2c1d1b9094 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -474,18 +474,11 @@ static int register_fentry(struct bpf_trampoline *tr, struct bpf_tramp_image *im
 			   void *data __maybe_unused)
 {
 	void *new_addr = im->image;
-	void *ip = tr->func.addr;
-	unsigned long faddr;
 	int ret;
 
-	faddr = ftrace_location((unsigned long)ip);
-	if (faddr) {
+	if (tr->func.ftrace_managed) {
 		if (!tr->fops)
 			return -ENOTSUPP;
-		tr->func.ftrace_managed = true;
-	}
-
-	if (tr->func.ftrace_managed) {
 		ret = direct_ops_add(tr, new_addr);
 	} else {
 		ret = bpf_trampoline_update_fentry(tr, 0, NULL, new_addr);
@@ -1226,6 +1219,7 @@ struct bpf_trampoline *bpf_trampoline_get(u64 key,
 
 	memcpy(&tr->func.model, &tgt_info->fmodel, sizeof(tgt_info->fmodel));
 	tr->func.addr = (void *)tgt_info->tgt_addr;
+	tr->func.ftrace_managed = tr->ip != 0;
 out:
 	trampoline_unlock(tr);
 	return tr;
@@ -1583,7 +1577,6 @@ static int register_fentry_multi(struct bpf_trampoline *tr, struct bpf_tramp_ima
 	if (bpf_trampoline_use_jmp(tr->flags))
 		addr = ftrace_jmp_set(addr);
 
-	tr->func.ftrace_managed = true;
 	ftrace_hash_add(data->reg, data->entry, ip, addr);
 	tr->cur_image = im;
 	return 0;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 02/13] bpf: Factor out update_fentry_multi helper
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
  2026-08-09 15:00 ` [PATCH bpf-next 01/13] bpf: Initialize ftrace_managed in bpf_trampoline_get Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 03/13] bpf: Drop unnecessary ftrace_location() in update_fentry_multi() Leon Hwang
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

The tracing_multi register, unregister, and modify callbacks differ only
in the trampoline image and ftrace hash they use. Move their common
address setup, hash insertion, and current image update into
update_fentry_multi().

No functional changes intended.

Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 kernel/bpf/trampoline.c | 34 ++++++++++++++--------------------
 1 file changed, 14 insertions(+), 20 deletions(-)

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 8c2c1d1b9094..6e1898e1b200 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -1568,47 +1568,41 @@ static void ftrace_hash_add(struct ftrace_hash *hash, struct ftrace_func_entry *
 	add_ftrace_hash_entry(hash, entry);
 }
 
-static int register_fentry_multi(struct bpf_trampoline *tr, struct bpf_tramp_image *im, void *ptr)
+static int update_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags,
+			       struct bpf_tramp_image *im, struct ftrace_hash *hash,
+			       struct bpf_tracing_multi_data *data)
 {
-	unsigned long addr = (unsigned long) im->image;
+	unsigned long addr = (unsigned long)(im ? im->image : tr->cur_image->image);
 	unsigned long ip = ftrace_location(tr->ip);
-	struct bpf_tracing_multi_data *data = ptr;
 
 	if (bpf_trampoline_use_jmp(tr->flags))
 		addr = ftrace_jmp_set(addr);
 
-	ftrace_hash_add(data->reg, data->entry, ip, addr);
+	ftrace_hash_add(hash, data->entry, ip, addr);
 	tr->cur_image = im;
 	return 0;
 }
 
-static int unregister_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags, void *ptr)
+static int register_fentry_multi(struct bpf_trampoline *tr, struct bpf_tramp_image *im, void *ptr)
 {
-	unsigned long addr = (unsigned long) tr->cur_image->image;
-	unsigned long ip = ftrace_location(tr->ip);
 	struct bpf_tracing_multi_data *data = ptr;
 
-	if (bpf_trampoline_use_jmp(tr->flags))
-		addr = ftrace_jmp_set(addr);
+	return update_fentry_multi(tr, 0, im, data->reg, data);
+}
 
-	ftrace_hash_add(data->unreg, data->entry, ip, addr);
-	tr->cur_image = NULL;
-	return 0;
+static int unregister_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags, void *ptr)
+{
+	struct bpf_tracing_multi_data *data = ptr;
+
+	return update_fentry_multi(tr, orig_flags, NULL, data->unreg, data);
 }
 
 static int modify_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags, struct bpf_tramp_image *im,
 			       bool lock_direct_mutex, void *ptr)
 {
-	unsigned long addr = (unsigned long) im->image;
-	unsigned long ip = ftrace_location(tr->ip);
 	struct bpf_tracing_multi_data *data = ptr;
 
-	if (bpf_trampoline_use_jmp(tr->flags))
-		addr = ftrace_jmp_set(addr);
-
-	ftrace_hash_add(data->modify, data->entry, ip, addr);
-	tr->cur_image = im;
-	return 0;
+	return update_fentry_multi(tr, orig_flags, im, data->modify, data);
 }
 
 static const struct bpf_trampoline_ops trampoline_multi_ops = {
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 03/13] bpf: Drop unnecessary ftrace_location() in update_fentry_multi()
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
  2026-08-09 15:00 ` [PATCH bpf-next 01/13] bpf: Initialize ftrace_managed in bpf_trampoline_get Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 02/13] bpf: Factor out update_fentry_multi helper Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 04/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Since 'tr->ip' is already a 'ftrace_location()'-ed IP in
bpf_trampoline_lookup(), it is unnecessary to call ftrace_location() again
in update_fentry_multi().

Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 kernel/bpf/trampoline.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 6e1898e1b200..eddd259d3776 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -1573,12 +1573,11 @@ static int update_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags,
 			       struct bpf_tracing_multi_data *data)
 {
 	unsigned long addr = (unsigned long)(im ? im->image : tr->cur_image->image);
-	unsigned long ip = ftrace_location(tr->ip);
 
 	if (bpf_trampoline_use_jmp(tr->flags))
 		addr = ftrace_jmp_set(addr);
 
-	ftrace_hash_add(hash, data->entry, ip, addr);
+	ftrace_hash_add(hash, data->entry, tr->ip, addr);
 	tr->cur_image = im;
 	return 0;
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 04/13] bpf: Add tracing_multi link support for bpf progs
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (2 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 03/13] bpf: Drop unnecessary ftrace_location() in update_fentry_multi() Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-10 13:13   ` Jiri Olsa
  2026-08-09 15:01 ` [PATCH bpf-next 05/13] libbpf: " Leon Hwang
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Similar to the tracing_multi link support for kernel functions, add
support for bpf progs by reusing the 'ids' fields as BTF ids, and adding
fds array to indicate the target bpf prog. Then, for each pair of fd+id,
they will be the attach targets of tracing_multi link.

When attaching bpf prog, it must attaches to the target by text poke way.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 include/linux/bpf.h            |   3 +
 include/uapi/linux/bpf.h       |   2 +
 kernel/bpf/trampoline.c        |  45 ++++++++--
 kernel/bpf/verifier.c          |  22 ++---
 kernel/trace/bpf_trace.c       | 157 ++++++++++++++++++++++++++++-----
 tools/include/uapi/linux/bpf.h |   2 +
 6 files changed, 189 insertions(+), 42 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index b4a10c9878cf..9962228bca41 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1517,6 +1517,7 @@ void bpf_trampoline_put(struct bpf_trampoline *tr);
 int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_funcs);
 
 int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
+				u64 *keys, struct bpf_prog **progs,
 				struct bpf_tracing_multi_link *link);
 int bpf_trampoline_multi_detach(struct bpf_prog *prog,
 				struct bpf_tracing_multi_link *link);
@@ -1635,6 +1636,7 @@ static inline bool bpf_prog_has_trampoline(const struct bpf_prog *prog)
 	return false;
 }
 static inline int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
+					      u64 *keys, struct bpf_prog **progs,
 					      struct bpf_tracing_multi_link *link)
 {
 	return -ENOTSUPP;
@@ -2002,6 +2004,7 @@ struct bpf_tracing_multi_link {
 	struct bpf_tracing_multi_data data;
 	u64 *cookies;
 	struct bpf_tramp_node *fexits;
+	struct bpf_prog **progs;
 	int nodes_cnt;
 	struct bpf_tracing_multi_node nodes[] __counted_by(nodes_cnt);
 };
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index ffd96e8b920b..6fa93a6ff54f 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1888,6 +1888,8 @@ union bpf_attr {
 				__aligned_u64	ids;
 				__aligned_u64	cookies;
 				__u32		cnt;
+				__u32		:32;
+				__aligned_u64	fds;
 			} tracing_multi;
 		};
 	} link_create;
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index eddd259d3776..fc51ea2428be 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -1572,12 +1572,23 @@ static int update_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags,
 			       struct bpf_tramp_image *im, struct ftrace_hash *hash,
 			       struct bpf_tracing_multi_data *data)
 {
-	unsigned long addr = (unsigned long)(im ? im->image : tr->cur_image->image);
+	if (tr->func.ftrace_managed) {
+		unsigned long addr = (unsigned long)(im ? im->image : tr->cur_image->image);
 
-	if (bpf_trampoline_use_jmp(tr->flags))
-		addr = ftrace_jmp_set(addr);
+		if (bpf_trampoline_use_jmp(tr->flags))
+			addr = ftrace_jmp_set(addr);
+
+		ftrace_hash_add(hash, data->entry, tr->ip, addr);
+	} else {
+		void *old_addr = tr->cur_image ? tr->cur_image->image : NULL;
+		void *new_addr = im ? im->image : NULL;
+		int ret;
+
+		ret = bpf_trampoline_update_fentry(tr, orig_flags, old_addr, new_addr);
+		if (ret)
+			return ret;
+	}
 
-	ftrace_hash_add(hash, data->entry, tr->ip, addr);
 	tr->cur_image = im;
 	return 0;
 }
@@ -1627,6 +1638,18 @@ static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)
 
 static void bpf_trampoline_multi_attach_rollback(struct bpf_trampoline *tr)
 {
+	if (!tr->func.ftrace_managed) {
+		void *failed_addr = tr->cur_image ? tr->cur_image->image : NULL;
+		void *old_addr = tr->multi_attach.old_image ?
+				 tr->multi_attach.old_image->image : NULL;
+		u32 orig_flags = tr->flags;
+		int ret;
+
+		tr->flags = tr->multi_attach.old_flags;
+		ret = bpf_trampoline_update_fentry(tr, orig_flags, failed_addr, old_addr);
+		WARN_ONCE(ret, "bpf_trampoline_update_fentry failed: %d\n", ret);
+	}
+
 	if (tr->cur_image)
 		bpf_tramp_image_put(tr->cur_image);
 	tr->cur_image = tr->multi_attach.old_image;
@@ -1643,6 +1666,7 @@ static void bpf_trampoline_multi_attach_rollback(struct bpf_trampoline *tr)
 	for_each_mnode_cnt(mnode, link, link->nodes_cnt)
 
 int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
+				u64 *keys, struct bpf_prog **progs,
 				struct bpf_tracing_multi_link *link)
 {
 	struct bpf_tracing_multi_data *data = &link->data;
@@ -1651,18 +1675,18 @@ int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
 	struct bpf_tracing_multi_node *mnode;
 	struct bpf_trampoline *tr;
 	int i, err, rollback_cnt;
-	u64 key;
 
 	for_each_mnode(mnode, link) {
 		rollback_cnt = i;
 
-		err = bpf_check_attach_btf_id_multi(btf, prog, ids[i], &tgt_info);
+		if (progs)
+			err = bpf_check_attach_target(NULL, prog, progs[i], ids[i], &tgt_info);
+		else
+			err = bpf_check_attach_btf_id_multi(btf, prog, ids[i], &tgt_info);
 		if (err)
 			goto rollback_put;
 
-		key = bpf_trampoline_compute_key(NULL, btf, ids[i]);
-
-		tr = bpf_trampoline_get(key, &tgt_info);
+		tr = bpf_trampoline_get(keys[i], &tgt_info);
 		if (!tr) {
 			err = -ENOMEM;
 			goto rollback_put;
@@ -1691,6 +1715,9 @@ int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
 	for_each_mnode(mnode, link) {
 		bpf_trampoline_multi_attach_init(mnode->trampoline);
 
+		if (progs && progs[i]->aux->tail_call_reachable)
+			mnode->trampoline->flags |= BPF_TRAMP_F_TAIL_CALL_CTX;
+
 		data->entry = &mnode->entry;
 		err = __bpf_trampoline_link_prog(&mnode->node, mnode->trampoline, NULL,
 						 &trampoline_multi_ops, data);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index add3affc5703..887d5f654a3a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -19291,22 +19291,24 @@ int bpf_check_attach_target(struct bpf_verifier_log *log,
 			return -EOPNOTSUPP;
 		}
 
-		/*
-		 * *.multi programs don't need an address during program
-		 * verification, we just take the module ref if needed.
-		 */
-		if (is_tracing_multi_id(prog, btf_id)) {
+		if (tgt_prog) {
+			if (subprog == 0)
+				addr = (long) tgt_prog->bpf_func;
+			else
+				addr = (long) tgt_prog->aux->func[subprog]->bpf_func;
+		} else if (is_tracing_multi_id(prog, btf_id)) {
+			/*
+			 * *.multi programs don't need an address during program
+			 * verification, we just take the module ref if needed.
+			 * *.multi programs don't have tgt_prog during program
+			 * verification.
+			 */
 			if (btf_is_module(btf)) {
 				mod = btf_try_get_module(btf);
 				if (!mod)
 					return -ENOENT;
 			}
 			addr = 0;
-		} else if (tgt_prog) {
-			if (subprog == 0)
-				addr = (long) tgt_prog->bpf_func;
-			else
-				addr = (long) tgt_prog->aux->func[subprog]->bpf_func;
 		} else {
 			if (btf_is_module(btf)) {
 				mod = btf_try_get_module(btf);
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 891897f8a1b3..a805a5b73303 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3682,12 +3682,15 @@ __bpf_kfunc_end_defs();
 #if defined(CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS) && \
     defined(CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS)
 
+static void bpf_put_progs(struct bpf_prog **progs, int cnt);
+
 static void bpf_tracing_multi_link_release(struct bpf_link *link)
 {
 	struct bpf_tracing_multi_link *tr_link =
 		container_of(link, struct bpf_tracing_multi_link, link);
 
 	WARN_ON_ONCE(bpf_trampoline_multi_detach(link->prog, tr_link));
+	bpf_put_progs(tr_link->progs, tr_link->nodes_cnt);
 }
 
 static void bpf_tracing_multi_link_dealloc(struct bpf_link *link)
@@ -3695,6 +3698,7 @@ static void bpf_tracing_multi_link_dealloc(struct bpf_link *link)
 	struct bpf_tracing_multi_link *tr_link =
 		container_of(link, struct bpf_tracing_multi_link, link);
 
+	kvfree(tr_link->progs);
 	kvfree(tr_link->fexits);
 	kvfree(tr_link->cookies);
 	kvfree(tr_link);
@@ -3790,64 +3794,128 @@ static const struct bpf_link_ops bpf_tracing_multi_link_lops = {
 #endif
 };
 
-static int ids_cmp_r(const void *pa, const void *pb, const void *priv __maybe_unused)
+struct tracing_multi_sort_data {
+	u64 *keys;
+	struct bpf_prog **progs;
+	u32 *ids;
+	u64 *cookies;
+};
+
+static int keys_cmp_r(const void *pa, const void *pb, const void *priv __maybe_unused)
 {
-	u32 a = *(u32 *) pa;
-	u32 b = *(u32 *) pb;
+	const u64 *key_a = pa, *key_b = pb;
 
-	return (a > b) - (a < b);
+	return (*key_a > *key_b) - (*key_a < *key_b);
 }
 
-static void ids_swap_r(void *a, void *b, int size __maybe_unused,
-		       const void *priv __maybe_unused)
+static void keys_swap_r(void *a, void *b, int size __maybe_unused,
+			const void *priv __maybe_unused)
 {
+	const struct tracing_multi_sort_data *data = priv;
+	struct bpf_prog **prog_a, **prog_b;
 	u64 *cookie_a, *cookie_b, *cookies;
-	u32 *id_a = a, *id_b = b, *ids;
-	void **data = (void **) priv;
+	u64 *key_a = a, *key_b = b, *keys;
+	u32 *id_a, *id_b, *ids;
 
-	ids     = data[0];
-	cookies = data[1];
+	keys    = data->keys;
+	ids     = data->ids;
+	cookies = data->cookies;
+	id_a    = ids + (key_a - keys);
+	id_b    = ids + (key_b - keys);
+
+	if (data->progs) {
+		prog_a = data->progs + (key_a - keys);
+		prog_b = data->progs + (key_b - keys);
+		swap(*prog_a, *prog_b);
+	}
 
 	if (cookies) {
 		cookie_a = cookies + (id_a - ids);
 		cookie_b = cookies + (id_b - ids);
 		swap(*cookie_a, *cookie_b);
 	}
+
 	swap(*id_a, *id_b);
+	swap(*key_a, *key_b);
 }
 
-static int check_dup_ids(u32 *ids, u64 *cookies, u32 cnt)
+static int check_dup_keys(u64 *keys, struct bpf_prog **progs, u32 *ids,
+			  u64 *cookies, u32 cnt)
 {
-	void *data[2] = { ids, cookies };
-	int err = 0;
+	struct tracing_multi_sort_data data = {
+		.keys = keys,
+		.progs = progs,
+		.ids = ids,
+		.cookies = cookies,
+	};
 
 	/*
-	 * Sort ids array (together with cookies array if defined)
-	 * and check it for duplicates. The ids and cookies arrays
-	 * are left sorted.
+	 * Sort trampoline keys together with target programs, ids, and cookies,
+	 * then check for duplicates. The arrays are left sorted.
 	 */
-	sort_r_nonatomic(ids, cnt, sizeof(ids[0]), ids_cmp_r, ids_swap_r, data);
+	sort_r_nonatomic(keys, cnt, sizeof(keys[0]), keys_cmp_r, keys_swap_r, &data);
+
+	for (int i = 1; i < cnt; i++)
+		if (keys[i] == keys[i - 1])
+			return -EINVAL;
+
+	return 0;
+}
+
+static void bpf_put_progs(struct bpf_prog **progs, int cnt)
+{
+	int i;
+
+	if (!progs)
+		return;
+
+	for (i = 0; i < cnt; i++)
+		bpf_prog_put(progs[i]);
+}
+
+static int bpf_get_progs(struct bpf_prog **progs, int *fds, int cnt)
+{
+	int err, i, put_cnt = 0;
+	struct bpf_prog *prog;
+
+	for (i = 0; i < cnt; i++) {
+		prog = bpf_prog_get(fds[i]);
+		if (IS_ERR(prog)) {
+			err = PTR_ERR(prog);
+			goto error;
+		}
+
+		progs[i] = prog;
+		put_cnt++;
 
-	for (int i = 1; i < cnt; i++) {
-		if (ids[i] == ids[i - 1]) {
+		if (is_tracing_multi(prog->expected_attach_type)) {
 			err = -EINVAL;
-			break;
+			goto error;
 		}
 	}
+	return 0;
+
+error:
+	bpf_put_progs(progs, put_cnt);
 	return err;
 }
 
 int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
 {
 	struct bpf_tracing_multi_link *link = NULL;
+	struct btf *btf = prog->aux->attach_btf;
 	struct bpf_tramp_node *fexits = NULL;
 	struct bpf_link_primer link_primer;
+	u64 *cookies = NULL, *keys = NULL;
+	struct bpf_prog **progs = NULL;
 	u32 cnt, *ids = NULL;
 	u64 __user *ucookies;
-	u64 *cookies = NULL;
 	u32 __user *uids;
+	int __user *ufds;
+	int *fds = NULL;
 	int err;
 
+	ufds = u64_to_user_ptr(attr->link_create.tracing_multi.fds);
 	uids = u64_to_user_ptr(attr->link_create.tracing_multi.ids);
 	cnt = attr->link_create.tracing_multi.cnt;
 
@@ -3867,6 +3935,32 @@ int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
 		goto error;
 	}
 
+	if (ufds) {
+		fds = kvmalloc_objs(*fds, cnt);
+		if (!fds) {
+			err = -ENOMEM;
+			goto error;
+		}
+
+		if (copy_from_user(fds, ufds, cnt * sizeof(*fds))) {
+			err = -EFAULT;
+			goto error;
+
+		}
+
+		progs = kvmalloc_objs(*progs, cnt);
+		if (!progs) {
+			err = -ENOMEM;
+			goto error;
+		}
+
+		err = bpf_get_progs(progs, fds, cnt);
+		if (err) {
+			cnt = 0;
+			goto error;
+		}
+	}
+
 	ucookies = u64_to_user_ptr(attr->link_create.tracing_multi.cookies);
 	if (ucookies) {
 		cookies = kvmalloc_objs(*cookies, cnt);
@@ -3880,7 +3974,16 @@ int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
 		}
 	}
 
-	err = check_dup_ids(ids, cookies, cnt);
+	keys = kvmalloc_objs(*keys, cnt);
+	if (!keys) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	for (int i = 0; i < cnt; i++)
+		keys[i] = bpf_trampoline_compute_key(progs ? progs[i] : NULL, btf, ids[i]);
+
+	err = check_dup_keys(keys, progs, ids, cookies, cnt);
 	if (err)
 		goto error;
 
@@ -3908,19 +4011,27 @@ int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)
 	link->nodes_cnt = cnt;
 	link->cookies = cookies;
 	link->fexits = fexits;
+	link->progs = progs;
 
-	err = bpf_trampoline_multi_attach(prog, ids, link);
+	err = bpf_trampoline_multi_attach(prog, ids, keys, progs, link);
 	kvfree(ids);
+	kvfree(keys);
+	kvfree(fds);
 	if (err) {
+		bpf_put_progs(progs, cnt);
 		bpf_link_cleanup(&link_primer);
 		return err;
 	}
 	return bpf_link_settle(&link_primer);
 
 error:
+	bpf_put_progs(progs, cnt);
 	kvfree(fexits);
 	kvfree(cookies);
 	kvfree(ids);
+	kvfree(keys);
+	kvfree(fds);
+	kvfree(progs);
 	kvfree(link);
 	return err;
 }
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index ffd96e8b920b..6fa93a6ff54f 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1888,6 +1888,8 @@ union bpf_attr {
 				__aligned_u64	ids;
 				__aligned_u64	cookies;
 				__u32		cnt;
+				__u32		:32;
+				__aligned_u64	fds;
 			} tracing_multi;
 		};
 	} link_create;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 05/13] libbpf: Add tracing_multi link support for bpf progs
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (3 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 04/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 06/13] bpf: Add tracing_multi link fdinfo " Leon Hwang
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

By design, a tracing_multi link does not support both kernel functions
and bpf progs at the same time.

In libbpf, the bpf_tracing_multi_opts options provides two new fields for
tracing bpf progs:

* 'fds': the fd array of the target bpf progs. One fd must be provided
         multiple times for multiple targets.
* 'funcs': the function name array of the target bpf progs.

The 'cnt' field indicates the size of both 'fds' and 'funcs' arrays.

The 'funcs' will be translated to 'ids' by 'libbpf_find_prog_btf_id()'.

At last, 'fds'+'ids' will be passed to kernel.

Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 tools/lib/bpf/bpf.c    |  1 +
 tools/lib/bpf/bpf.h    |  2 ++
 tools/lib/bpf/libbpf.c | 37 +++++++++++++++++++++++++++++++++----
 tools/lib/bpf/libbpf.h |  4 +++-
 4 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 96819c082c77..a20585174903 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -852,6 +852,7 @@ int bpf_link_create(int prog_fd, int target_fd,
 		attr.link_create.tracing_multi.ids = ptr_to_u64(OPTS_GET(opts, tracing_multi.ids, 0));
 		attr.link_create.tracing_multi.cookies = ptr_to_u64(OPTS_GET(opts, tracing_multi.cookies, 0));
 		attr.link_create.tracing_multi.cnt = OPTS_GET(opts, tracing_multi.cnt, 0);
+		attr.link_create.tracing_multi.fds = ptr_to_u64(OPTS_GET(opts, tracing_multi.fds, 0));
 		if (!OPTS_ZEROED(opts, tracing_multi))
 			return libbpf_err(-EINVAL);
 		break;
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 7534a593edae..63961db29b74 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -474,6 +474,8 @@ struct bpf_link_create_opts {
 			const __u32 *ids;
 			const __u64 *cookies;
 			__u32 cnt;
+			__u32 :32;
+			const int *fds;
 		} tracing_multi;
 	};
 	size_t :0;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 514e4e9daa82..d698a64ff800 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -12687,10 +12687,12 @@ bpf_program__attach_tracing_multi(const struct bpf_program *prog, const char *pa
 {
 	LIBBPF_OPTS(bpf_link_create_opts, lopts);
 	int prog_fd, link_fd, err, cnt;
+	struct bpf_link *link = NULL;
 	__u32 *free_ids = NULL;
-	struct bpf_link *link;
 	const __u64 *cookies;
+	const char **funcs;
 	const __u32 *ids;
+	const int *fds;
 
 	if (!OPTS_VALID(opts, bpf_tracing_multi_opts))
 		return libbpf_err_ptr(-EINVAL);
@@ -12705,12 +12707,22 @@ bpf_program__attach_tracing_multi(const struct bpf_program *prog, const char *pa
 	cnt = OPTS_GET(opts, cnt, 0);
 	ids = OPTS_GET(opts, ids, NULL);
 	cookies = OPTS_GET(opts, cookies, NULL);
+	fds = OPTS_GET(opts, fds, NULL);
+	funcs = OPTS_GET(opts, funcs, NULL);
 
-	if (!!ids != !!cnt)
+	if (ids && fds)
+		return libbpf_err_ptr(-EINVAL);
+	if (!!fds != !!funcs)
+		return libbpf_err_ptr(-EINVAL);
+	if (fds && !cnt)
 		return libbpf_err_ptr(-EINVAL);
-	if (pattern && (ids || cookies))
+	if (ids && !cnt)
 		return libbpf_err_ptr(-EINVAL);
-	if (!pattern && !ids)
+	if (!fds && !ids && cnt)
+		return libbpf_err_ptr(-EINVAL);
+	if (pattern && (fds || ids || cookies))
+		return libbpf_err_ptr(-EINVAL);
+	if (!pattern && !ids && !fds)
 		return libbpf_err_ptr(-EINVAL);
 
 	if (pattern) {
@@ -12720,11 +12732,28 @@ bpf_program__attach_tracing_multi(const struct bpf_program *prog, const char *pa
 		if (cnt == 0)
 			return libbpf_err_ptr(-EINVAL);
 		ids = (const __u32 *) free_ids;
+	} else if (fds) {
+		size_t cap = 0;
+		int i;
+
+		err = libbpf_ensure_mem((void **) &free_ids, &cap, sizeof(*free_ids), cnt);
+		if (err)
+			return libbpf_err_ptr(err);
+
+		for (i = 0; i < cnt; i++) {
+			err = libbpf_find_prog_btf_id(funcs[i], fds[i], prog->obj->token_fd);
+			if (err < 0)
+				goto error;
+
+			free_ids[i] = err;
+		}
+		ids = (const __u32 *) free_ids;
 	}
 
 	lopts.tracing_multi.ids = ids;
 	lopts.tracing_multi.cookies = cookies;
 	lopts.tracing_multi.cnt = cnt;
+	lopts.tracing_multi.fds = fds;
 
 	link = calloc(1, sizeof(*link));
 	if (!link) {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b965ad571540..7b2cfa1ad572 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -732,10 +732,12 @@ struct bpf_tracing_multi_opts {
 	const __u32 *ids;
 	const __u64 *cookies;
 	size_t cnt;
+	const int *fds;
+	const char **funcs;
 	size_t :0;
 };
 
-#define bpf_tracing_multi_opts__last_field cnt
+#define bpf_tracing_multi_opts__last_field funcs
 
 LIBBPF_API struct bpf_link *
 bpf_program__attach_tracing_multi(const struct bpf_program *prog, const char *pattern,
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 06/13] bpf: Add tracing_multi link fdinfo support for bpf progs
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (4 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 05/13] libbpf: " Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 16:20   ` bot+bpf-ci
  2026-08-09 15:01 ` [PATCH bpf-next 07/13] bpf: Add tracing_multi link info " Leon Hwang
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Show the target bpf prog info in the fdinfo.

$ cat /proc/self/fdinfo/13
pos:	0
flags:	02000000
mnt_id:	18
ino:	1045
link_type:	tracing_multi
link_id:	9
prog_tag:	9a7f9e3ab6f138ab
prog_id:	25
attach_type:	61
cnt:	2
prog-id	 btf-id	 cookie	 func
21	 4	 16706819	 target_1
22	 5	 16706819	 target_2

Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 kernel/trace/bpf_trace.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index a805a5b73303..80928401830c 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3759,26 +3759,46 @@ static int bpf_tracing_multi_link_fill_link_info(const struct bpf_link *link,
 }
 
 #ifdef CONFIG_PROC_FS
+static const char *bpf_prog_func_name(struct bpf_prog *prog, u32 btf_id)
+{
+	const struct btf *btf = prog->aux->btf;
+	const struct btf_type *t;
+
+	t = btf_type_by_id(btf, btf_id);
+	if (!t || !btf_type_is_func(t))
+		return "";
+
+	return btf_name_by_offset(btf, t->name_off);
+}
+
 static void bpf_tracing_multi_show_fdinfo(const struct bpf_link *link,
 					  struct seq_file *seq)
 {
 	struct bpf_tracing_multi_link *tr_link =
 		container_of(link, struct bpf_tracing_multi_link, link);
 	bool has_cookies = !!tr_link->cookies;
+	bool has_progs = !!tr_link->progs;
 
 	seq_printf(seq, "attach_type:\t%u\n", tr_link->link.attach_type);
 	seq_printf(seq, "cnt:\t%u\n", tr_link->nodes_cnt);
 
-	seq_printf(seq, "%s\t %s\t %s\t %s\n", "obj-id", "btf-id", "cookie", "func");
+	seq_printf(seq, "%s\t %s\t %s\t %s\n", has_progs ? "prog-id" : "obj-id", "btf-id", "cookie",
+		   "func");
 	for (int i = 0; i < tr_link->nodes_cnt; i++) {
 		struct bpf_tracing_multi_node *mnode = &tr_link->nodes[i];
+		u64 cookie = has_cookies ? tr_link->cookies[i] : 0;
 		u32 btf_id, obj_id;
 
 		bpf_trampoline_unpack_key(mnode->trampoline->key, &obj_id, &btf_id);
-		seq_printf(seq, "%u\t %u\t %llu\t %pS\n",
-			   obj_id, btf_id,
-			   has_cookies ? tr_link->cookies[i] : 0,
-			   (void *) mnode->trampoline->ip);
+		btf_id = has_progs ? (u32) mnode->trampoline->key : btf_id;
+		if (has_progs)
+			seq_printf(seq, "%u\t %u\t %llu\t %s\n",
+				   obj_id, btf_id, cookie,
+				   bpf_prog_func_name(tr_link->progs[i], btf_id));
+		else
+			seq_printf(seq, "%u\t %u\t %llu\t %pS\n",
+				   obj_id, btf_id, cookie,
+				   (void *) mnode->trampoline->ip);
 
 		cond_resched();
 	}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 07/13] bpf: Add tracing_multi link info support for bpf progs
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (5 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 06/13] bpf: Add tracing_multi link fdinfo " Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 08/13] selftests/bpf: Add tracing_multi bpf prog attach test Leon Hwang
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Show bpf prog info in the link info.

$ bpftool link
9: tracing_multi  prog 25
	attach_type trace_fsession_multi  btf_obj_id 1  count 2
	prog_id          func_btf_id      addr             cookie           func [module]
	21               4                ffffffffc080064c 16706819         target_1 [bpf]
	22               5                ffffffffc08006d4 16706819         target_2 [bpf]
	pids test_progs(98)

Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 include/uapi/linux/bpf.h       |  4 ++-
 kernel/trace/bpf_trace.c       | 25 ++++++++++----
 tools/bpf/bpftool/link.c       | 59 +++++++++++++++++++++++++++-------
 tools/include/uapi/linux/bpf.h |  4 ++-
 4 files changed, 71 insertions(+), 21 deletions(-)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6fa93a6ff54f..e0c8ea5ed2c8 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -6890,10 +6890,12 @@ struct bpf_link_info {
 			__u32 attach_type;
 			__u32 count; /* in/out: tracing_multi target count */
 			__u32 btf_obj_id;
-			__u32 :32;
+			__u32 tgt_progs:1;
+			__u32 :31;
 			__aligned_u64 ids;
 			__aligned_u64 addrs;
 			__aligned_u64 cookies;
+			__aligned_u64 func_btf_ids;
 		} tracing_multi;
 		struct {
 			__u32 type; /* enum bpf_perf_event_type */
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 80928401830c..d998e7ea563d 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3707,15 +3707,16 @@ static void bpf_tracing_multi_link_dealloc(struct bpf_link *link)
 static int bpf_tracing_multi_link_fill_link_info(const struct bpf_link *link,
 						 struct bpf_link_info *info)
 {
+	u32 __user *fids = u64_to_user_ptr(info->tracing_multi.func_btf_ids);
 	u64 __user *ucookies = u64_to_user_ptr(info->tracing_multi.cookies);
 	u64 __user *uaddrs = u64_to_user_ptr(info->tracing_multi.addrs);
 	u32 __user *uids = u64_to_user_ptr(info->tracing_multi.ids);
 	struct bpf_tracing_multi_link *tr_link;
 	u32 ucount = info->tracing_multi.count;
-	bool has_cookies, show_addrs;
+	bool has_cookies, show_addrs, has_progs;
 	int err = 0;
 
-	if ((uids || ucookies || uaddrs) && !ucount)
+	if ((uids || ucookies || uaddrs || fids) && !ucount)
 		return -EINVAL;
 
 	tr_link = container_of(link, struct bpf_tracing_multi_link, link);
@@ -3723,9 +3724,12 @@ static int bpf_tracing_multi_link_fill_link_info(const struct bpf_link *link,
 	info->tracing_multi.attach_type = tr_link->link.attach_type;
 	info->tracing_multi.count = tr_link->nodes_cnt;
 	info->tracing_multi.btf_obj_id = btf_obj_id(tr_link->link.prog->aux->attach_btf);
+	info->tracing_multi.tgt_progs = has_progs = !!tr_link->progs;
 
-	if (!uids && !ucookies && !uaddrs)
+	if (!uids && !ucookies && !uaddrs && !fids)
 		return 0;
+	if (has_progs != !!fids)
+		return -EINVAL;
 
 	if (ucount < tr_link->nodes_cnt)
 		err = -ENOSPC;
@@ -3737,20 +3741,27 @@ static int bpf_tracing_multi_link_fill_link_info(const struct bpf_link *link,
 
 	for (int i = 0; i < ucount; i++) {
 		struct bpf_tracing_multi_node *mnode = &tr_link->nodes[i];
+		u32 prog_id, btf_id;
 		u64 addr, cookie;
-		u32 id;
 
-		bpf_trampoline_unpack_key(mnode->trampoline->key, NULL, &id);
+		bpf_trampoline_unpack_key(mnode->trampoline->key, &prog_id, &btf_id);
 
-		addr = show_addrs ? mnode->trampoline->ip : 0;
+		if (!show_addrs)
+			addr = 0;
+		else if (has_progs)
+			addr = (u64) mnode->trampoline->func.addr;
+		else
+			addr = mnode->trampoline->ip;
 		cookie = has_cookies ? tr_link->cookies[i] : 0;
 
-		if (uids && put_user(id, uids + i))
+		if (uids && put_user(has_progs ? prog_id : btf_id, uids + i))
 			return -EFAULT;
 		if (uaddrs && put_user(addr, uaddrs + i))
 			return -EFAULT;
 		if (ucookies && put_user(cookie, ucookies + i))
 			return -EFAULT;
+		if (has_progs && put_user((u32) mnode->trampoline->key, fids + i))
+			return -EFAULT;
 
 		cond_resched();
 	}
diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c
index 088d1d206065..d5c5ec433350 100644
--- a/tools/bpf/bpftool/link.c
+++ b/tools/bpf/bpftool/link.c
@@ -422,12 +422,30 @@ show_uprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
 	jsonw_end_array(json_wtr);
 }
 
+#define BPF_PROG_PREFIX		"bpf_prog_"
+#define BPF_PROG_PREFIX_LEN	(sizeof(BPF_PROG_PREFIX) - 1)
+
+static const char *
+sym_name_trim_prefix(struct kernel_sym *sym, bool is_prog)
+{
+	int prefix_len;
+
+	if (!is_prog)
+		return sym->name;
+
+	/* Ref kernel/bpf/core.c::bpf_prog_ksym_set_name() */
+	prefix_len  = BPF_PROG_PREFIX_LEN;
+	prefix_len += BPF_TAG_SIZE * 2;
+	prefix_len += 1;			/* skip the '_' */
+	return sym->name + prefix_len;
+}
+
 static void
 show_tracing_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
 {
-	bool is_ibt_enabled = is_x86_ibt_enabled(), show_symbol;
+	bool is_ibt_enabled = is_x86_ibt_enabled(), show_symbol, tgt_progs;
 	__u64 *addrs, *cookies;
-	__u32 i, *ids;
+	__u32 i, *ids, *fids;
 
 	if (!dd.sym_count)
 		kernel_syms_load(&dd);
@@ -443,6 +461,8 @@ show_tracing_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
 	ids = u64_to_u32_arr(info->tracing_multi.ids);
 	addrs = u64_to_arr(info->tracing_multi.addrs);
 	cookies = u64_to_arr(info->tracing_multi.cookies);
+	fids = u64_to_u32_arr(info->tracing_multi.func_btf_ids);
+	tgt_progs = info->tracing_multi.tgt_progs;
 
 	for (i = 0; i < info->tracing_multi.count; i++) {
 		struct kernel_sym *sym;
@@ -451,10 +471,12 @@ show_tracing_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
 		sym = show_symbol ? find_kernel_sym_by_addr(addr, is_ibt_enabled) : NULL;
 
 		jsonw_start_object(wtr);
-		jsonw_uint_field(wtr, "id", ids[i]);
+		jsonw_uint_field(wtr, tgt_progs ? "prog_id" : "id", ids[i]);
+		if (tgt_progs)
+			jsonw_uint_field(wtr, "func_btf_id", fids[i]);
 		jsonw_uint_field(wtr, "addr", addr);
 		if (sym) {
-			jsonw_string_field(wtr, "func", sym->name);
+			jsonw_string_field(wtr, "func", sym_name_trim_prefix(sym, tgt_progs));
 			if (sym->module[0] == '\0') {
 				jsonw_name(wtr, "module");
 				jsonw_null(wtr);
@@ -903,9 +925,9 @@ static void show_uprobe_multi_plain(struct bpf_link_info *info)
 
 static void show_tracing_multi_plain(struct bpf_link_info *info)
 {
-	bool is_ibt_enabled = is_x86_ibt_enabled(), show_symbol;
+	bool is_ibt_enabled = is_x86_ibt_enabled(), show_symbol, tgt_progs;
 	__u64 *addrs, *cookies;
-	__u32 i, *ids;
+	__u32 i, *ids, *fids;
 
 	if (!info->tracing_multi.count)
 		return;
@@ -919,12 +941,18 @@ static void show_tracing_multi_plain(struct bpf_link_info *info)
 	printf("btf_obj_id %u  ", info->tracing_multi.btf_obj_id);
 	printf("count %u  ", info->tracing_multi.count);
 
-	printf("\n\t%-16s %-16s %-16s %s",
-	       "btf_id", "addr", "cookie", "func [module]");
+	tgt_progs = info->tracing_multi.tgt_progs;
+	if (tgt_progs)
+		printf("\n\t%-16s %-16s %-16s %-16s %s",
+		       "prog_id", "func_btf_id", "addr", "cookie", "func [module]");
+	else
+		printf("\n\t%-16s %-16s %-16s %s",
+		       "btf_id", "addr", "cookie", "func [module]");
 
 	ids = u64_to_u32_arr(info->tracing_multi.ids);
 	addrs = u64_to_arr(info->tracing_multi.addrs);
 	cookies = u64_to_arr(info->tracing_multi.cookies);
+	fids = u64_to_u32_arr(info->tracing_multi.func_btf_ids);
 
 	for (i = 0; i < info->tracing_multi.count; i++) {
 		__u64 addr = addrs[i];
@@ -932,9 +960,12 @@ static void show_tracing_multi_plain(struct bpf_link_info *info)
 
 		sym = show_symbol ? find_kernel_sym_by_addr(addr, is_ibt_enabled) : NULL;
 
-		printf("\n\t%-16u %016llx %-16llu", ids[i], addr, cookies[i]);
+		if (tgt_progs)
+			printf("\n\t%-16u %-16u %016llx %-16llu", ids[i], fids[i], addr, cookies[i]);
+		else
+			printf("\n\t%-16u %016llx %-16llu", ids[i], addr, cookies[i]);
 		if (sym) {
-			printf(" %s", sym->name);
+			printf(" %s", sym_name_trim_prefix(sym, tgt_progs));
 			if (sym->module[0] != '\0')
 				printf(" [%s]", sym->module);
 		}
@@ -1140,7 +1171,7 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
 static int do_show_link(int fd)
 {
 	__u64 *ref_ctr_offsets = NULL, *offsets = NULL, *cookies = NULL;
-	__u32 *ids = NULL;
+	__u32 *ids = NULL, *func_btf_ids = NULL;
 	struct bpf_link_info info;
 	__u32 len = sizeof(info);
 	char path_buf[PATH_MAX];
@@ -1232,9 +1263,11 @@ static int do_show_link(int fd)
 			ids = calloc(count, sizeof(__u32));
 			addrs = calloc(count, sizeof(__u64));
 			cookies = calloc(count, sizeof(__u64));
-			if (!ids || !addrs || !cookies) {
+			func_btf_ids = info.tracing_multi.tgt_progs ? calloc(count, sizeof(__u32)) : NULL;
+			if (!ids || !addrs || !cookies || (info.tracing_multi.tgt_progs && !func_btf_ids)) {
 				p_err("mem alloc failed");
 				close(fd);
+				free(func_btf_ids);
 				free(cookies);
 				free(addrs);
 				free(ids);
@@ -1243,6 +1276,7 @@ static int do_show_link(int fd)
 			info.tracing_multi.ids = ptr_to_u64(ids);
 			info.tracing_multi.addrs = ptr_to_u64(addrs);
 			info.tracing_multi.cookies = ptr_to_u64(cookies);
+			info.tracing_multi.func_btf_ids = ptr_to_u64(func_btf_ids);
 			goto again;
 		}
 	}
@@ -1282,6 +1316,7 @@ static int do_show_link(int fd)
 		show_link_close_plain(fd, &info);
 
 	free(ref_ctr_offsets);
+	free(func_btf_ids);
 	free(cookies);
 	free(offsets);
 	free(addrs);
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 6fa93a6ff54f..e0c8ea5ed2c8 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -6890,10 +6890,12 @@ struct bpf_link_info {
 			__u32 attach_type;
 			__u32 count; /* in/out: tracing_multi target count */
 			__u32 btf_obj_id;
-			__u32 :32;
+			__u32 tgt_progs:1;
+			__u32 :31;
 			__aligned_u64 ids;
 			__aligned_u64 addrs;
 			__aligned_u64 cookies;
+			__aligned_u64 func_btf_ids;
 		} tracing_multi;
 		struct {
 			__u32 type; /* enum bpf_perf_event_type */
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 08/13] selftests/bpf: Add tracing_multi bpf prog attach test
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (6 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 07/13] bpf: Add tracing_multi link info " Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 09/13] selftests/bpf: Add tracing_multi bpf prog attach failure tests Leon Hwang
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Attach to two bpf progs with fentry.multi, fexit.multi, and fsession.multi
progs.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 .../selftests/bpf/prog_tests/tracing_multi.c  | 70 +++++++++++++++++++
 .../selftests/bpf/progs/tracing_multi_bpf.c   | 52 ++++++++++++++
 2 files changed, 122 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/tracing_multi_bpf.c

diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
index 0aa9532a05cf..5ecb751af9be 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
@@ -12,6 +12,7 @@
 #include "tracing_multi_verifier.skel.h"
 #include "tracing_multi_bench.skel.h"
 #include "tracing_multi_rollback.skel.h"
+#include "tracing_multi_bpf.skel.h"
 #include "trace_helpers.h"
 
 static __u64 bpf_fentry_test_cookies[] = {
@@ -288,6 +289,73 @@ static void test_link_api_ids(bool test_cookies)
 	free(ids);
 }
 
+static int run_bpf_target(struct bpf_program *prog, __u32 retval, const char *name)
+{
+	LIBBPF_OPTS(bpf_test_run_opts, topts);
+	int err;
+
+	err = bpf_prog_test_run_opts(bpf_program__fd(prog), &topts);
+	if (!ASSERT_OK(err, name))
+		return err;
+
+	if (!ASSERT_EQ(topts.retval, retval, name))
+		return -EINVAL;
+
+	return 0;
+}
+
+static void test_link_api_bpf_prog(void)
+{
+	const char *funcs[] = { "target_1", "target_2" };
+	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
+	struct tracing_multi_bpf *skel = NULL;
+	int fds[ARRAY_SIZE(funcs)];
+	struct bpf_link *link;
+
+	skel = tracing_multi_bpf__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "tracing_multi_bpf__open_and_load"))
+		return;
+
+	fds[0] = bpf_program__fd(skel->progs.target_1);
+	fds[1] = bpf_program__fd(skel->progs.target_2);
+
+	opts.fds = fds;
+	opts.funcs = funcs;
+	opts.cnt = ARRAY_SIZE(fds);
+
+	link = bpf_program__attach_tracing_multi(skel->progs.test_fentry, NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "attach_fentry"))
+		goto cleanup;
+	skel->links.test_fentry = link;
+
+	link = bpf_program__attach_tracing_multi(skel->progs.test_fexit, NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "attach_fexit"))
+		goto cleanup;
+	skel->links.test_fexit = link;
+
+	link = bpf_program__attach_tracing_multi(skel->progs.test_fsession, NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "attach_fsession"))
+		goto cleanup;
+	skel->links.test_fsession = link;
+
+	if (run_bpf_target(skel->progs.target_1, 1, "target_1"))
+		goto cleanup;
+	if (run_bpf_target(skel->progs.target_2, 2, "target_2"))
+		goto cleanup;
+
+	ASSERT_EQ(skel->bss->target_1_result, 1, "target_1_result");
+	ASSERT_EQ(skel->bss->target_2_result, 1, "target_2_result");
+	ASSERT_EQ(skel->bss->test_result_fentry, ARRAY_SIZE(funcs), "test_result_fentry");
+	ASSERT_EQ(skel->bss->test_result_fexit, ARRAY_SIZE(funcs), "test_result_fexit");
+	ASSERT_EQ(skel->bss->test_result_fsession_entry, ARRAY_SIZE(funcs),
+		  "test_result_fsession_entry");
+	ASSERT_EQ(skel->bss->test_result_fsession_exit, ARRAY_SIZE(funcs),
+		  "test_result_fsession_exit");
+
+cleanup:
+	tracing_multi_bpf__destroy(skel);
+}
+
 static void test_module_skel_api(void)
 {
 	struct tracing_multi_module *skel = NULL;
@@ -1026,4 +1094,6 @@ void test_tracing_multi_test(void)
 	RUN_TESTS(tracing_multi_verifier);
 	if (test__start_subtest("fentry_after_multi"))
 		test_fentry_after_multi();
+	if (test__start_subtest("link_api_bpf_prog"))
+		test_link_api_bpf_prog();
 }
diff --git a/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c b/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c
new file mode 100644
index 000000000000..3e02602558ad
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+__u64 test_result_fentry;
+__u64 test_result_fexit;
+__u64 test_result_fsession_entry;
+__u64 test_result_fsession_exit;
+__u64 target_1_result;
+__u64 target_2_result;
+
+SEC("syscall")
+int target_1(void *ctx)
+{
+	target_1_result++;
+	return 1;
+}
+
+SEC("syscall")
+int target_2(void *ctx)
+{
+	target_2_result++;
+	return 2;
+}
+
+SEC("fentry.multi")
+int BPF_PROG(test_fentry)
+{
+	test_result_fentry++;
+	return 0;
+}
+
+SEC("fexit.multi")
+int BPF_PROG(test_fexit)
+{
+	test_result_fexit++;
+	return 0;
+}
+
+SEC("fsession.multi")
+int BPF_PROG(test_fsession)
+{
+	if (bpf_session_is_return(ctx))
+		test_result_fsession_exit++;
+	else
+		test_result_fsession_entry++;
+
+	return 0;
+}
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 09/13] selftests/bpf: Add tracing_multi bpf prog attach failure tests
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (7 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 08/13] selftests/bpf: Add tracing_multi bpf prog attach test Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 10/13] selftests/bpf: Add tracing_multi bpf prog cookie test Leon Hwang
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Tests against two libbpf failures and two kernel failures:

* libbpf: 'ids' + 'fds'.
* libbpf: 'pattern' + 'fds'.
* kernel: 'fds' + invalid 'ids'.
* kernel: duplicate targets via different fd for the same bpf prog + func.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 .../selftests/bpf/prog_tests/tracing_multi.c  | 100 ++++++++++++++++++
 1 file changed, 100 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
index 5ecb751af9be..5cf11707eaf8 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
@@ -730,6 +730,104 @@ static void test_attach_api_fails(void)
 	free(ids2);
 }
 
+static void test_attach_api_bpf_prog_fails(void)
+{
+	static const char * const kfuncs[] = {
+		"bpf_fentry_test1",
+	};
+	const char *bpf_funcs[] = { "target_1" };
+	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
+	LIBBPF_OPTS(bpf_link_create_opts, lopts);
+	struct tracing_multi_bpf *skel = NULL;
+	int fd, target_id, token_fd;
+	int fds[2], dup_fd = -1;
+	struct bpf_link *link;
+	__u32 target_ids[2];
+	__u32 *ids = NULL;
+	long err;
+
+	skel = tracing_multi_bpf__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "tracing_multi_bpf__open_and_load"))
+		return;
+
+	ids = get_ids(kfuncs, ARRAY_SIZE(kfuncs), NULL);
+	if (!ASSERT_OK_PTR(ids, "get_ids"))
+		goto cleanup;
+
+	fds[0] = bpf_program__fd(skel->progs.target_1);
+
+	/* fail#1 (libbpf) kernel ids and BPF prog fds */
+	LIBBPF_OPTS_RESET(opts,
+		.ids = ids,
+		.fds = fds,
+		.funcs = bpf_funcs,
+		.cnt = ARRAY_SIZE(bpf_funcs),
+	);
+
+	link = bpf_program__attach_tracing_multi(skel->progs.test_fentry, NULL, &opts);
+	err = libbpf_get_error(link);
+	if (!ASSERT_EQ(err, -EINVAL, "libbpf_ids_fds")) {
+		if (!err)
+			bpf_link__destroy(link);
+		goto cleanup;
+	}
+
+	/* fail#2 (libbpf) kernel pattern and BPF prog fds */
+	LIBBPF_OPTS_RESET(opts,
+		.fds = fds,
+		.funcs = bpf_funcs,
+		.cnt = ARRAY_SIZE(bpf_funcs),
+	);
+
+	link = bpf_program__attach_tracing_multi(skel->progs.test_fentry,
+						"bpf_fentry_test*", &opts);
+	err = libbpf_get_error(link);
+	if (!ASSERT_EQ(err, -EINVAL, "libbpf_pattern_fds")) {
+		if (!err)
+			bpf_link__destroy(link);
+		goto cleanup;
+	}
+
+	/* fail#3 (kernel) kernel id with BPF prog fd */
+	lopts.tracing_multi.ids = ids;
+	lopts.tracing_multi.fds = fds;
+	lopts.tracing_multi.cnt = ARRAY_SIZE(bpf_funcs);
+	fd = bpf_link_create(bpf_program__fd(skel->progs.test_fentry), 0,
+			     BPF_TRACE_FENTRY_MULTI, &lopts);
+	ASSERT_EQ(fd, -EINVAL, "kernel_id_bpf_fd");
+	if (fd >= 0)
+		close(fd);
+
+	/* fail#4 (kernel) duplicate target through different fds */
+	token_fd = bpf_object__token_fd(skel->obj);
+	token_fd = token_fd < 0 ? 0 : token_fd;
+	target_id = libbpf_find_prog_btf_id("target_1", fds[0], token_fd);
+	if (!ASSERT_GT(target_id, 0, "target_1_btf_id"))
+		goto cleanup;
+
+	dup_fd = dup(fds[0]);
+	if (!ASSERT_GE(dup_fd, 0, "dup_target_fd"))
+		goto cleanup;
+
+	fds[1] = dup_fd;
+	target_ids[0] = target_id;
+	target_ids[1] = target_id;
+	lopts.tracing_multi.ids = target_ids;
+	lopts.tracing_multi.fds = fds;
+	lopts.tracing_multi.cnt = ARRAY_SIZE(fds);
+	fd = bpf_link_create(bpf_program__fd(skel->progs.test_fentry), 0,
+			     BPF_TRACE_FENTRY_MULTI, &lopts);
+	ASSERT_EQ(fd, -EINVAL, "duplicate_bpf_target");
+	if (fd >= 0)
+		close(fd);
+
+cleanup:
+	if (dup_fd >= 0)
+		close(dup_fd);
+	tracing_multi_bpf__destroy(skel);
+	free(ids);
+}
+
 void serial_test_tracing_multi_bench_attach(void)
 {
 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
@@ -1096,4 +1194,6 @@ void test_tracing_multi_test(void)
 		test_fentry_after_multi();
 	if (test__start_subtest("link_api_bpf_prog"))
 		test_link_api_bpf_prog();
+	if (test__start_subtest("attach_api_bpf_prog_fails"))
+		test_attach_api_bpf_prog_fails();
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 10/13] selftests/bpf: Add tracing_multi bpf prog cookie test
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (8 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 09/13] selftests/bpf: Add tracing_multi bpf prog attach failure tests Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 16:20   ` bot+bpf-ci
  2026-08-09 15:01 ` [PATCH bpf-next 11/13] selftests/bpf: Add tracing_multi bpf prog rollback test Leon Hwang
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Verify that the cookie also works for bpf prog targets of tracing_multi
link.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 .../selftests/bpf/prog_tests/tracing_multi.c  | 25 ++++++++++++++++++-
 .../selftests/bpf/progs/tracing_multi_bpf.c   | 13 ++++++++--
 2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
index 5cf11707eaf8..070266a864d4 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
@@ -304,9 +304,12 @@ static int run_bpf_target(struct bpf_program *prog, __u32 retval, const char *na
 	return 0;
 }
 
-static void test_link_api_bpf_prog(void)
+static void __test_link_api_bpf_prog(bool test_cookies)
 {
 	const char *funcs[] = { "target_1", "target_2" };
+	__u64 fentry_cookies[] = { 0xfeed01, 0xfeed01 };
+	__u64 fexit_cookies[] = { 0xfeed02, 0xfeed02 };
+	__u64 fsession_cookies[] = { 0xfeed03, 0xfeed03 };
 	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
 	struct tracing_multi_bpf *skel = NULL;
 	int fds[ARRAY_SIZE(funcs)];
@@ -319,20 +322,28 @@ static void test_link_api_bpf_prog(void)
 	fds[0] = bpf_program__fd(skel->progs.target_1);
 	fds[1] = bpf_program__fd(skel->progs.target_2);
 
+	skel->bss->test_cookies = test_cookies;
+	skel->bss->fentry_cookie = fentry_cookies[0];
+	skel->bss->fexit_cookie = fexit_cookies[0];
+	skel->bss->fsession_cookie = fsession_cookies[0];
+
 	opts.fds = fds;
 	opts.funcs = funcs;
 	opts.cnt = ARRAY_SIZE(fds);
 
+	opts.cookies = test_cookies ? fentry_cookies : NULL;
 	link = bpf_program__attach_tracing_multi(skel->progs.test_fentry, NULL, &opts);
 	if (!ASSERT_OK_PTR(link, "attach_fentry"))
 		goto cleanup;
 	skel->links.test_fentry = link;
 
+	opts.cookies = test_cookies ? fexit_cookies : NULL;
 	link = bpf_program__attach_tracing_multi(skel->progs.test_fexit, NULL, &opts);
 	if (!ASSERT_OK_PTR(link, "attach_fexit"))
 		goto cleanup;
 	skel->links.test_fexit = link;
 
+	opts.cookies = test_cookies ? fsession_cookies : NULL;
 	link = bpf_program__attach_tracing_multi(skel->progs.test_fsession, NULL, &opts);
 	if (!ASSERT_OK_PTR(link, "attach_fsession"))
 		goto cleanup;
@@ -356,6 +367,16 @@ static void test_link_api_bpf_prog(void)
 	tracing_multi_bpf__destroy(skel);
 }
 
+static void test_link_api_bpf_prog(void)
+{
+	__test_link_api_bpf_prog(false);
+}
+
+static void test_cookies_bpf_prog(void)
+{
+	__test_link_api_bpf_prog(true);
+}
+
 static void test_module_skel_api(void)
 {
 	struct tracing_multi_module *skel = NULL;
@@ -1196,4 +1217,6 @@ void test_tracing_multi_test(void)
 		test_link_api_bpf_prog();
 	if (test__start_subtest("attach_api_bpf_prog_fails"))
 		test_attach_api_bpf_prog_fails();
+	if (test__start_subtest("cookies_bpf_prog"))
+		test_cookies_bpf_prog();
 }
diff --git a/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c b/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c
index 3e02602558ad..651b5d86203f 100644
--- a/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c
+++ b/tools/testing/selftests/bpf/progs/tracing_multi_bpf.c
@@ -5,6 +5,10 @@
 
 char _license[] SEC("license") = "GPL";
 
+bool test_cookies;
+__u64 fentry_cookie;
+__u64 fexit_cookie;
+__u64 fsession_cookie;
 __u64 test_result_fentry;
 __u64 test_result_fexit;
 __u64 test_result_fsession_entry;
@@ -29,20 +33,25 @@ int target_2(void *ctx)
 SEC("fentry.multi")
 int BPF_PROG(test_fentry)
 {
-	test_result_fentry++;
+	if (!test_cookies || bpf_get_attach_cookie(ctx) == fentry_cookie)
+		test_result_fentry++;
 	return 0;
 }
 
 SEC("fexit.multi")
 int BPF_PROG(test_fexit)
 {
-	test_result_fexit++;
+	if (!test_cookies || bpf_get_attach_cookie(ctx) == fexit_cookie)
+		test_result_fexit++;
 	return 0;
 }
 
 SEC("fsession.multi")
 int BPF_PROG(test_fsession)
 {
+	if (test_cookies && bpf_get_attach_cookie(ctx) != fsession_cookie)
+		return 0;
+
 	if (bpf_session_is_return(ctx))
 		test_result_fsession_exit++;
 	else
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 11/13] selftests/bpf: Add tracing_multi bpf prog rollback test
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (9 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 10/13] selftests/bpf: Add tracing_multi bpf prog cookie test Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 12/13] selftests/bpf: Add tracing_multi bpf prog link info test Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 13/13] selftests/bpf: Test tailcall with fentry.multi Leon Hwang
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

When rollbacking, the prepared trampolines should be reverted.

In this test, the first bpf prog will be attached with the trampoline, and
the second bpf prog won't be attached because the bpf prog has been
attached by a fentry.multi prog, that causes the rollback procedure.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 tools/lib/bpf/libbpf.c                        |  2 +-
 tools/lib/bpf/libbpf_internal.h               |  1 +
 .../selftests/bpf/prog_tests/tracing_multi.c  | 61 +++++++++++++++++++
 3 files changed, 63 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index d698a64ff800..705f50a44410 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10629,7 +10629,7 @@ int libbpf_find_vmlinux_btf_id(const char *name,
 	return libbpf_err(err);
 }
 
-static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd, int token_fd)
+int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd, int token_fd)
 {
 	struct bpf_prog_info info;
 	__u32 info_len = sizeof(info);
diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
index 7a74abb904f8..87a3828e9b8a 100644
--- a/tools/lib/bpf/libbpf_internal.h
+++ b/tools/lib/bpf/libbpf_internal.h
@@ -436,6 +436,7 @@ int btf_load_into_kernel(struct btf *btf,
 			 char *log_buf, size_t log_sz, __u32 log_level,
 			 int token_fd);
 struct btf *btf_load_from_kernel(__u32 id, struct btf *base_btf, int token_fd);
+int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd, int token_fd);
 
 struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
 void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
index 070266a864d4..a4d9934ad1dc 100644
--- a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
+++ b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
@@ -377,6 +377,65 @@ static void test_cookies_bpf_prog(void)
 	__test_link_api_bpf_prog(true);
 }
 
+static void test_rollback_bpf_prog(void)
+{
+	int token_fd, prog_fd, fd = -1, pre_fd = -1;
+	LIBBPF_OPTS(bpf_link_create_opts, lopts);
+	struct tracing_multi_bpf *skel = NULL;
+	__u32 target_ids[2], ids[2];
+	int target_fds[2], fds[2];
+	int first, second;
+
+	skel = tracing_multi_bpf__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "tracing_multi_bpf__open_and_load"))
+		return;
+
+	token_fd = bpf_object__token_fd(skel->obj);
+	token_fd = token_fd < 0 ? 0 : token_fd;
+	target_fds[0] = bpf_program__fd(skel->progs.target_1);
+	target_fds[1] = bpf_program__fd(skel->progs.target_2);
+	target_ids[0] = libbpf_find_prog_btf_id("target_1", target_fds[0], token_fd);
+	if (!ASSERT_GT(target_ids[0], 0, "target_1_btf_id"))
+		goto cleanup;
+	target_ids[1] = libbpf_find_prog_btf_id("target_2", target_fds[1], token_fd);
+	if (!ASSERT_GT(target_ids[1], 0, "target_2_btf_id"))
+		goto cleanup;
+
+	first = target_fds[0] < target_fds[1] ? 0 : 1;
+	second = 1 - first;
+	fds[0] = target_fds[first];
+	fds[1] = target_fds[second];
+	ids[0] = target_ids[first];
+	ids[1] = target_ids[second];
+
+	prog_fd = bpf_program__fd(skel->progs.test_fentry);
+	lopts.tracing_multi.ids = &ids[1];
+	lopts.tracing_multi.fds = &fds[1];
+	lopts.tracing_multi.cnt = 1;
+	pre_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_FENTRY_MULTI, &lopts);
+	if (!ASSERT_GE(pre_fd, 0, "pre_attach"))
+		goto cleanup;
+
+	lopts.tracing_multi.ids = ids;
+	lopts.tracing_multi.fds = fds;
+	lopts.tracing_multi.cnt = ARRAY_SIZE(ids);
+
+	fd = bpf_link_create(prog_fd, 0, BPF_TRACE_FENTRY_MULTI, &lopts);
+	if (!ASSERT_EQ(fd, -EBUSY, "bpf_link_create"))
+		goto cleanup;
+
+	if (run_bpf_target(skel->progs.target_1, first + 1, "rollback_target"))
+		goto cleanup;
+	ASSERT_EQ(skel->bss->test_result_fentry, 0, "test_result_fentry");
+
+cleanup:
+	if (fd >= 0)
+		close(fd);
+	if (pre_fd >= 0)
+		close(pre_fd);
+	tracing_multi_bpf__destroy(skel);
+}
+
 static void test_module_skel_api(void)
 {
 	struct tracing_multi_module *skel = NULL;
@@ -1219,4 +1278,6 @@ void test_tracing_multi_test(void)
 		test_attach_api_bpf_prog_fails();
 	if (test__start_subtest("cookies_bpf_prog"))
 		test_cookies_bpf_prog();
+	if (test__start_subtest("rollback_bpf_prog"))
+		test_rollback_bpf_prog();
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 12/13] selftests/bpf: Add tracing_multi bpf prog link info test
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (10 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 11/13] selftests/bpf: Add tracing_multi bpf prog rollback test Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  2026-08-09 15:01 ` [PATCH bpf-next 13/13] selftests/bpf: Test tailcall with fentry.multi Leon Hwang
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Verify the link info works well for bpf prog targets of tracing_multi link.

Assisted-by: Codex:gpt-5.5
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 .../selftests/bpf/prog_tests/fill_link_info.c | 170 ++++++++++++++++--
 1 file changed, 152 insertions(+), 18 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c
index 0918321c8e63..a5f8bfcb1e07 100644
--- a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c
+++ b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c
@@ -3,11 +3,13 @@
 
 #include <string.h>
 #include <linux/bpf.h>
+#include <linux/kallsyms.h>
 #include <linux/limits.h>
 #include <bpf/btf.h>
 #include <test_progs.h>
 #include "trace_helpers.h"
 #include "test_fill_link_info.skel.h"
+#include "tracing_multi_bpf.skel.h"
 #include "bpf/libbpf_internal.h"
 
 #define TP_CAT "sched"
@@ -39,8 +41,19 @@ struct tmulti_target {
 	__u64 addr;
 	__u64 cookie;
 	__u32 id;
+	__u32 func_btf_id;
 };
 
+static const char *tmulti_bpf_funcs[] = {
+	"target_1",
+	"target_2",
+};
+
+static __u64 tmulti_bpf_fentry_cookies[] = { 0xfeed01, 0xfeed01 };
+static __u64 tmulti_bpf_fexit_cookies[] = { 0xfeed02, 0xfeed02 };
+static __u64 tmulti_bpf_fsession_cookies[] = { 0xfeed03, 0xfeed03 };
+#define TRACING_MULTI_BPF_CNT ARRAY_SIZE(tmulti_bpf_funcs)
+
 #define UPROBE_FILE "/proc/self/exe"
 static ssize_t uprobe_offset;
 /* uprobe attach point */
@@ -421,14 +434,28 @@ static int tmulti_target_cmp(const void *a, const void *b)
 	return (ta->id > tb->id) - (ta->id < tb->id);
 }
 
+static int setup_tmulti_btf_obj_id(const struct bpf_program *prog, __u32 *btf_obj_id)
+{
+	struct bpf_prog_info prog_info = {};
+	__u32 len = sizeof(prog_info);
+	int err;
+
+	err = bpf_prog_get_info_by_fd(bpf_program__fd(prog), &prog_info, &len);
+	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
+		return -1;
+	if (!ASSERT_GT(prog_info.attach_btf_obj_id, 0, "attach_btf_obj_id"))
+		return -1;
+
+	*btf_obj_id = prog_info.attach_btf_obj_id;
+	return 0;
+}
+
 static int setup_tmulti_targets(const struct bpf_program *prog,
 				struct tmulti_target *targets,
 				__u32 *btf_obj_id)
 {
-	struct bpf_prog_info prog_info;
-	__u32 len = sizeof(prog_info);
 	struct btf *btf;
-	int err, i;
+	int i;
 	__s32 id;
 
 	btf = btf__load_vmlinux_btf();
@@ -446,13 +473,8 @@ static int setup_tmulti_targets(const struct bpf_program *prog,
 		targets[i].id = id;
 	}
 
-	memset(&prog_info, 0, len);
-	err = bpf_prog_get_info_by_fd(bpf_program__fd(prog), &prog_info, &len);
-	if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
-		goto error;
-	if (!ASSERT_GT(prog_info.attach_btf_obj_id, 0, "attach_btf_obj_id"))
+	if (setup_tmulti_btf_obj_id(prog, btf_obj_id))
 		goto error;
-	*btf_obj_id = prog_info.attach_btf_obj_id;
 
 	/*
 	 * The kernel tracing multi attach sorts ids. We sort as well,
@@ -467,13 +489,48 @@ static int setup_tmulti_targets(const struct bpf_program *prog,
 	return -1;
 }
 
+static int setup_tmulti_bpf_prog_targets(const struct bpf_program *prog, int token_fd,
+					 const __u64 *cookies,
+					 const struct bpf_program * const *target_progs,
+					 struct tmulti_target *targets,
+					 __u32 *btf_obj_id)
+{
+	struct bpf_prog_info prog_info = {};
+	__u32 len = sizeof(prog_info), func_btf_id;
+	int err, i, prog_fd;
+
+	for (i = 0; i < TRACING_MULTI_BPF_CNT; i++) {
+		memset(&prog_info, 0, sizeof(prog_info));
+		len = sizeof(prog_info);
+		prog_info.jited_ksyms = ptr_to_u64(&targets[i].addr);
+		prog_info.nr_jited_ksyms = 1;
+		prog_fd = bpf_program__fd(target_progs[i]);
+		err = bpf_prog_get_info_by_fd(prog_fd, &prog_info, &len);
+		if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd"))
+			return -1;
+
+		func_btf_id = libbpf_find_prog_btf_id(tmulti_bpf_funcs[i], prog_fd, token_fd);
+		if (!ASSERT_GT(func_btf_id, 0, "libbpf_find_prog_btf_id"))
+			return -1;
+
+		targets[i].cookie = cookies[i];
+		targets[i].id = prog_info.id;
+		targets[i].func_btf_id = func_btf_id;
+	}
+
+	return setup_tmulti_btf_obj_id(prog, btf_obj_id);
+}
+
 static int verify_tracing_multi_link_info(int fd, const struct bpf_program *prog,
 					  const struct tmulti_target *targets,
-					  __u32 btf_obj_id, bool has_cookies)
+					  __u32 btf_obj_id, __u32 count,
+					  bool has_cookies, bool tgt_progs)
 {
+#define TMULTI_CNT (TRACING_MULTI_CNT > TRACING_MULTI_BPF_CNT ? \
+		    TRACING_MULTI_CNT : TRACING_MULTI_BPF_CNT)
 	enum bpf_attach_type attach_type = bpf_program__expected_attach_type(prog);
-	__u64 addrs[TRACING_MULTI_CNT], cookies[TRACING_MULTI_CNT];
-	__u32 ids[TRACING_MULTI_CNT];
+	__u64 addrs[TMULTI_CNT], cookies[TMULTI_CNT];
+	__u32 ids[TMULTI_CNT], fids[TMULTI_CNT];
 	struct bpf_link_info info;
 	__u32 len = sizeof(info);
 	int err, i;
@@ -487,7 +544,9 @@ static int verify_tracing_multi_link_info(int fd, const struct bpf_program *prog
 		return -1;
 
 	ASSERT_EQ(info.tracing_multi.attach_type, attach_type, "info.tracing_multi.attach_type");
-	ASSERT_EQ(info.tracing_multi.count, TRACING_MULTI_CNT, "info.tracing_multi.count");
+	ASSERT_EQ(info.tracing_multi.count, count, "info.tracing_multi.count");
+	ASSERT_EQ((int) info.tracing_multi.tgt_progs, (int) tgt_progs,
+		  "info.tracing_multi.tgt_progs");
 
 	memset(ids, 0, sizeof(ids));
 	memset(cookies, 0, sizeof(cookies));
@@ -496,7 +555,8 @@ static int verify_tracing_multi_link_info(int fd, const struct bpf_program *prog
 	info.tracing_multi.ids = ptr_to_u64(ids);
 	info.tracing_multi.addrs = ptr_to_u64(addrs);
 	info.tracing_multi.cookies = has_cookies ? ptr_to_u64(cookies) : 0;
-	info.tracing_multi.count = TRACING_MULTI_CNT;
+	info.tracing_multi.func_btf_ids = tgt_progs ? ptr_to_u64(fids) : 0;
+	info.tracing_multi.count = count;
 
 	err = bpf_link_get_info_by_fd(fd, &info, &len);
 	if (!ASSERT_OK(err, "bpf_link_get_info_by_fd"))
@@ -506,14 +566,19 @@ static int verify_tracing_multi_link_info(int fd, const struct bpf_program *prog
 		return -1;
 
 	ASSERT_EQ(info.tracing_multi.attach_type, attach_type, "info.tracing_multi.attach_type");
-	ASSERT_EQ(info.tracing_multi.count, TRACING_MULTI_CNT, "info.tracing_multi.count");
+	ASSERT_EQ(info.tracing_multi.count, count, "info.tracing_multi.count");
 	ASSERT_EQ(info.tracing_multi.btf_obj_id, btf_obj_id, "tracing_multi.btf_obj_id");
+	ASSERT_EQ((int) info.tracing_multi.tgt_progs, (int) tgt_progs,
+		  "info.tracing_multi.tgt_progs");
 
-	for (i = 0; i < TRACING_MULTI_CNT; i++) {
+	for (i = 0; i < count; i++) {
 		ASSERT_EQ(ids[i], targets[i].id, "tracing_multi.ids");
 		ASSERT_EQ(cookies[i], has_cookies ? targets[i].cookie : 0, "tracing_multi.cookies");
 
-		if (targets[i].addr) {
+		if (tgt_progs) {
+			ASSERT_EQ(addrs[i], targets[i].addr, "tracing_multi.addrs");
+			ASSERT_EQ(fids[i], targets[i].func_btf_id, "tracing_multi.func_btf_ids");
+		} else if (targets[i].addr) {
 			struct ksym *ksym;
 
 			if (!ASSERT_NEQ(addrs[i], 0, "tracing_multi.addrs"))
@@ -624,13 +689,78 @@ static void test_tracing_multi_fill_link_info(struct test_fill_link_info *skel,
 		verify_tracing_multi_invalid_user_buffer(link_fd, targets);
 	} else {
 		err = verify_tracing_multi_link_info(link_fd, skel->progs.tmulti_run,
-						     targets, btf_obj_id, has_cookies);
+						     targets, btf_obj_id, TRACING_MULTI_CNT,
+						     has_cookies, false);
 		ASSERT_OK(err, "verify_tracing_multi_link_info");
 	}
 
 	bpf_link__destroy(link);
 }
 
+static void test_tracing_multi_bpf_fill_link_info_one(struct tracing_multi_bpf *skel,
+						      struct bpf_program *prog,
+						      __u64 *cookies,
+						      bool has_cookies)
+{
+	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
+	const struct bpf_program *target_progs[] = {
+		skel->progs.target_1,
+		skel->progs.target_2,
+	};
+	struct tmulti_target targets[TRACING_MULTI_BPF_CNT] = {};
+	int fds[TRACING_MULTI_BPF_CNT], token_fd, err, i;
+	struct bpf_link *link;
+	__u32 btf_obj_id;
+
+	token_fd = bpf_object__token_fd(skel->obj);
+	token_fd = token_fd < 0 ? 0 : token_fd;
+
+	if (setup_tmulti_bpf_prog_targets(prog, token_fd, cookies, target_progs, targets,
+					  &btf_obj_id))
+		return;
+
+	for (i = 0; i < TRACING_MULTI_BPF_CNT; i++)
+		fds[i] = bpf_program__fd(target_progs[i]);
+
+	opts.fds = fds;
+	opts.funcs = tmulti_bpf_funcs;
+	opts.cnt = ARRAY_SIZE(fds);
+	opts.cookies = has_cookies ? cookies : NULL;
+
+	link = bpf_program__attach_tracing_multi(prog, NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_tracing_multi"))
+		return;
+
+	err = verify_tracing_multi_link_info(bpf_link__fd(link), prog, targets, btf_obj_id,
+					     TRACING_MULTI_BPF_CNT, has_cookies, true);
+	ASSERT_OK(err, "verify_tracing_multi_link_info");
+
+	bpf_link__destroy(link);
+}
+
+static void test_tracing_multi_bpf_fill_link_info(bool has_cookies)
+{
+	struct tracing_multi_bpf *skel;
+
+#ifndef __x86_64__
+	test__skip();
+	return;
+#endif
+
+	skel = tracing_multi_bpf__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "tracing_multi_bpf__open_and_load"))
+		return;
+
+	test_tracing_multi_bpf_fill_link_info_one(skel, skel->progs.test_fentry,
+						  tmulti_bpf_fentry_cookies, has_cookies);
+	test_tracing_multi_bpf_fill_link_info_one(skel, skel->progs.test_fexit,
+						  tmulti_bpf_fexit_cookies, has_cookies);
+	test_tracing_multi_bpf_fill_link_info_one(skel, skel->progs.test_fsession,
+						  tmulti_bpf_fsession_cookies, has_cookies);
+
+	tracing_multi_bpf__destroy(skel);
+}
+
 #define SEC(name) __attribute__((section(name), used))
 
 static short uprobe_link_info_sema_1 SEC(".probes");
@@ -879,6 +1009,10 @@ void test_fill_link_info(void)
 		test_tracing_multi_fill_link_info(skel, false, false);
 		test_tracing_multi_fill_link_info(skel, true, false);
 	}
+	if (test__start_subtest("tracing_multi_bpf_link_info")) {
+		test_tracing_multi_bpf_fill_link_info(false);
+		test_tracing_multi_bpf_fill_link_info(true);
+	}
 	if (test__start_subtest("tracing_multi_invalid_ubuff"))
 		test_tracing_multi_fill_link_info(skel, true, true);
 
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH bpf-next 13/13] selftests/bpf: Test tailcall with fentry.multi
  2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
                   ` (11 preceding siblings ...)
  2026-08-09 15:01 ` [PATCH bpf-next 12/13] selftests/bpf: Add tracing_multi bpf prog link info test Leon Hwang
@ 2026-08-09 15:01 ` Leon Hwang
  12 siblings, 0 replies; 17+ messages in thread
From: Leon Hwang @ 2026-08-09 15:01 UTC (permalink / raw)
  To: bpf
  Cc: Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Ihor Solodrai, Quentin Monnet, Shuah Khan,
	Leon Hwang, Mykyta Yatsenko, Avinash Duduskar, Anton Protopopov,
	Amery Hung, Jordan Rife, Rong Tao, Eyal Birger, Pu Lehui,
	Jingguo Tan, Lin Ma, linux-kernel, linux-trace-kernel,
	linux-kselftest

Positively verify that a tail call from a BPF subprog works when the
subprog is traced by fentry.multi.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Leon Hwang <leon.hwang@linux.dev>
---
 .../selftests/bpf/prog_tests/tailcalls.c      | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
index c5c9d6c359bb..1bef6b290f3a 100644
--- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c
+++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
@@ -15,6 +15,7 @@
 #include "tailcall_callback.skel.h"
 #include "tailcall_bpf2bpf2.skel.h"
 #include "tailcall_bpf2bpf_fexit.skel.h"
+#include "tracing_multi_bpf.skel.h"
 
 /* test_tailcall_1 checks basic functionality by patching multiple locations
  * in a single program for a single tail call slot with nop->jmp, jmp->nop
@@ -1953,6 +1954,65 @@ static void test_tailcall_bpf2bpf_fexit_links(void)
 	tailcall_bpf2bpf2__destroy(skel_tc);
 }
 
+/*
+ * Verify that a tail call from a BPF subprog works when the subprog is
+ * traced by fentry.multi.
+ */
+static void test_tailcall_bpf2bpf_fentry_multi(void)
+{
+	struct tracing_multi_bpf *skel_tracing = NULL;
+	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
+	const char *funcs[] = { "subprog_tail" };
+	struct tailcall_bpf2bpf2 *skel_tc;
+	struct bpf_link *link = NULL;
+	int fds[ARRAY_SIZE(funcs)];
+	char buff[128] = {};
+	int err, key = 0;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);
+
+#ifndef __x86_64__
+	test__skip();
+	return;
+#endif
+
+	skel_tc = tailcall_bpf2bpf2__open_and_load();
+	if (!ASSERT_OK_PTR(skel_tc, "tailcall_bpf2bpf2__open_and_load"))
+		return;
+
+	skel_tracing = tracing_multi_bpf__open_and_load();
+	if (!ASSERT_OK_PTR(skel_tracing, "tracing_multi_bpf__open_and_load"))
+		goto out;
+
+	fds[0] = bpf_program__fd(skel_tc->progs.classifier_0);
+	err = bpf_map_update_elem(bpf_map__fd(skel_tc->maps.jmp_table), &key, &fds[0], BPF_ANY);
+	if (!ASSERT_OK(err, "bpf_map_update_elem"))
+		goto out;
+
+	opts.fds = fds;
+	opts.funcs = funcs;
+	opts.cnt = ARRAY_SIZE(fds);
+	link = bpf_program__attach_tracing_multi(skel_tracing->progs.test_fentry, NULL, &opts);
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_tracing_multi"))
+		goto out;
+
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel_tc->progs.entry), &topts);
+	if (!ASSERT_OK(err, "bpf_prog_test_run_opts"))
+		goto out;
+
+	ASSERT_EQ(topts.retval, 1, "retval");
+	ASSERT_EQ(skel_tc->bss->count, 33, "count");
+	ASSERT_EQ(skel_tracing->bss->test_result_fentry, 33, "fentry.multi count");
+
+out:
+	bpf_link__destroy(link);
+	tracing_multi_bpf__destroy(skel_tracing);
+	tailcall_bpf2bpf2__destroy(skel_tc);
+}
+
 void test_tailcalls(void)
 {
 	if (test__start_subtest("tailcall_1"))
@@ -2022,4 +2082,6 @@ void test_tailcalls(void)
 	test_tailcall_callback();
 	if (test__start_subtest("tailcall_bpf2bpf_fexit_links"))
 		test_tailcall_bpf2bpf_fexit_links();
+	if (test__start_subtest("tailcall_bpf2bpf_fentry_multi"))
+		test_tailcall_bpf2bpf_fentry_multi();
 }
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH bpf-next 10/13] selftests/bpf: Add tracing_multi bpf prog cookie test
  2026-08-09 15:01 ` [PATCH bpf-next 10/13] selftests/bpf: Add tracing_multi bpf prog cookie test Leon Hwang
@ 2026-08-09 16:20   ` bot+bpf-ci
  0 siblings, 0 replies; 17+ messages in thread
From: bot+bpf-ci @ 2026-08-09 16:20 UTC (permalink / raw)
  To: leon.hwang, bpf
  Cc: ast, daniel, john.fastabend, andrii, eddyz87, memxor, martin.lau,
	song, yonghong.song, jolsa, emil, ihor.solodrai, qmo, shuah,
	leon.hwang, yatsenko, avinash.duduskar, a.s.protopopov, ameryhung,
	jordan, rongtao, eyal.birger, pulehui, tanjingguo, malin89,
	linux-kernel, linux-trace-kernel, linux-kselftest, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3639 bytes --]

> diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
> index 5cf11707eaf89..070266a864d4f 100644
> --- a/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
> +++ b/tools/testing/selftests/bpf/prog_tests/tracing_multi.c
> @@ -304,9 +304,12 @@ static int run_bpf_target(struct bpf_program *prog, __u32 retval, const char *na
>  	return 0;
>  }
>
> -static void test_link_api_bpf_prog(void)
> +static void __test_link_api_bpf_prog(bool test_cookies)
>  {
>  	const char *funcs[] = { "target_1", "target_2" };
> +	__u64 fentry_cookies[] = { 0xfeed01, 0xfeed01 };
> +	__u64 fexit_cookies[] = { 0xfeed02, 0xfeed02 };
> +	__u64 fsession_cookies[] = { 0xfeed03, 0xfeed03 };

A subsystem pattern flags this as potentially concerning: every per-target
cookie array holds the same value twice. The test on the BPF side compares
against a single scalar global set from element [0]:

  skel->bss->fentry_cookie = fentry_cookies[0];

and in progs/tracing_multi_bpf.c:

  if (!test_cookies || bpf_get_attach_cookie(ctx) == fentry_cookie)
          test_result_fentry++;

Since cookie[0] == cookie[1] for all three arrays, the test cannot
distinguish a correct cookie-to-target mapping from a permuted one. The
assertions (test_result_fentry == 2, etc.) succeed as long as some
correct-looking cookie value is delivered on both invocations, but swapping
the cookies of target_1 and target_2 in the kernel is invisible.

That matters because the kernel path this commit exercises is exactly a
permutation: bpf_tracing_multi_attach() calls check_dup_keys() which calls
keys_swap_r() in kernel/trace/bpf_trace.c to reorder the cookies, ids, and
progs arrays to follow the sorted trampoline keys:

  id_a    = ids + (key_a - keys);
  if (data->progs) { ... swap(*prog_a, *prog_b); }
  if (cookies) { cookie_a = cookies + (id_a - ids); ... swap(*cookie_a, *cookie_b); }

An indexing mistake in that swap could desynchronise cookies from progs/ids,
yet the test would still pass. Since fds[]/ids[] come from BPF prog FDs, the
sort order is not the array order the caller supplied.

The neighbouring test for kernel-function targets in this same file already
does this correctly: bpf_fentry_test_cookies[] uses ten distinct values
deliberately out of order (8, 9, 7, 5, 4, 2, 3, 1, 10, 6) and
progs/tracing_multi_check.c checks the cookie per-IP with something like:

  err |= test_cookies ? cookie != 8 : 0;

under `ip == &bpf_fentry_test1`.

Can the test use distinct cookies and a bpf_get_func_ip()-based per-target
expectation so it is sensitive to cookie mis-association?

>  	LIBBPF_OPTS(bpf_tracing_multi_opts, opts);
>  	struct tracing_multi_bpf *skel = NULL;
>  	int fds[ARRAY_SIZE(funcs)];

[ ... ]

> @@ -356,6 +367,16 @@ static void test_link_api_bpf_prog(void)
>  	tracing_multi_bpf__destroy(skel);
>  }
>
> +static void test_link_api_bpf_prog(void)
> +{
> +	__test_link_api_bpf_prog(false);
> +}
> +
> +static void test_cookies_bpf_prog(void)
> +{
> +	__test_link_api_bpf_prog(true);
> +}

This isn't a bug, but would calling __test_link_api_bpf_prog(false) and
__test_link_api_bpf_prog(true) straight from the two subtest arms read more
consistently? The neighbouring test_link_api_ids() is dispatched directly as
test_link_api_ids(false) and test_link_api_ids(true) just a dozen lines
down, without wrapper functions.

[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31320427087

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH bpf-next 06/13] bpf: Add tracing_multi link fdinfo support for bpf progs
  2026-08-09 15:01 ` [PATCH bpf-next 06/13] bpf: Add tracing_multi link fdinfo " Leon Hwang
@ 2026-08-09 16:20   ` bot+bpf-ci
  0 siblings, 0 replies; 17+ messages in thread
From: bot+bpf-ci @ 2026-08-09 16:20 UTC (permalink / raw)
  To: leon.hwang, bpf
  Cc: ast, daniel, john.fastabend, andrii, eddyz87, memxor, martin.lau,
	song, yonghong.song, jolsa, emil, ihor.solodrai, qmo, shuah,
	leon.hwang, yatsenko, avinash.duduskar, a.s.protopopov, ameryhung,
	jordan, rongtao, eyal.birger, pulehui, tanjingguo, malin89,
	linux-kernel, linux-trace-kernel, linux-kselftest, ast, andrii,
	daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai

[-- Attachment #1: Type: text/plain, Size: 3692 bytes --]

> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index a805a5b73303f..80928401830c7 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -3759,26 +3759,46 @@ static int bpf_tracing_multi_link_fill_link_info(const struct bpf_link *link,
>  }
>
>  #ifdef CONFIG_PROC_FS
> +static const char *bpf_prog_func_name(struct bpf_prog *prog, u32 btf_id)
> +{
> +	const struct btf *btf = prog->aux->btf;
> +	const struct btf_type *t;
> +
> +	t = btf_type_by_id(btf, btf_id);
> +	if (!t || !btf_type_is_func(t))
> +		return "";
> +
> +	return btf_name_by_offset(btf, t->name_off);
> +}
> +
>  static void bpf_tracing_multi_show_fdinfo(const struct bpf_link *link,
>  					  struct seq_file *seq)
>  {
>  	struct bpf_tracing_multi_link *tr_link =
>  		container_of(link, struct bpf_tracing_multi_link, link);
>  	bool has_cookies = !!tr_link->cookies;
> +	bool has_progs = !!tr_link->progs;
>
>  	seq_printf(seq, "attach_type:\t%u\n", tr_link->link.attach_type);
>  	seq_printf(seq, "cnt:\t%u\n", tr_link->nodes_cnt);
>
> -	seq_printf(seq, "%s\t %s\t %s\t %s\n", "obj-id", "btf-id", "cookie", "func");
> +	seq_printf(seq, "%s\t %s\t %s\t %s\n", has_progs ? "prog-id" : "obj-id", "btf-id", "cookie",
> +		   "func");
>  	for (int i = 0; i < tr_link->nodes_cnt; i++) {
>  		struct bpf_tracing_multi_node *mnode = &tr_link->nodes[i];
> +		u64 cookie = has_cookies ? tr_link->cookies[i] : 0;
>  		u32 btf_id, obj_id;
>
>  		bpf_trampoline_unpack_key(mnode->trampoline->key, &obj_id, &btf_id);
> +		btf_id = has_progs ? (u32) mnode->trampoline->key : btf_id;
                        ^^^^

This isn't a bug, but since bpf_trampoline_unpack_key() only masks off
bit 31, which is never set for prog targets, does this re-assignment
change anything, or could the unpacked value be used directly?

bpf_trampoline_unpack_key() already sets btf_id = key & 0x7FFFFFFF, and
bit 31 is only set by the !tgt_prog branch of bpf_trampoline_compute_key()
(the kernel-BTF case).

In the has_progs case the key was built as:

    ((u64)tgt_prog->aux->id << 32) | ids[i]

and ids[i] must be a valid BTF type id (<= BTF_MAX_TYPE, 0xfffff) or
bpf_check_attach_target() would have rejected the attach, so (u32)key
and the unpacked btf_id are always equal here.

> +		if (has_progs)
> +			seq_printf(seq, "%u\t %u\t %llu\t %s\n",
> +				   obj_id, btf_id, cookie,
> +				   bpf_prog_func_name(tr_link->progs[i], btf_id));
> +		else
> +			seq_printf(seq, "%u\t %u\t %llu\t %pS\n",
> +				   obj_id, btf_id, cookie,
> +				   (void *) mnode->trampoline->ip);
>
>  		cond_resched();
>  	}

The commit message shows the sample fdinfo output but doesn't say much
about the implementation or why this change is needed. In particular:

> Show the target bpf prog info in the fdinfo.
>
> $ cat /proc/self/fdinfo/13
> pos:    0
> flags:  02000000
> mnt_id: 18
> ino:    1045
> link_type:      tracing_multi
> link_id:        9
> prog_tag:       9a7f9e3ab6f138ab
> prog_id:        25
> attach_type:    61
> cnt:    2
> prog-id  btf-id  cookie  func
> 21       4       16706819        target_1
> 22       5       16706819        target_2

This isn't a bug, but could the changelog say why the trampoline ip is
not usable as the func column for prog targets (tr->ip is 0 for JIT'd
prog addresses), rather than mostly showing the resulting output? It
also doesn't mention that the first column header changes from "obj-id"
to "prog-id" for prog-target links.


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/31320427087

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH bpf-next 04/13] bpf: Add tracing_multi link support for bpf progs
  2026-08-09 15:01 ` [PATCH bpf-next 04/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
@ 2026-08-10 13:13   ` Jiri Olsa
  0 siblings, 0 replies; 17+ messages in thread
From: Jiri Olsa @ 2026-08-10 13:13 UTC (permalink / raw)
  To: Leon Hwang
  Cc: bpf, Alexei Starovoitov, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Emil Tsalapatis,
	Ihor Solodrai, Quentin Monnet, Shuah Khan, Mykyta Yatsenko,
	Avinash Duduskar, Anton Protopopov, Amery Hung, Jordan Rife,
	Rong Tao, Eyal Birger, Pu Lehui, Jingguo Tan, Lin Ma,
	linux-kernel, linux-trace-kernel, linux-kselftest

On Sun, Aug 09, 2026 at 11:01:02PM +0800, Leon Hwang wrote:

SNIP

> diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> index eddd259d3776..fc51ea2428be 100644
> --- a/kernel/bpf/trampoline.c
> +++ b/kernel/bpf/trampoline.c
> @@ -1572,12 +1572,23 @@ static int update_fentry_multi(struct bpf_trampoline *tr, u32 orig_flags,
>  			       struct bpf_tramp_image *im, struct ftrace_hash *hash,
>  			       struct bpf_tracing_multi_data *data)
>  {
> -	unsigned long addr = (unsigned long)(im ? im->image : tr->cur_image->image);
> +	if (tr->func.ftrace_managed) {
> +		unsigned long addr = (unsigned long)(im ? im->image : tr->cur_image->image);
>  
> -	if (bpf_trampoline_use_jmp(tr->flags))
> -		addr = ftrace_jmp_set(addr);
> +		if (bpf_trampoline_use_jmp(tr->flags))
> +			addr = ftrace_jmp_set(addr);
> +
> +		ftrace_hash_add(hash, data->entry, tr->ip, addr);
> +	} else {
> +		void *old_addr = tr->cur_image ? tr->cur_image->image : NULL;
> +		void *new_addr = im ? im->image : NULL;
> +		int ret;
> +
> +		ret = bpf_trampoline_update_fentry(tr, orig_flags, old_addr, new_addr);
> +		if (ret)
> +			return ret;
> +	}

hum, so IIUC this sequentially attaches to target bpf program as it
would with current API, so there's no attachent speedup, right?

>  
> -	ftrace_hash_add(hash, data->entry, tr->ip, addr);
>  	tr->cur_image = im;
>  	return 0;
>  }
> @@ -1627,6 +1638,18 @@ static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)
>  
>  static void bpf_trampoline_multi_attach_rollback(struct bpf_trampoline *tr)
>  {
> +	if (!tr->func.ftrace_managed) {
> +		void *failed_addr = tr->cur_image ? tr->cur_image->image : NULL;
> +		void *old_addr = tr->multi_attach.old_image ?
> +				 tr->multi_attach.old_image->image : NULL;
> +		u32 orig_flags = tr->flags;
> +		int ret;
> +
> +		tr->flags = tr->multi_attach.old_flags;
> +		ret = bpf_trampoline_update_fentry(tr, orig_flags, failed_addr, old_addr);
> +		WARN_ONCE(ret, "bpf_trampoline_update_fentry failed: %d\n", ret);
> +	}
> +
>  	if (tr->cur_image)
>  		bpf_tramp_image_put(tr->cur_image);
>  	tr->cur_image = tr->multi_attach.old_image;
> @@ -1643,6 +1666,7 @@ static void bpf_trampoline_multi_attach_rollback(struct bpf_trampoline *tr)
>  	for_each_mnode_cnt(mnode, link, link->nodes_cnt)
>  
>  int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
> +				u64 *keys, struct bpf_prog **progs,
>  				struct bpf_tracing_multi_link *link)
>  {
>  	struct bpf_tracing_multi_data *data = &link->data;
> @@ -1651,18 +1675,18 @@ int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
>  	struct bpf_tracing_multi_node *mnode;
>  	struct bpf_trampoline *tr;
>  	int i, err, rollback_cnt;
> -	u64 key;
>  
>  	for_each_mnode(mnode, link) {
>  		rollback_cnt = i;
>  
> -		err = bpf_check_attach_btf_id_multi(btf, prog, ids[i], &tgt_info);
> +		if (progs)
> +			err = bpf_check_attach_target(NULL, prog, progs[i], ids[i], &tgt_info);
> +		else
> +			err = bpf_check_attach_btf_id_multi(btf, prog, ids[i], &tgt_info);
>  		if (err)
>  			goto rollback_put;
>  
> -		key = bpf_trampoline_compute_key(NULL, btf, ids[i]);
> -
> -		tr = bpf_trampoline_get(key, &tgt_info);
> +		tr = bpf_trampoline_get(keys[i], &tgt_info);
>  		if (!tr) {
>  			err = -ENOMEM;
>  			goto rollback_put;
> @@ -1691,6 +1715,9 @@ int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,
>  	for_each_mnode(mnode, link) {
>  		bpf_trampoline_multi_attach_init(mnode->trampoline);
>  
> +		if (progs && progs[i]->aux->tail_call_reachable)
> +			mnode->trampoline->flags |= BPF_TRAMP_F_TAIL_CALL_CTX;
> +
>  		data->entry = &mnode->entry;
>  		err = __bpf_trampoline_link_prog(&mnode->node, mnode->trampoline, NULL,
>  						 &trampoline_multi_ops, data);

IIUC for bpf_program targets the actuall attachment is happening in
here, right?

the rest of the function logic won't execute, because there won't
be any data in the reg/noreg/mod hashes..?

you seem to use the tracing_multi API to ease up attachment to multiple
bpf programs and end up with just single link fd for all attachments

I think it'd be cleaner to have separate api or code paths for that

jirka

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-08-10 13:13 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-09 15:00 [PATCH bpf-next 00/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
2026-08-09 15:00 ` [PATCH bpf-next 01/13] bpf: Initialize ftrace_managed in bpf_trampoline_get Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 02/13] bpf: Factor out update_fentry_multi helper Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 03/13] bpf: Drop unnecessary ftrace_location() in update_fentry_multi() Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 04/13] bpf: Add tracing_multi link support for bpf progs Leon Hwang
2026-08-10 13:13   ` Jiri Olsa
2026-08-09 15:01 ` [PATCH bpf-next 05/13] libbpf: " Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 06/13] bpf: Add tracing_multi link fdinfo " Leon Hwang
2026-08-09 16:20   ` bot+bpf-ci
2026-08-09 15:01 ` [PATCH bpf-next 07/13] bpf: Add tracing_multi link info " Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 08/13] selftests/bpf: Add tracing_multi bpf prog attach test Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 09/13] selftests/bpf: Add tracing_multi bpf prog attach failure tests Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 10/13] selftests/bpf: Add tracing_multi bpf prog cookie test Leon Hwang
2026-08-09 16:20   ` bot+bpf-ci
2026-08-09 15:01 ` [PATCH bpf-next 11/13] selftests/bpf: Add tracing_multi bpf prog rollback test Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 12/13] selftests/bpf: Add tracing_multi bpf prog link info test Leon Hwang
2026-08-09 15:01 ` [PATCH bpf-next 13/13] selftests/bpf: Test tailcall with fentry.multi Leon Hwang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox