bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr}
@ 2022-01-28  1:23 Delyan Kratunov
  2022-01-28  1:23 ` [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run Delyan Kratunov
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Delyan Kratunov @ 2022-01-28  1:23 UTC (permalink / raw)
  To: bpf, ast, andrii, daniel; +Cc: Delyan Kratunov, lmb

Fairly straight-forward mechanical transformation from bpf_prog_test_run
and bpf_prog_test_run_xattr to the bpf_prog_test_run_opts goodness.
Most of the changes are in tests, though bpftool and libbpf (xsk.c) have one
call site each as well.

The only aspect that's still a bit RFC is that prog_run_xattr is testing
behavior specific to bpf_prog_test_run_xattr, which does not exist in prog_run_opts.
Namely, -EINVAL return on data_out == NULL && data_size_out > 0.
Adding this behavior to prog_test_run_opts is one option, keeping the test as-is
and cloning it to use bpf_prog_test_run_opts is another possibility.
The current version just suppresses the deprecation warning.

As an aside, checkpatch really doesn't like that LIBBPF_OPTS looks like
a function call but is formatted like a struct declaration. If anyone
cares about formatting, now would be a good time to mention it.

v1 -> v2:
Split selftests/bpf changes into two commits to appease the mailing list.

Delyan Kratunov (4):
  selftests: bpf: migrate from bpf_prog_test_run
  selftests: bpf: migrate from bpf_prog_test_run_xattr
  bpftool: migrate from bpf_prog_test_run_xattr
  libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run

 tools/bpf/bpftool/prog.c                      |  55 ++--
 tools/lib/bpf/bpf.h                           |   8 +-
 tools/lib/bpf/xsk.c                           |  11 +-
 .../selftests/bpf/prog_tests/atomics.c        |  86 +++---
 .../testing/selftests/bpf/prog_tests/bpf_nf.c |  10 +-
 .../selftests/bpf/prog_tests/check_mtu.c      |  47 ++--
 .../selftests/bpf/prog_tests/cls_redirect.c   |  30 +--
 .../selftests/bpf/prog_tests/dummy_st_ops.c   |  31 +--
 .../selftests/bpf/prog_tests/fentry_fexit.c   |  33 ++-
 .../selftests/bpf/prog_tests/fentry_test.c    |   9 +-
 .../selftests/bpf/prog_tests/fexit_bpf2bpf.c  |  33 ++-
 .../selftests/bpf/prog_tests/fexit_stress.c   |  26 +-
 .../selftests/bpf/prog_tests/fexit_test.c     |   9 +-
 .../selftests/bpf/prog_tests/flow_dissector.c |  75 +++---
 .../prog_tests/flow_dissector_load_bytes.c    |  27 +-
 .../selftests/bpf/prog_tests/for_each.c       |  32 ++-
 .../bpf/prog_tests/get_func_args_test.c       |  14 +-
 .../bpf/prog_tests/get_func_ip_test.c         |  12 +-
 .../selftests/bpf/prog_tests/global_data.c    |  25 +-
 .../bpf/prog_tests/global_func_args.c         |  13 +-
 .../selftests/bpf/prog_tests/kfree_skb.c      |  16 +-
 .../selftests/bpf/prog_tests/kfunc_call.c     |  46 ++--
 .../selftests/bpf/prog_tests/ksyms_module.c   |  23 +-
 .../selftests/bpf/prog_tests/l4lb_all.c       |  35 ++-
 .../selftests/bpf/prog_tests/map_lock.c       |  15 +-
 .../selftests/bpf/prog_tests/map_ptr.c        |  18 +-
 .../selftests/bpf/prog_tests/modify_return.c  |  38 +--
 .../selftests/bpf/prog_tests/pkt_access.c     |  27 +-
 .../selftests/bpf/prog_tests/pkt_md_access.c  |  15 +-
 .../selftests/bpf/prog_tests/prog_run_xattr.c |   5 +
 .../bpf/prog_tests/queue_stack_map.c          |  43 +--
 .../bpf/prog_tests/raw_tp_test_run.c          |  85 +++---
 .../bpf/prog_tests/raw_tp_writable_test_run.c |  16 +-
 .../selftests/bpf/prog_tests/signal_pending.c |  24 +-
 .../selftests/bpf/prog_tests/skb_ctx.c        |  93 +++----
 .../selftests/bpf/prog_tests/skb_helpers.c    |  16 +-
 .../selftests/bpf/prog_tests/sockmap_basic.c  |  19 +-
 .../selftests/bpf/prog_tests/spinlock.c       |  15 +-
 .../selftests/bpf/prog_tests/syscall.c        |  12 +-
 .../selftests/bpf/prog_tests/tailcalls.c      | 245 ++++++++++--------
 .../selftests/bpf/prog_tests/test_profiler.c  |  16 +-
 .../bpf/prog_tests/test_skb_pkt_end.c         |  15 +-
 .../testing/selftests/bpf/prog_tests/timer.c  |   9 +-
 .../selftests/bpf/prog_tests/timer_mim.c      |   9 +-
 .../selftests/bpf/prog_tests/trace_ext.c      |  28 +-
 tools/testing/selftests/bpf/prog_tests/xdp.c  |  35 ++-
 .../bpf/prog_tests/xdp_adjust_frags.c         |  34 ++-
 .../bpf/prog_tests/xdp_adjust_tail.c          | 148 ++++++-----
 .../selftests/bpf/prog_tests/xdp_bpf2bpf.c    |  16 +-
 .../selftests/bpf/prog_tests/xdp_noinline.c   |  45 ++--
 .../selftests/bpf/prog_tests/xdp_perf.c       |  19 +-
 tools/testing/selftests/bpf/test_lru_map.c    |  11 +-
 tools/testing/selftests/bpf/test_progs.h      |   2 +
 tools/testing/selftests/bpf/test_verifier.c   |  16 +-
 54 files changed, 993 insertions(+), 802 deletions(-)

--
2.30.2

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

* [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run
  2022-01-28  1:23 [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Delyan Kratunov
@ 2022-01-28  1:23 ` Delyan Kratunov
  2022-02-01  1:24   ` Andrii Nakryiko
  2022-01-28  1:23 ` [PATCH bpf-next v2 2/4] selftests: bpf: migrate from bpf_prog_test_run_xattr Delyan Kratunov
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Delyan Kratunov @ 2022-01-28  1:23 UTC (permalink / raw)
  To: bpf, ast, andrii, daniel; +Cc: Delyan Kratunov

Signed-off-by: Delyan Kratunov <delyank@fb.com>
---
 .../selftests/bpf/prog_tests/atomics.c        |  86 +++---
 .../testing/selftests/bpf/prog_tests/bpf_nf.c |  10 +-
 .../selftests/bpf/prog_tests/fentry_fexit.c   |  33 ++-
 .../selftests/bpf/prog_tests/fentry_test.c    |   9 +-
 .../selftests/bpf/prog_tests/fexit_bpf2bpf.c  |  33 ++-
 .../selftests/bpf/prog_tests/fexit_stress.c   |  26 +-
 .../selftests/bpf/prog_tests/fexit_test.c     |   9 +-
 .../prog_tests/flow_dissector_load_bytes.c    |  27 +-
 .../selftests/bpf/prog_tests/for_each.c       |  32 ++-
 .../bpf/prog_tests/get_func_args_test.c       |  14 +-
 .../bpf/prog_tests/get_func_ip_test.c         |  12 +-
 .../selftests/bpf/prog_tests/global_data.c    |  25 +-
 .../bpf/prog_tests/global_func_args.c         |  13 +-
 .../selftests/bpf/prog_tests/kfunc_call.c     |  46 ++--
 .../selftests/bpf/prog_tests/ksyms_module.c   |  23 +-
 .../selftests/bpf/prog_tests/l4lb_all.c       |  35 ++-
 .../selftests/bpf/prog_tests/map_lock.c       |  15 +-
 .../selftests/bpf/prog_tests/map_ptr.c        |  18 +-
 .../selftests/bpf/prog_tests/modify_return.c  |  38 +--
 .../selftests/bpf/prog_tests/pkt_access.c     |  27 +-
 .../selftests/bpf/prog_tests/pkt_md_access.c  |  15 +-
 .../bpf/prog_tests/queue_stack_map.c          |  43 +--
 .../bpf/prog_tests/raw_tp_writable_test_run.c |  16 +-
 .../selftests/bpf/prog_tests/signal_pending.c |  24 +-
 .../selftests/bpf/prog_tests/spinlock.c       |  15 +-
 .../selftests/bpf/prog_tests/tailcalls.c      | 245 ++++++++++--------
 .../bpf/prog_tests/test_skb_pkt_end.c         |  15 +-
 .../testing/selftests/bpf/prog_tests/timer.c  |   9 +-
 .../selftests/bpf/prog_tests/timer_mim.c      |   9 +-
 .../selftests/bpf/prog_tests/trace_ext.c      |  28 +-
 tools/testing/selftests/bpf/prog_tests/xdp.c  |  35 ++-
 .../bpf/prog_tests/xdp_adjust_frags.c         |  34 ++-
 .../bpf/prog_tests/xdp_adjust_tail.c          | 114 +++++---
 .../selftests/bpf/prog_tests/xdp_bpf2bpf.c    |  16 +-
 .../selftests/bpf/prog_tests/xdp_noinline.c   |  45 ++--
 .../selftests/bpf/prog_tests/xdp_perf.c       |  19 +-
 tools/testing/selftests/bpf/test_lru_map.c    |  11 +-
 tools/testing/selftests/bpf/test_progs.h      |   2 +
 tools/testing/selftests/bpf/test_verifier.c   |  16 +-
 39 files changed, 727 insertions(+), 515 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c b/tools/testing/selftests/bpf/prog_tests/atomics.c
index 86b7d5d84eec..12ecb3ae5c28 100644
--- a/tools/testing/selftests/bpf/prog_tests/atomics.c
+++ b/tools/testing/selftests/bpf/prog_tests/atomics.c
@@ -7,18 +7,20 @@
 static void test_add(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__add__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(add)"))
 		return;

 	prog_fd = skel->progs.add.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run add",
-		  "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run add",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->add64_value, 3, "add64_value");
@@ -39,19 +41,20 @@ static void test_add(struct atomics_lskel *skel)
 static void test_sub(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__sub__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(sub)"))
 		return;

 	prog_fd = skel->progs.sub.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run sub",
-		  "err %d errno %d retval %d duration %d\n",
-		  err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run sub",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->sub64_value, -1, "sub64_value");
@@ -72,18 +75,20 @@ static void test_sub(struct atomics_lskel *skel)
 static void test_and(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__and__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(and)"))
 		return;

 	prog_fd = skel->progs.and.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run and",
-		  "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run and",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->and64_value, 0x010ull << 32, "and64_value");
@@ -100,19 +105,20 @@ static void test_and(struct atomics_lskel *skel)
 static void test_or(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__or__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(or)"))
 		return;

 	prog_fd = skel->progs.or.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run or",
-		  "err %d errno %d retval %d duration %d\n",
-		  err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run or",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->or64_value, 0x111ull << 32, "or64_value");
@@ -129,18 +135,20 @@ static void test_or(struct atomics_lskel *skel)
 static void test_xor(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__xor__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(xor)"))
 		return;

 	prog_fd = skel->progs.xor.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run xor",
-		  "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run xor",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->xor64_value, 0x101ull << 32, "xor64_value");
@@ -157,18 +165,20 @@ static void test_xor(struct atomics_lskel *skel)
 static void test_cmpxchg(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__cmpxchg__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(cmpxchg)"))
 		return;

 	prog_fd = skel->progs.cmpxchg.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run cmpxchg",
-		  "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run cmpxchg",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->cmpxchg64_value, 2, "cmpxchg64_value");
@@ -186,18 +196,20 @@ static void test_cmpxchg(struct atomics_lskel *skel)
 static void test_xchg(struct atomics_lskel *skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	int link_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	link_fd = atomics_lskel__xchg__attach(skel);
 	if (!ASSERT_GT(link_fd, 0, "attach(xchg)"))
 		return;

 	prog_fd = skel->progs.xchg.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "test_run xchg",
-		  "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "test_run xchg",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto cleanup;

 	ASSERT_EQ(skel->data->xchg64_value, 2, "xchg64_value");
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
index e3166a81e989..dd30b1e3a67c 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c
@@ -11,7 +11,12 @@ enum {
 void test_bpf_nf_ct(int mode)
 {
 	struct test_bpf_nf *skel;
-	int prog_fd, err, retval;
+	int prog_fd, err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	skel = test_bpf_nf__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "test_bpf_nf__open_and_load"))
@@ -22,8 +27,7 @@ void test_bpf_nf_ct(int mode)
 	else
 		prog_fd = bpf_program__fd(skel->progs.nf_skb_ct_test);

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
-				(__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	if (!ASSERT_OK(err, "bpf_prog_test_run"))
 		goto end;

diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
index 4374ac8a8a91..cb0bcd9bb950 100644
--- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
+++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
@@ -9,38 +9,43 @@ void test_fentry_fexit(void)
 	struct fentry_test_lskel *fentry_skel = NULL;
 	struct fexit_test_lskel *fexit_skel = NULL;
 	__u64 *fentry_res, *fexit_res;
-	__u32 duration = 0, retval;
 	int err, prog_fd, i;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	fentry_skel = fentry_test_lskel__open_and_load();
-	if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n"))
+	if (CHECK_OPTS(!fentry_skel, "fentry_skel_load",
+		       "fentry skeleton failed\n"))
 		goto close_prog;
 	fexit_skel = fexit_test_lskel__open_and_load();
-	if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n"))
+	if (CHECK_OPTS(!fexit_skel, "fexit_skel_load",
+		       "fexit skeleton failed\n"))
 		goto close_prog;

 	err = fentry_test_lskel__attach(fentry_skel);
-	if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err))
+	if (CHECK_OPTS(err, "fentry_attach", "fentry attach failed: %d\n", err))
 		goto close_prog;
 	err = fexit_test_lskel__attach(fexit_skel);
-	if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err))
+	if (CHECK_OPTS(err, "fexit_attach", "fexit attach failed: %d\n", err))
 		goto close_prog;

 	prog_fd = fexit_skel->progs.test1.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "ipv6",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "ipv6",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);

 	fentry_res = (__u64 *)fentry_skel->bss;
 	fexit_res = (__u64 *)fexit_skel->bss;
 	printf("%lld\n", fentry_skel->bss->test1_result);
 	for (i = 0; i < 8; i++) {
-		CHECK(fentry_res[i] != 1, "result",
-		      "fentry_test%d failed err %lld\n", i + 1, fentry_res[i]);
-		CHECK(fexit_res[i] != 1, "result",
-		      "fexit_test%d failed err %lld\n", i + 1, fexit_res[i]);
+		CHECK_OPTS(fentry_res[i] != 1, "result",
+			   "fentry_test%d failed err %lld\n", i + 1,
+			   fentry_res[i]);
+		CHECK_OPTS(fexit_res[i] != 1, "result",
+			   "fexit_test%d failed err %lld\n", i + 1,
+			   fexit_res[i]);
 	}

 close_prog:
diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_test.c b/tools/testing/selftests/bpf/prog_tests/fentry_test.c
index 12921b3850d2..6cbf96ef3bdd 100644
--- a/tools/testing/selftests/bpf/prog_tests/fentry_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/fentry_test.c
@@ -6,9 +6,11 @@
 static int fentry_test(struct fentry_test_lskel *fentry_skel)
 {
 	int err, prog_fd, i;
-	__u32 duration = 0, retval;
 	int link_fd;
 	__u64 *result;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	err = fentry_test_lskel__attach(fentry_skel);
 	if (!ASSERT_OK(err, "fentry_attach"))
@@ -20,10 +22,9 @@ static int fentry_test(struct fentry_test_lskel *fentry_skel)
 		return -1;

 	prog_fd = fentry_skel->progs.test1.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 0, "test_run");
+	ASSERT_EQ(topts.retval, 0, "test_run");

 	result = (__u64 *)fentry_skel->bss;
 	for (i = 0; i < sizeof(*fentry_skel->bss) / sizeof(__u64); i++) {
diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
index e83575e5480f..de70b9877438 100644
--- a/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
+++ b/tools/testing/selftests/bpf/prog_tests/fexit_bpf2bpf.c
@@ -58,12 +58,17 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
 				      test_cb cb)
 {
 	struct bpf_object *obj = NULL, *tgt_obj;
-	__u32 retval, tgt_prog_id, info_len;
+	__u32 tgt_prog_id, info_len;
 	struct bpf_prog_info prog_info = {};
 	struct bpf_program **prog = NULL, *p;
 	struct bpf_link **link = NULL;
 	int err, tgt_fd, i;
 	struct btf *btf;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v6,
+		.data_size_in = sizeof(pkt_v6),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(target_obj_file, BPF_PROG_TYPE_UNSPEC,
 			    &tgt_obj, &tgt_fd);
@@ -147,10 +152,9 @@ static void test_fexit_bpf2bpf_common(const char *obj_file,
 	if (!run_prog)
 		goto close_prog;

-	err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6),
-				NULL, NULL, &retval, NULL);
+	err = bpf_prog_test_run_opts(tgt_fd, &topts);
 	ASSERT_OK(err, "prog_run");
-	ASSERT_EQ(retval, 0, "prog_run_ret");
+	ASSERT_EQ(topts.retval, 0, "prog_run_ret");

 	if (check_data_map(obj, prog_cnt, false))
 		goto close_prog;
@@ -225,29 +229,32 @@ static int test_second_attach(struct bpf_object *obj)
 	const char *tgt_obj_file = "./test_pkt_access.o";
 	struct bpf_program *prog = NULL;
 	struct bpf_object *tgt_obj;
-	__u32 duration = 0, retval;
 	struct bpf_link *link;
 	int err = 0, tgt_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v6,
+		.data_size_in = sizeof(pkt_v6),
+		.repeat = 1,
+	);

 	prog = bpf_object__find_program_by_name(obj, prog_name);
-	if (CHECK(!prog, "find_prog", "prog %s not found\n", prog_name))
+	if (CHECK_OPTS(!prog, "find_prog", "prog %s not found\n", prog_name))
 		return -ENOENT;

 	err = bpf_prog_test_load(tgt_obj_file, BPF_PROG_TYPE_UNSPEC,
 			    &tgt_obj, &tgt_fd);
-	if (CHECK(err, "second_prog_load", "file %s err %d errno %d\n",
-		  tgt_obj_file, err, errno))
+	if (CHECK_OPTS(err, "second_prog_load", "file %s err %d errno %d\n",
+		       tgt_obj_file, err, errno))
 		return err;

 	link = bpf_program__attach_freplace(prog, tgt_fd, tgt_name);
 	if (!ASSERT_OK_PTR(link, "second_link"))
 		goto out;

-	err = bpf_prog_test_run(tgt_fd, 1, &pkt_v6, sizeof(pkt_v6),
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval, "ipv6",
-		  "err %d errno %d retval %d duration %d\n",
-		  err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(tgt_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval, "ipv6",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		goto out;

 	err = check_data_map(obj, 1, true);
diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_stress.c b/tools/testing/selftests/bpf/prog_tests/fexit_stress.c
index e4cede6b4b2d..e6c43321f0db 100644
--- a/tools/testing/selftests/bpf/prog_tests/fexit_stress.c
+++ b/tools/testing/selftests/bpf/prog_tests/fexit_stress.c
@@ -10,9 +10,7 @@ void test_fexit_stress(void)
 	char test_skb[128] = {};
 	int fexit_fd[CNT] = {};
 	int link_fd[CNT] = {};
-	__u32 duration = 0;
 	char error[4096];
-	__u32 prog_ret;
 	int err, i, filter_fd;

 	const struct bpf_insn trace_program[] = {
@@ -36,9 +34,15 @@ void test_fexit_stress(void)
 		.log_size = sizeof(error),
 	);

+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = test_skb,
+		.data_size_in = sizeof(test_skb),
+		.repeat = 1,
+	);
+
 	err = libbpf_find_vmlinux_btf_id("bpf_fentry_test1",
 					 trace_opts.expected_attach_type);
-	if (CHECK(err <= 0, "find_vmlinux_btf_id", "failed: %d\n", err))
+	if (CHECK_OPTS(err <= 0, "find_vmlinux_btf_id", "failed: %d\n", err))
 		goto out;
 	trace_opts.attach_btf_id = err;

@@ -47,24 +51,24 @@ void test_fexit_stress(void)
 					    trace_program,
 					    sizeof(trace_program) / sizeof(struct bpf_insn),
 					    &trace_opts);
-		if (CHECK(fexit_fd[i] < 0, "fexit loaded",
-			  "failed: %d errno %d\n", fexit_fd[i], errno))
+		if (CHECK_OPTS(fexit_fd[i] < 0, "fexit loaded",
+			       "failed: %d errno %d\n", fexit_fd[i], errno))
 			goto out;
 		link_fd[i] = bpf_raw_tracepoint_open(NULL, fexit_fd[i]);
-		if (CHECK(link_fd[i] < 0, "fexit attach failed",
-			  "prog %d failed: %d err %d\n", i, link_fd[i], errno))
+		if (CHECK_OPTS(link_fd[i] < 0, "fexit attach failed",
+			       "prog %d failed: %d err %d\n", i, link_fd[i],
+			       errno))
 			goto out;
 	}

 	filter_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
 				  skb_program, sizeof(skb_program) / sizeof(struct bpf_insn),
 				  &skb_opts);
-	if (CHECK(filter_fd < 0, "test_program_loaded", "failed: %d errno %d\n",
-		  filter_fd, errno))
+	if (CHECK_OPTS(filter_fd < 0, "test_program_loaded",
+		       "failed: %d errno %d\n", filter_fd, errno))
 		goto out;

-	err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0,
-				0, &prog_ret, 0);
+	err = bpf_prog_test_run_opts(filter_fd, &topts);
 	close(filter_fd);
 	CHECK_FAIL(err);
 out:
diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_test.c b/tools/testing/selftests/bpf/prog_tests/fexit_test.c
index d4887d8bb396..641ba92aec7e 100644
--- a/tools/testing/selftests/bpf/prog_tests/fexit_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/fexit_test.c
@@ -6,9 +6,11 @@
 static int fexit_test(struct fexit_test_lskel *fexit_skel)
 {
 	int err, prog_fd, i;
-	__u32 duration = 0, retval;
 	int link_fd;
 	__u64 *result;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	err = fexit_test_lskel__attach(fexit_skel);
 	if (!ASSERT_OK(err, "fexit_attach"))
@@ -20,10 +22,9 @@ static int fexit_test(struct fexit_test_lskel *fexit_skel)
 		return -1;

 	prog_fd = fexit_skel->progs.test1.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 0, "test_run");
+	ASSERT_EQ(topts.retval, 0, "test_run");

 	result = (__u64 *)fexit_skel->bss;
 	for (i = 0; i < sizeof(*fexit_skel->bss) / sizeof(__u64); i++) {
diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c
index 93ac3f28226c..17bbd7576843 100644
--- a/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c
+++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector_load_bytes.c
@@ -5,7 +5,6 @@
 void serial_test_flow_dissector_load_bytes(void)
 {
 	struct bpf_flow_keys flow_keys;
-	__u32 duration = 0, retval, size;
 	struct bpf_insn prog[] = {
 		// BPF_REG_1 - 1st argument: context
 		// BPF_REG_2 - 2nd argument: offset, start at first byte
@@ -27,22 +26,28 @@ void serial_test_flow_dissector_load_bytes(void)
 		BPF_EXIT_INSN(),
 	};
 	int fd, err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = &flow_keys,
+		.data_size_out = sizeof(flow_keys),
+		.repeat = 1,
+	);

 	/* make sure bpf_skb_load_bytes is not allowed from skb-less context
 	 */
 	fd = bpf_test_load_program(BPF_PROG_TYPE_FLOW_DISSECTOR, prog,
 			      ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
-	CHECK(fd < 0,
-	      "flow_dissector-bpf_skb_load_bytes-load",
-	      "fd %d errno %d\n",
-	      fd, errno);
+	CHECK_OPTS(fd < 0, "flow_dissector-bpf_skb_load_bytes-load",
+		   "fd %d errno %d\n", fd, errno);

-	err = bpf_prog_test_run(fd, 1, &pkt_v4, sizeof(pkt_v4),
-				&flow_keys, &size, &retval, &duration);
-	CHECK(size != sizeof(flow_keys) || err || retval != 1,
-	      "flow_dissector-bpf_skb_load_bytes",
-	      "err %d errno %d retval %d duration %d size %u/%zu\n",
-	      err, errno, retval, duration, size, sizeof(flow_keys));
+	err = bpf_prog_test_run_opts(fd, &topts);
+	CHECK_OPTS(topts.data_size_out != sizeof(flow_keys) || err ||
+		   topts.retval != 1,
+		   "flow_dissector-bpf_skb_load_bytes",
+		   "err %d errno %d retval %d duration %d size %u/%zu\n", err,
+		   errno, topts.retval, topts.duration, topts.data_size_out,
+		   sizeof(flow_keys));

 	if (fd >= -1)
 		close(fd);
diff --git a/tools/testing/selftests/bpf/prog_tests/for_each.c b/tools/testing/selftests/bpf/prog_tests/for_each.c
index 68eb12a287d4..044df13ee069 100644
--- a/tools/testing/selftests/bpf/prog_tests/for_each.c
+++ b/tools/testing/selftests/bpf/prog_tests/for_each.c
@@ -12,8 +12,13 @@ static void test_hash_map(void)
 	int i, err, hashmap_fd, max_entries, percpu_map_fd;
 	struct for_each_hash_map_elem *skel;
 	__u64 *percpu_valbuf = NULL;
-	__u32 key, num_cpus, retval;
+	__u32 key, num_cpus;
 	__u64 val;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	skel = for_each_hash_map_elem__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "for_each_hash_map_elem__open_and_load"))
@@ -42,11 +47,10 @@ static void test_hash_map(void)
 	if (!ASSERT_OK(err, "percpu_map_update"))
 		goto out;

-	err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_pkt_access),
-				1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
-				&retval, &duration);
-	if (CHECK(err || retval, "ipv4", "err %d errno %d retval %d\n",
-		  err, errno, retval))
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_pkt_access), &topts);
+	duration = topts.duration;
+	if (CHECK(err || topts.retval, "ipv4", "err %d errno %d retval %d\n",
+		  err, errno, topts.retval))
 		goto out;

 	ASSERT_EQ(skel->bss->hashmap_output, 4, "hashmap_output");
@@ -69,11 +73,16 @@ static void test_hash_map(void)

 static void test_array_map(void)
 {
-	__u32 key, num_cpus, max_entries, retval;
+	__u32 key, num_cpus, max_entries;
 	int i, arraymap_fd, percpu_map_fd, err;
 	struct for_each_array_map_elem *skel;
 	__u64 *percpu_valbuf = NULL;
 	__u64 val, expected_total;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	skel = for_each_array_map_elem__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "for_each_array_map_elem__open_and_load"))
@@ -106,11 +115,10 @@ static void test_array_map(void)
 	if (!ASSERT_OK(err, "percpu_map_update"))
 		goto out;

-	err = bpf_prog_test_run(bpf_program__fd(skel->progs.test_pkt_access),
-				1, &pkt_v4, sizeof(pkt_v4), NULL, NULL,
-				&retval, &duration);
-	if (CHECK(err || retval, "ipv4", "err %d errno %d retval %d\n",
-		  err, errno, retval))
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_pkt_access), &topts);
+	duration = topts.duration;
+	if (CHECK(err || topts.retval, "ipv4", "err %d errno %d retval %d\n",
+		  err, errno, topts.retval))
 		goto out;

 	ASSERT_EQ(skel->bss->arraymap_output, expected_total, "array_output");
diff --git a/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c b/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c
index 85c427119fe9..e3e71d41fb57 100644
--- a/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/get_func_args_test.c
@@ -5,8 +5,10 @@
 void test_get_func_args_test(void)
 {
 	struct get_func_args_test *skel = NULL;
-	__u32 duration = 0, retval;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	skel = get_func_args_test__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "get_func_args_test__open_and_load"))
@@ -20,19 +22,17 @@ void test_get_func_args_test(void)
 	 * fentry/fexit programs.
 	 */
 	prog_fd = bpf_program__fd(skel->progs.test1);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 0, "test_run");
+	ASSERT_EQ(topts.retval, 0, "test_run");

 	/* This runs bpf_modify_return_test function and triggers
 	 * fmod_ret_test and fexit_test programs.
 	 */
 	prog_fd = bpf_program__fd(skel->progs.fmod_ret_test);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 1234, "test_run");
+	ASSERT_EQ(topts.retval, 1234, "test_run");

 	ASSERT_EQ(skel->bss->test1_result, 1, "test1_result");
 	ASSERT_EQ(skel->bss->test2_result, 1, "test2_result");
diff --git a/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c b/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c
index 02a465f36d59..fd0299a94463 100644
--- a/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/get_func_ip_test.c
@@ -5,8 +5,10 @@
 void test_get_func_ip_test(void)
 {
 	struct get_func_ip_test *skel = NULL;
-	__u32 duration = 0, retval;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	skel = get_func_ip_test__open();
 	if (!ASSERT_OK_PTR(skel, "get_func_ip_test__open"))
@@ -29,14 +31,12 @@ void test_get_func_ip_test(void)
 		goto cleanup;

 	prog_fd = bpf_program__fd(skel->progs.test1);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 0, "test_run");
+	ASSERT_EQ(topts.retval, 0, "test_run");

 	prog_fd = bpf_program__fd(skel->progs.test5);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_OK(err, "test_run");

diff --git a/tools/testing/selftests/bpf/prog_tests/global_data.c b/tools/testing/selftests/bpf/prog_tests/global_data.c
index 917165e04427..feb56c1fe73f 100644
--- a/tools/testing/selftests/bpf/prog_tests/global_data.c
+++ b/tools/testing/selftests/bpf/prog_tests/global_data.c
@@ -132,24 +132,27 @@ static void test_global_data_rdonly(struct bpf_object *obj, __u32 duration)
 void test_global_data(void)
 {
 	const char *file = "./test_global_data.o";
-	__u32 duration = 0, retval;
 	struct bpf_object *obj;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
-	if (CHECK(err, "load program", "error %d loading %s\n", err, file))
+	if (CHECK_OPTS(err, "load program", "error %d loading %s\n", err, file))
 		return;

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "pass global data run",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "pass global data run",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);

-	test_global_data_number(obj, duration);
-	test_global_data_string(obj, duration);
-	test_global_data_struct(obj, duration);
-	test_global_data_rdonly(obj, duration);
+	test_global_data_number(obj, topts.duration);
+	test_global_data_string(obj, topts.duration);
+	test_global_data_struct(obj, topts.duration);
+	test_global_data_rdonly(obj, topts.duration);

 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_args.c b/tools/testing/selftests/bpf/prog_tests/global_func_args.c
index 93a2439237b0..ee3703f3ecec 100644
--- a/tools/testing/selftests/bpf/prog_tests/global_func_args.c
+++ b/tools/testing/selftests/bpf/prog_tests/global_func_args.c
@@ -40,19 +40,22 @@ static void test_global_func_args0(struct bpf_object *obj)
 void test_global_func_args(void)
 {
 	const char *file = "./test_global_func_args.o";
-	__u32 retval;
 	struct bpf_object *obj;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_CGROUP_SKB, &obj, &prog_fd);
 	if (CHECK(err, "load program", "error %d loading %s\n", err, file))
 		return;

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "pass global func args run",
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK(err || topts.retval, "pass global func args run",
 	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	      err, errno, topts.retval, topts.duration);

 	test_global_func_args0(obj);

diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
index b39a4f09aefd..c00eb974eb85 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_call.c
@@ -9,29 +9,31 @@
 static void test_main(void)
 {
 	struct kfunc_call_test_lskel *skel;
-	int prog_fd, retval, err;
+	int prog_fd, err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	skel = kfunc_call_test_lskel__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "skel"))
 		return;

 	prog_fd = skel->progs.kfunc_call_test1.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "bpf_prog_test_run(test1)");
-	ASSERT_EQ(retval, 12, "test1-retval");
+	ASSERT_EQ(topts.retval, 12, "test1-retval");

 	prog_fd = skel->progs.kfunc_call_test2.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "bpf_prog_test_run(test2)");
-	ASSERT_EQ(retval, 3, "test2-retval");
+	ASSERT_EQ(topts.retval, 3, "test2-retval");

 	prog_fd = skel->progs.kfunc_call_test_ref_btf_id.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "bpf_prog_test_run(test_ref_btf_id)");
-	ASSERT_EQ(retval, 0, "test_ref_btf_id-retval");
+	ASSERT_EQ(topts.retval, 0, "test_ref_btf_id-retval");

 	kfunc_call_test_lskel__destroy(skel);
 }
@@ -39,17 +41,21 @@ static void test_main(void)
 static void test_subprog(void)
 {
 	struct kfunc_call_test_subprog *skel;
-	int prog_fd, retval, err;
+	int prog_fd, err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	skel = kfunc_call_test_subprog__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "skel"))
 		return;

 	prog_fd = bpf_program__fd(skel->progs.kfunc_call_test1);
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "bpf_prog_test_run(test1)");
-	ASSERT_EQ(retval, 10, "test1-retval");
+	ASSERT_EQ(topts.retval, 10, "test1-retval");
 	ASSERT_NEQ(skel->data->active_res, -1, "active_res");
 	ASSERT_EQ(skel->data->sk_state_res, BPF_TCP_CLOSE, "sk_state_res");

@@ -59,17 +65,21 @@ static void test_subprog(void)
 static void test_subprog_lskel(void)
 {
 	struct kfunc_call_test_subprog_lskel *skel;
-	int prog_fd, retval, err;
+	int prog_fd, err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	skel = kfunc_call_test_subprog_lskel__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "skel"))
 		return;

 	prog_fd = skel->progs.kfunc_call_test1.prog_fd;
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "bpf_prog_test_run(test1)");
-	ASSERT_EQ(retval, 10, "test1-retval");
+	ASSERT_EQ(topts.retval, 10, "test1-retval");
 	ASSERT_NEQ(skel->data->active_res, -1, "active_res");
 	ASSERT_EQ(skel->data->sk_state_res, BPF_TCP_CLOSE, "sk_state_res");

diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms_module.c b/tools/testing/selftests/bpf/prog_tests/ksyms_module.c
index d490ad80eccb..ecc58c9e7631 100644
--- a/tools/testing/selftests/bpf/prog_tests/ksyms_module.c
+++ b/tools/testing/selftests/bpf/prog_tests/ksyms_module.c
@@ -9,8 +9,12 @@
 void test_ksyms_module_lskel(void)
 {
 	struct test_ksyms_module_lskel *skel;
-	int retval;
 	int err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	if (!env.has_testmod) {
 		test__skip();
@@ -20,11 +24,10 @@ void test_ksyms_module_lskel(void)
 	skel = test_ksyms_module_lskel__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "test_ksyms_module_lskel__open_and_load"))
 		return;
-	err = bpf_prog_test_run(skel->progs.load.prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(skel->progs.load.prog_fd, &topts);
 	if (!ASSERT_OK(err, "bpf_prog_test_run"))
 		goto cleanup;
-	ASSERT_EQ(retval, 0, "retval");
+	ASSERT_EQ(topts.retval, 0, "retval");
 	ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym");
 cleanup:
 	test_ksyms_module_lskel__destroy(skel);
@@ -33,7 +36,12 @@ void test_ksyms_module_lskel(void)
 void test_ksyms_module_libbpf(void)
 {
 	struct test_ksyms_module *skel;
-	int retval, err;
+	int err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	if (!env.has_testmod) {
 		test__skip();
@@ -43,11 +51,10 @@ void test_ksyms_module_libbpf(void)
 	skel = test_ksyms_module__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "test_ksyms_module__open"))
 		return;
-	err = bpf_prog_test_run(bpf_program__fd(skel->progs.load), 1, &pkt_v4,
-				sizeof(pkt_v4), NULL, NULL, (__u32 *)&retval, NULL);
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.load), &topts);
 	if (!ASSERT_OK(err, "bpf_prog_test_run"))
 		goto cleanup;
-	ASSERT_EQ(retval, 0, "retval");
+	ASSERT_EQ(topts.retval, 0, "retval");
 	ASSERT_EQ(skel->bss->out_bpf_testmod_ksym, 42, "bpf_testmod_ksym");
 cleanup:
 	test_ksyms_module__destroy(skel);
diff --git a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c
index 540ef28fabff..aef03d4a74e6 100644
--- a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c
+++ b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c
@@ -23,12 +23,16 @@ static void test_l4lb(const char *file)
 		__u8 flags;
 	} real_def = {.dst = MAGIC_VAL};
 	__u32 ch_key = 11, real_num = 3;
-	__u32 duration, retval, size;
 	int err, i, prog_fd, map_fd;
 	__u64 bytes = 0, pkts = 0;
 	struct bpf_object *obj;
 	char buf[128];
 	u32 *magic = (u32 *)buf;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = NUM_ITER,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
 	if (CHECK_FAIL(err))
@@ -49,19 +53,24 @@ static void test_l4lb(const char *file)
 		goto out;
 	bpf_map_update_elem(map_fd, &real_num, &real_def, 0);

-	err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v4, sizeof(pkt_v4),
-				buf, &size, &retval, &duration);
-	CHECK(err || retval != 7/*TC_ACT_REDIRECT*/ || size != 54 ||
-	      *magic != MAGIC_VAL, "ipv4",
-	      "err %d errno %d retval %d size %d magic %x\n",
-	      err, errno, retval, size, *magic);
+	topts.data_in = &pkt_v4;
+	topts.data_size_in = sizeof(pkt_v4);

-	err = bpf_prog_test_run(prog_fd, NUM_ITER, &pkt_v6, sizeof(pkt_v6),
-				buf, &size, &retval, &duration);
-	CHECK(err || retval != 7/*TC_ACT_REDIRECT*/ || size != 74 ||
-	      *magic != MAGIC_VAL, "ipv6",
-	      "err %d errno %d retval %d size %d magic %x\n",
-	      err, errno, retval, size, *magic);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 7 /*TC_ACT_REDIRECT*/ ||
+		   topts.data_size_out != 54 || *magic != MAGIC_VAL,
+		   "ipv4", "err %d errno %d retval %d size %d magic %x\n", err,
+		   errno, topts.retval, topts.data_size_out, *magic);
+
+	topts.data_in = &pkt_v6;
+	topts.data_size_in = sizeof(pkt_v6);
+	topts.data_size_out = sizeof(buf); /* reset out size */
+
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 7 /*TC_ACT_REDIRECT*/ ||
+			   topts.data_size_out != 74 || *magic != MAGIC_VAL,
+		   "ipv6", "err %d errno %d retval %d size %d magic %x\n", err,
+		   errno, topts.retval, topts.data_size_out, *magic);

 	map_fd = bpf_find_map(__func__, obj, "stats");
 	if (map_fd < 0)
diff --git a/tools/testing/selftests/bpf/prog_tests/map_lock.c b/tools/testing/selftests/bpf/prog_tests/map_lock.c
index 23d19e9cf26a..a290297905f9 100644
--- a/tools/testing/selftests/bpf/prog_tests/map_lock.c
+++ b/tools/testing/selftests/bpf/prog_tests/map_lock.c
@@ -4,14 +4,17 @@

 static void *spin_lock_thread(void *arg)
 {
-	__u32 duration, retval;
 	int err, prog_fd = *(u32 *) arg;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 10000,
+	);

-	err = bpf_prog_test_run(prog_fd, 10000, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);
 	pthread_exit(arg);
 }

diff --git a/tools/testing/selftests/bpf/prog_tests/map_ptr.c b/tools/testing/selftests/bpf/prog_tests/map_ptr.c
index 273725504f11..67c7517283da 100644
--- a/tools/testing/selftests/bpf/prog_tests/map_ptr.c
+++ b/tools/testing/selftests/bpf/prog_tests/map_ptr.c
@@ -9,10 +9,16 @@
 void test_map_ptr(void)
 {
 	struct map_ptr_kern_lskel *skel;
-	__u32 duration = 0, retval;
 	char buf[128];
 	int err;
 	int page_size = getpagesize();
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = 1,
+	);

 	skel = map_ptr_kern_lskel__open();
 	if (!ASSERT_OK_PTR(skel, "skel_open"))
@@ -26,14 +32,14 @@ void test_map_ptr(void)

 	skel->bss->page_size = page_size;

-	err = bpf_prog_test_run(skel->progs.cg_skb.prog_fd, 1, &pkt_v4,
-				sizeof(pkt_v4), buf, NULL, &retval, NULL);
+	err = bpf_prog_test_run_opts(skel->progs.cg_skb.prog_fd, &topts);

-	if (CHECK(err, "test_run", "err=%d errno=%d\n", err, errno))
+	if (CHECK_OPTS(err, "test_run", "err=%d errno=%d\n", err, errno))
 		goto cleanup;

-	if (CHECK(!retval, "retval", "retval=%d map_type=%u line=%u\n", retval,
-		  skel->bss->g_map_type, skel->bss->g_line))
+	if (CHECK_OPTS(!topts.retval, "retval",
+		       "retval=%d map_type=%u line=%u\n", topts.retval,
+		       skel->bss->g_map_type, skel->bss->g_line))
 		goto cleanup;

 cleanup:
diff --git a/tools/testing/selftests/bpf/prog_tests/modify_return.c b/tools/testing/selftests/bpf/prog_tests/modify_return.c
index b772fe30ce9b..55ae9da1241a 100644
--- a/tools/testing/selftests/bpf/prog_tests/modify_return.c
+++ b/tools/testing/selftests/bpf/prog_tests/modify_return.c
@@ -15,39 +15,40 @@ static void run_test(__u32 input_retval, __u16 want_side_effect, __s16 want_ret)
 {
 	struct modify_return *skel = NULL;
 	int err, prog_fd;
-	__u32 duration = 0, retval;
 	__u16 side_effect;
 	__s16 ret;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	skel = modify_return__open_and_load();
-	if (CHECK(!skel, "skel_load", "modify_return skeleton failed\n"))
+	if (CHECK_OPTS(!skel, "skel_load", "modify_return skeleton failed\n"))
 		goto cleanup;

 	err = modify_return__attach(skel);
-	if (CHECK(err, "modify_return", "attach failed: %d\n", err))
+	if (CHECK_OPTS(err, "modify_return", "attach failed: %d\n", err))
 		goto cleanup;

 	skel->bss->input_retval = input_retval;
 	prog_fd = bpf_program__fd(skel->progs.fmod_ret_test);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, 0,
-				&retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

-	CHECK(err, "test_run", "err %d errno %d\n", err, errno);
+	CHECK_OPTS(err, "test_run", "err %d errno %d\n", err, errno);

-	side_effect = UPPER(retval);
-	ret  = LOWER(retval);
+	side_effect = UPPER(topts.retval);
+	ret = LOWER(topts.retval);

-	CHECK(ret != want_ret, "test_run",
-	      "unexpected ret: %d, expected: %d\n", ret, want_ret);
-	CHECK(side_effect != want_side_effect, "modify_return",
-	      "unexpected side_effect: %d\n", side_effect);
+	CHECK_OPTS(ret != want_ret, "test_run",
+		   "unexpected ret: %d, expected: %d\n", ret, want_ret);
+	CHECK_OPTS(side_effect != want_side_effect, "modify_return",
+		   "unexpected side_effect: %d\n", side_effect);

-	CHECK(skel->bss->fentry_result != 1, "modify_return",
-	      "fentry failed\n");
-	CHECK(skel->bss->fexit_result != 1, "modify_return",
-	      "fexit failed\n");
-	CHECK(skel->bss->fmod_ret_result != 1, "modify_return",
-	      "fmod_ret failed\n");
+	CHECK_OPTS(skel->bss->fentry_result != 1, "modify_return",
+		   "fentry failed\n");
+	CHECK_OPTS(skel->bss->fexit_result != 1, "modify_return",
+		   "fexit failed\n");
+	CHECK_OPTS(skel->bss->fmod_ret_result != 1, "modify_return",
+		   "fmod_ret failed\n");

 cleanup:
 	modify_return__destroy(skel);
@@ -63,4 +64,3 @@ void serial_test_modify_return(void)
 		 0 /* want_side_effect */,
 		 -EINVAL /* want_ret */);
 }
-
diff --git a/tools/testing/selftests/bpf/prog_tests/pkt_access.c b/tools/testing/selftests/bpf/prog_tests/pkt_access.c
index 6628710ec3c6..7205aeabb6c3 100644
--- a/tools/testing/selftests/bpf/prog_tests/pkt_access.c
+++ b/tools/testing/selftests/bpf/prog_tests/pkt_access.c
@@ -6,23 +6,28 @@ void test_pkt_access(void)
 {
 	const char *file = "./test_pkt_access.o";
 	struct bpf_object *obj;
-	__u32 duration, retval;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 100000,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
 	if (CHECK_FAIL(err))
 		return;

-	err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "ipv4",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "ipv4",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);

-	err = bpf_prog_test_run(prog_fd, 100000, &pkt_v6, sizeof(pkt_v6),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "ipv6",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	topts.data_in = &pkt_v6;
+	topts.data_size_in = sizeof(pkt_v6);
+	topts.data_size_out = 0; /* reset from last call */
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "ipv6",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);
 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c b/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c
index c9d2d6a1bfcc..5810a3074dfd 100644
--- a/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c
+++ b/tools/testing/selftests/bpf/prog_tests/pkt_md_access.c
@@ -6,18 +6,21 @@ void test_pkt_md_access(void)
 {
 	const char *file = "./test_pkt_md_access.o";
 	struct bpf_object *obj;
-	__u32 duration, retval;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 10,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
 	if (CHECK_FAIL(err))
 		return;

-	err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);

 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
index b9822f914eeb..1a06e2d55a4b 100644
--- a/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
+++ b/tools/testing/selftests/bpf/prog_tests/queue_stack_map.c
@@ -10,11 +10,18 @@ enum {
 static void test_queue_stack_map_by_type(int type)
 {
 	const int MAP_SIZE = 32;
-	__u32 vals[MAP_SIZE], duration, retval, size, val;
+	__u32 vals[MAP_SIZE], val;
 	int i, err, prog_fd, map_in_fd, map_out_fd;
 	char file[32], buf[128];
 	struct bpf_object *obj;
 	struct iphdr iph;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = 1,
+	);

 	/* Fill test values to be used */
 	for (i = 0; i < MAP_SIZE; i++)
@@ -58,27 +65,31 @@ static void test_queue_stack_map_by_type(int type)
 			pkt_v4.iph.saddr = vals[MAP_SIZE - 1 - i] * 5;
 		}

-		err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-					buf, &size, &retval, &duration);
-		if (err || retval || size != sizeof(pkt_v4))
+		topts.data_size_out = sizeof(buf);
+		err = bpf_prog_test_run_opts(prog_fd, &topts);
+		if (err || topts.retval ||
+		    topts.data_size_out != sizeof(pkt_v4))
 			break;
 		memcpy(&iph, buf + sizeof(struct ethhdr), sizeof(iph));
 		if (iph.daddr != val)
 			break;
 	}

-	CHECK(err || retval || size != sizeof(pkt_v4) || iph.daddr != val,
-	      "bpf_map_pop_elem",
-	      "err %d errno %d retval %d size %d iph->daddr %u\n",
-	      err, errno, retval, size, iph.daddr);
+	CHECK_OPTS(err || topts.retval ||
+		   topts.data_size_out != sizeof(pkt_v4) ||
+		   iph.daddr != val,
+		   "bpf_map_pop_elem",
+		   "err %d errno %d retval %d size %d iph->daddr %u\n", err,
+		   errno, topts.retval, topts.data_size_out, iph.daddr);

 	/* Queue is empty, program should return TC_ACT_SHOT */
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				buf, &size, &retval, &duration);
-	CHECK(err || retval != 2 /* TC_ACT_SHOT */|| size != sizeof(pkt_v4),
-	      "check-queue-stack-map-empty",
-	      "err %d errno %d retval %d size %d\n",
-	      err, errno, retval, size);
+	topts.data_size_out = sizeof(buf);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 2 /* TC_ACT_SHOT */ ||
+			   topts.data_size_out != sizeof(pkt_v4),
+		   "check-queue-stack-map-empty",
+		   "err %d errno %d retval %d size %d\n", err, errno,
+		   topts.retval, topts.data_size_out);

 	/* Check that the program pushed elements correctly */
 	for (i = 0; i < MAP_SIZE; i++) {
@@ -87,8 +98,8 @@ static void test_queue_stack_map_by_type(int type)
 			break;
 	}

-	CHECK(i != MAP_SIZE && (err || val != vals[i] * 5),
-	      "bpf_map_push_elem", "err %d value %u\n", err, val);
+	CHECK_OPTS(i != MAP_SIZE && (err || val != vals[i] * 5),
+		   "bpf_map_push_elem", "err %d value %u\n", err, val);

 out:
 	pkt_v4.iph.saddr = 0;
diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
index 239baccabccb..f4aa7dab4766 100644
--- a/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
+++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_writable_test_run.c
@@ -56,21 +56,23 @@ void serial_test_raw_tp_writable_test_run(void)
 		0,
 	};

-	__u32 prog_ret;
-	int err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0,
-				    0, &prog_ret, 0);
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = test_skb,
+		.data_size_in = sizeof(test_skb),
+		.repeat = 1,
+	);
+	int err = bpf_prog_test_run_opts(filter_fd, &topts);
 	CHECK(err != 42, "test_run",
 	      "tracepoint did not modify return value\n");
-	CHECK(prog_ret != 0, "test_run_ret",
+	CHECK(topts.retval != 0, "test_run_ret",
 	      "socket_filter did not return 0\n");

 	close(tp_fd);

-	err = bpf_prog_test_run(filter_fd, 1, test_skb, sizeof(test_skb), 0, 0,
-				&prog_ret, 0);
+	err = bpf_prog_test_run_opts(filter_fd, &topts);
 	CHECK(err != 0, "test_run_notrace",
 	      "test_run failed with %d errno %d\n", err, errno);
-	CHECK(prog_ret != 0, "test_run_ret_notrace",
+	CHECK(topts.retval != 0, "test_run_ret_notrace",
 	      "socket_filter did not return 0\n");

 out_filterfd:
diff --git a/tools/testing/selftests/bpf/prog_tests/signal_pending.c b/tools/testing/selftests/bpf/prog_tests/signal_pending.c
index aecfe662c070..b70d88bc0380 100644
--- a/tools/testing/selftests/bpf/prog_tests/signal_pending.c
+++ b/tools/testing/selftests/bpf/prog_tests/signal_pending.c
@@ -13,10 +13,14 @@ static void test_signal_pending_by_type(enum bpf_prog_type prog_type)
 	struct itimerval timeo = {
 		.it_value.tv_usec = 100000, /* 100ms */
 	};
-	__u32 duration = 0, retval;
 	int prog_fd;
 	int err;
 	int i;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 0xffffffff,
+	);

 	for (i = 0; i < ARRAY_SIZE(prog); i++)
 		prog[i] = BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0);
@@ -24,20 +28,18 @@ static void test_signal_pending_by_type(enum bpf_prog_type prog_type)

 	prog_fd = bpf_test_load_program(prog_type, prog, ARRAY_SIZE(prog),
 				   "GPL", 0, NULL, 0);
-	CHECK(prog_fd < 0, "test-run", "errno %d\n", errno);
+	CHECK_OPTS(prog_fd < 0, "test-run", "errno %d\n", errno);

 	err = sigaction(SIGALRM, &sigalrm_action, NULL);
-	CHECK(err, "test-run-signal-sigaction", "errno %d\n", errno);
+	CHECK_OPTS(err, "test-run-signal-sigaction", "errno %d\n", errno);

 	err = setitimer(ITIMER_REAL, &timeo, NULL);
-	CHECK(err, "test-run-signal-timer", "errno %d\n", errno);
-
-	err = bpf_prog_test_run(prog_fd, 0xffffffff, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(duration > 500000000, /* 500ms */
-	      "test-run-signal-duration",
-	      "duration %dns > 500ms\n",
-	      duration);
+	CHECK_OPTS(err, "test-run-signal-timer", "errno %d\n", errno);
+
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(topts.duration > 500000000, /* 500ms */
+		   "test-run-signal-duration", "duration %dns > 500ms\n",
+		   topts.duration);

 	signal(SIGALRM, SIG_DFL);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/spinlock.c b/tools/testing/selftests/bpf/prog_tests/spinlock.c
index 6307f5d2b417..c76f2bce0570 100644
--- a/tools/testing/selftests/bpf/prog_tests/spinlock.c
+++ b/tools/testing/selftests/bpf/prog_tests/spinlock.c
@@ -4,14 +4,17 @@

 static void *spin_lock_thread(void *arg)
 {
-	__u32 duration, retval;
 	int err, prog_fd = *(u32 *) arg;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 10000,
+	);

-	err = bpf_prog_test_run(prog_fd, 10000, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "",
-	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, retval, duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "",
+		   "err %d errno %d retval %d duration %d\n", err, errno,
+		   topts.retval, topts.duration);
 	pthread_exit(arg);
 }

diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
index 796f231582f8..532515be494a 100644
--- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c
+++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c
@@ -12,9 +12,13 @@ static void test_tailcall_1(void)
 	struct bpf_map *prog_array;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char prog_name[32];
 	char buff[128] = {};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall1.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
 			    &prog_fd);
@@ -54,20 +58,19 @@ static void test_tailcall_1(void)
 	}

 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != i, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != i, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);

 		err = bpf_map_delete_elem(map_fd, &i);
 		if (CHECK_FAIL(err))
 			goto out;
 	}

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 3, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
 		snprintf(prog_name, sizeof(prog_name), "classifier_%d", i);
@@ -85,10 +88,9 @@ static void test_tailcall_1(void)
 			goto out;
 	}

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 0, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
 		j = bpf_map__max_entries(prog_array) - 1 - i;
@@ -110,30 +112,29 @@ static void test_tailcall_1(void)
 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
 		j = bpf_map__max_entries(prog_array) - 1 - i;

-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != j, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != j, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);

 		err = bpf_map_delete_elem(map_fd, &i);
 		if (CHECK_FAIL(err))
 			goto out;
 	}

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 3, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
 		err = bpf_map_delete_elem(map_fd, &i);
 		if (CHECK_FAIL(err >= 0 || errno != ENOENT))
 			goto out;

-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != 3, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != 3, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);
 	}

 out:
@@ -150,9 +151,13 @@ static void test_tailcall_2(void)
 	struct bpf_map *prog_array;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char prog_name[32];
 	char buff[128] = {};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall2.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
 			    &prog_fd);
@@ -191,30 +196,27 @@ static void test_tailcall_2(void)
 			goto out;
 	}

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 2, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 2, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	i = 2;
 	err = bpf_map_delete_elem(map_fd, &i);
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 1, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	i = 0;
 	err = bpf_map_delete_elem(map_fd, &i);
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 3, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 3, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);
 out:
 	bpf_object__close(obj);
 }
@@ -225,8 +227,12 @@ static void test_tailcall_count(const char *which)
 	struct bpf_map *prog_array, *data_map;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char buff[128] = {};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(which, BPF_PROG_TYPE_SCHED_CLS, &obj,
 			    &prog_fd);
@@ -262,10 +268,9 @@ static void test_tailcall_count(const char *which)
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 1, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
@@ -277,18 +282,17 @@ static void test_tailcall_count(const char *which)

 	i = 0;
 	err = bpf_map_lookup_elem(data_fd, &i, &val);
-	CHECK(err || val != 33, "tailcall count", "err %d errno %d count %d\n",
-	      err, errno, val);
+	CHECK_OPTS(err || val != 33, "tailcall count",
+		   "err %d errno %d count %d\n", err, errno, val);

 	i = 0;
 	err = bpf_map_delete_elem(map_fd, &i);
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 0, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);
 out:
 	bpf_object__close(obj);
 }
@@ -319,10 +323,14 @@ static void test_tailcall_4(void)
 	struct bpf_map *prog_array, *data_map;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	static const int zero = 0;
 	char buff[128] = {};
 	char prog_name[32];
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall4.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
 			    &prog_fd);
@@ -374,10 +382,10 @@ static void test_tailcall_4(void)
 		if (CHECK_FAIL(err))
 			goto out;

-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != i, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != i, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);
 	}

 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
@@ -389,10 +397,10 @@ static void test_tailcall_4(void)
 		if (CHECK_FAIL(err))
 			goto out;

-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != 3, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != 3, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);
 	}
 out:
 	bpf_object__close(obj);
@@ -407,10 +415,14 @@ static void test_tailcall_5(void)
 	struct bpf_map *prog_array, *data_map;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	static const int zero = 0;
 	char buff[128] = {};
 	char prog_name[32];
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall5.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
 			    &prog_fd);
@@ -462,10 +474,10 @@ static void test_tailcall_5(void)
 		if (CHECK_FAIL(err))
 			goto out;

-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != i, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != i, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);
 	}

 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
@@ -477,10 +489,10 @@ static void test_tailcall_5(void)
 		if (CHECK_FAIL(err))
 			goto out;

-		err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-					&duration, &retval, NULL);
-		CHECK(err || retval != 3, "tailcall",
-		      "err %d errno %d retval %d\n", err, errno, retval);
+		err = bpf_prog_test_run_opts(main_fd, &topts);
+		CHECK_OPTS(err || topts.retval != 3, "tailcall",
+			   "err %d errno %d retval %d\n", err, errno,
+			   topts.retval);
 	}
 out:
 	bpf_object__close(obj);
@@ -495,8 +507,12 @@ static void test_tailcall_bpf2bpf_1(void)
 	struct bpf_map *prog_array;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char prog_name[32];
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall_bpf2bpf1.o", BPF_PROG_TYPE_SCHED_CLS,
 			    &obj, &prog_fd);
@@ -536,10 +552,9 @@ static void test_tailcall_bpf2bpf_1(void)
 			goto out;
 	}

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				0, &retval, &duration);
-	CHECK(err || retval != 1, "tailcall",
-	      "err %d errno %d retval %d\n", err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 1, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	/* jmp -> nop, call subprog that will do tailcall */
 	i = 1;
@@ -547,10 +562,9 @@ static void test_tailcall_bpf2bpf_1(void)
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				0, &retval, &duration);
-	CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 0, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	/* make sure that subprog can access ctx and entry prog that
 	 * called this subprog can properly return
@@ -560,11 +574,9 @@ static void test_tailcall_bpf2bpf_1(void)
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				0, &retval, &duration);
-	CHECK(err || retval != sizeof(pkt_v4) * 2,
-	      "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != sizeof(pkt_v4) * 2, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);
 out:
 	bpf_object__close(obj);
 }
@@ -579,8 +591,12 @@ static void test_tailcall_bpf2bpf_2(void)
 	struct bpf_map *prog_array, *data_map;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char buff[128] = {};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = buff,
+		.data_size_in = sizeof(buff),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall_bpf2bpf2.o", BPF_PROG_TYPE_SCHED_CLS,
 			    &obj, &prog_fd);
@@ -616,10 +632,9 @@ static void test_tailcall_bpf2bpf_2(void)
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 1, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 1, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	data_map = bpf_object__find_map_by_name(obj, "tailcall.bss");
 	if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map)))
@@ -631,18 +646,17 @@ static void test_tailcall_bpf2bpf_2(void)

 	i = 0;
 	err = bpf_map_lookup_elem(data_fd, &i, &val);
-	CHECK(err || val != 33, "tailcall count", "err %d errno %d count %d\n",
-	      err, errno, val);
+	CHECK_OPTS(err || val != 33, "tailcall count",
+		   "err %d errno %d count %d\n", err, errno, val);

 	i = 0;
 	err = bpf_map_delete_elem(map_fd, &i);
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, buff, sizeof(buff), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != 0, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != 0, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);
 out:
 	bpf_object__close(obj);
 }
@@ -657,8 +671,12 @@ static void test_tailcall_bpf2bpf_3(void)
 	struct bpf_map *prog_array;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char prog_name[32];
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall_bpf2bpf3.o", BPF_PROG_TYPE_SCHED_CLS,
 			    &obj, &prog_fd);
@@ -697,33 +715,27 @@ static void test_tailcall_bpf2bpf_3(void)
 			goto out;
 	}

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != sizeof(pkt_v4) * 3,
-	      "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != sizeof(pkt_v4) * 3, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	i = 1;
 	err = bpf_map_delete_elem(map_fd, &i);
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != sizeof(pkt_v4),
-	      "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != sizeof(pkt_v4), "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	i = 0;
 	err = bpf_map_delete_elem(map_fd, &i);
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != sizeof(pkt_v4) * 2,
-	      "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != sizeof(pkt_v4) * 2, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);
 out:
 	bpf_object__close(obj);
 }
@@ -754,8 +766,12 @@ static void test_tailcall_bpf2bpf_4(bool noise)
 	struct bpf_map *prog_array, *data_map;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
-	__u32 retval, duration;
 	char prog_name[32];
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load("tailcall_bpf2bpf4.o", BPF_PROG_TYPE_SCHED_CLS,
 			    &obj, &prog_fd);
@@ -809,15 +825,14 @@ static void test_tailcall_bpf2bpf_4(bool noise)
 	if (CHECK_FAIL(err))
 		goto out;

-	err = bpf_prog_test_run(main_fd, 1, &pkt_v4, sizeof(pkt_v4), 0,
-				&duration, &retval, NULL);
-	CHECK(err || retval != sizeof(pkt_v4) * 3, "tailcall", "err %d errno %d retval %d\n",
-	      err, errno, retval);
+	err = bpf_prog_test_run_opts(main_fd, &topts);
+	CHECK_OPTS(err || topts.retval != sizeof(pkt_v4) * 3, "tailcall",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

 	i = 0;
 	err = bpf_map_lookup_elem(data_fd, &i, &val);
-	CHECK(err || val.count != 31, "tailcall count", "err %d errno %d count %d\n",
-	      err, errno, val.count);
+	CHECK_OPTS(err || val.count != 31, "tailcall count",
+		   "err %d errno %d count %d\n", err, errno, val.count);

 out:
 	bpf_object__close(obj);
diff --git a/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c b/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c
index cf1215531920..22ea144abea3 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_skb_pkt_end.c
@@ -6,15 +6,18 @@

 static int sanity_run(struct bpf_program *prog)
 {
-	__u32 duration, retval;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	prog_fd = bpf_program__fd(prog);
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	if (CHECK(err || retval != 123, "test_run",
-		  "err %d errno %d retval %d duration %d\n",
-		  err, errno, retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK_OPTS(err || topts.retval != 123, "test_run",
+		       "err %d errno %d retval %d duration %d\n", err, errno,
+		       topts.retval, topts.duration))
 		return -1;
 	return 0;
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/timer.c b/tools/testing/selftests/bpf/prog_tests/timer.c
index 0f4e49e622cd..dbed89cff2ee 100644
--- a/tools/testing/selftests/bpf/prog_tests/timer.c
+++ b/tools/testing/selftests/bpf/prog_tests/timer.c
@@ -6,7 +6,9 @@
 static int timer(struct timer *timer_skel)
 {
 	int err, prog_fd;
-	__u32 duration = 0, retval;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	err = timer__attach(timer_skel);
 	if (!ASSERT_OK(err, "timer_attach"))
@@ -16,10 +18,9 @@ static int timer(struct timer *timer_skel)
 	ASSERT_EQ(timer_skel->data->callback2_check, 52, "callback2_check1");

 	prog_fd = bpf_program__fd(timer_skel->progs.test1);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 0, "test_run");
+	ASSERT_EQ(topts.retval, 0, "test_run");
 	timer__detach(timer_skel);

 	usleep(50); /* 10 usecs should be enough, but give it extra */
diff --git a/tools/testing/selftests/bpf/prog_tests/timer_mim.c b/tools/testing/selftests/bpf/prog_tests/timer_mim.c
index 949a0617869d..b5166d448172 100644
--- a/tools/testing/selftests/bpf/prog_tests/timer_mim.c
+++ b/tools/testing/selftests/bpf/prog_tests/timer_mim.c
@@ -6,19 +6,20 @@

 static int timer_mim(struct timer_mim *timer_skel)
 {
-	__u32 duration = 0, retval;
 	__u64 cnt1, cnt2;
 	int err, prog_fd, key1 = 1;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	err = timer_mim__attach(timer_skel);
 	if (!ASSERT_OK(err, "timer_attach"))
 		return err;

 	prog_fd = bpf_program__fd(timer_skel->progs.test1);
-	err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
-				NULL, NULL, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(retval, 0, "test_run");
+	ASSERT_EQ(topts.retval, 0, "test_run");
 	timer_mim__detach(timer_skel);

 	/* check that timer_cb[12] are incrementing 'cnt' */
diff --git a/tools/testing/selftests/bpf/prog_tests/trace_ext.c b/tools/testing/selftests/bpf/prog_tests/trace_ext.c
index 924441d4362d..0d36c43c59de 100644
--- a/tools/testing/selftests/bpf/prog_tests/trace_ext.c
+++ b/tools/testing/selftests/bpf/prog_tests/trace_ext.c
@@ -23,8 +23,12 @@ void test_trace_ext(void)
 	int err, pkt_fd, ext_fd;
 	struct bpf_program *prog;
 	char buf[100];
-	__u32 retval;
 	__u64 len;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.repeat = 1,
+	);

 	/* open/load/attach test_pkt_md_access */
 	skel_pkt = test_pkt_md_access__open_and_load();
@@ -77,32 +81,32 @@ void test_trace_ext(void)

 	/* load/attach tracing */
 	err = test_trace_ext_tracing__load(skel_trace);
-	if (CHECK(err, "setup", "tracing/test_pkt_md_access_new load failed\n")) {
+	if (CHECK_OPTS(err, "setup", "tracing/test_pkt_md_access_new load failed\n")) {
 		libbpf_strerror(err, buf, sizeof(buf));
 		fprintf(stderr, "%s\n", buf);
 		goto cleanup;
 	}

 	err = test_trace_ext_tracing__attach(skel_trace);
-	if (CHECK(err, "setup", "tracing/test_pkt_md_access_new attach failed: %d\n", err))
+	if (CHECK_OPTS(err, "setup", "tracing/test_pkt_md_access_new attach failed: %d\n", err))
 		goto cleanup;

 	/* trigger the test */
-	err = bpf_prog_test_run(pkt_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				NULL, NULL, &retval, &duration);
-	CHECK(err || retval, "run", "err %d errno %d retval %d\n", err, errno, retval);
+	err = bpf_prog_test_run_opts(pkt_fd, &topts);
+	CHECK_OPTS(err || topts.retval, "run", "err %d errno %d retval %d\n",
+		   err, errno, topts.retval);

 	bss_ext = skel_ext->bss;
 	bss_trace = skel_trace->bss;

 	len = bss_ext->ext_called;

-	CHECK(bss_ext->ext_called == 0,
-		"check", "failed to trigger freplace/test_pkt_md_access\n");
-	CHECK(bss_trace->fentry_called != len,
-		"check", "failed to trigger fentry/test_pkt_md_access_new\n");
-	CHECK(bss_trace->fexit_called != len,
-		"check", "failed to trigger fexit/test_pkt_md_access_new\n");
+	CHECK_OPTS(bss_ext->ext_called == 0, "check",
+		   "failed to trigger freplace/test_pkt_md_access\n");
+	CHECK_OPTS(bss_trace->fentry_called != len, "check",
+		   "failed to trigger fentry/test_pkt_md_access_new\n");
+	CHECK_OPTS(bss_trace->fexit_called != len, "check",
+		   "failed to trigger fexit/test_pkt_md_access_new\n");

 cleanup:
 	test_trace_ext_tracing__destroy(skel_trace);
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp.c b/tools/testing/selftests/bpf/prog_tests/xdp.c
index ac65456b7ab8..2499b6b6ab28 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp.c
@@ -13,8 +13,14 @@ void test_xdp(void)
 	char buf[128];
 	struct ipv6hdr iph6;
 	struct iphdr iph;
-	__u32 duration, retval, size;
 	int err, prog_fd, map_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
 	if (CHECK_FAIL(err))
@@ -26,21 +32,24 @@ void test_xdp(void)
 	bpf_map_update_elem(map_fd, &key4, &value4, 0);
 	bpf_map_update_elem(map_fd, &key6, &value6, 0);

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				buf, &size, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	memcpy(&iph, buf + sizeof(struct ethhdr), sizeof(iph));
-	CHECK(err || retval != XDP_TX || size != 74 ||
-	      iph.protocol != IPPROTO_IPIP, "ipv4",
-	      "err %d errno %d retval %d size %d\n",
-	      err, errno, retval, size);
+	CHECK_OPTS(err || topts.retval != XDP_TX || topts.data_size_out != 74 ||
+		   iph.protocol != IPPROTO_IPIP,
+		   "ipv4", "err %d errno %d retval %d size %d\n", err, errno,
+		   topts.retval, topts.data_size_out);

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6),
-				buf, &size, &retval, &duration);
+	topts.data_in = &pkt_v6;
+	topts.data_size_in = sizeof(pkt_v6);
+	topts.data_size_out = sizeof(buf);
+
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	memcpy(&iph6, buf + sizeof(struct ethhdr), sizeof(iph6));
-	CHECK(err || retval != XDP_TX || size != 114 ||
-	      iph6.nexthdr != IPPROTO_IPV6, "ipv6",
-	      "err %d errno %d retval %d size %d\n",
-	      err, errno, retval, size);
+	CHECK_OPTS(err || topts.retval != XDP_TX ||
+		   topts.data_size_out != 114 ||
+		   iph6.nexthdr != IPPROTO_IPV6,
+		   "ipv6", "err %d errno %d retval %d size %d\n", err, errno,
+		   topts.retval, topts.data_size_out);
 out:
 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c
index 31c188666e81..80be24f50982 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_frags.c
@@ -5,12 +5,14 @@
 void test_xdp_update_frags(void)
 {
 	const char *file = "./test_xdp_update_frags.o";
-	__u32 duration, retval, size;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	int err, prog_fd;
 	__u32 *offset;
 	__u8 *buf;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	obj = bpf_object__open(file);
 	if (libbpf_get_error(obj))
@@ -32,12 +34,16 @@ void test_xdp_update_frags(void)
 	buf[*offset] = 0xaa;		/* marker at offset 16 (head) */
 	buf[*offset + 15] = 0xaa;	/* marker at offset 31 (head) */

-	err = bpf_prog_test_run(prog_fd, 1, buf, 128,
-				buf, &size, &retval, &duration);
+	topts.data_in = buf;
+	topts.data_out = buf;
+	topts.data_size_in = 128;
+	topts.data_size_out = 128;
+
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	/* test_xdp_update_frags: buf[16,31]: 0xaa -> 0xbb */
 	ASSERT_OK(err, "xdp_update_frag");
-	ASSERT_EQ(retval, XDP_PASS, "xdp_update_frag retval");
+	ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval");
 	ASSERT_EQ(buf[16], 0xbb, "xdp_update_frag buf[16]");
 	ASSERT_EQ(buf[31], 0xbb, "xdp_update_frag buf[31]");

@@ -53,12 +59,16 @@ void test_xdp_update_frags(void)
 	buf[*offset] = 0xaa;		/* marker at offset 5000 (frag0) */
 	buf[*offset + 15] = 0xaa;	/* marker at offset 5015 (frag0) */

-	err = bpf_prog_test_run(prog_fd, 1, buf, 9000,
-				buf, &size, &retval, &duration);
+	topts.data_in = buf;
+	topts.data_out = buf;
+	topts.data_size_in = 9000;
+	topts.data_size_out = 9000;
+
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	/* test_xdp_update_frags: buf[5000,5015]: 0xaa -> 0xbb */
 	ASSERT_OK(err, "xdp_update_frag");
-	ASSERT_EQ(retval, XDP_PASS, "xdp_update_frag retval");
+	ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval");
 	ASSERT_EQ(buf[5000], 0xbb, "xdp_update_frag buf[5000]");
 	ASSERT_EQ(buf[5015], 0xbb, "xdp_update_frag buf[5015]");

@@ -68,12 +78,11 @@ void test_xdp_update_frags(void)
 	buf[*offset] = 0xaa;		/* marker at offset 3510 (head) */
 	buf[*offset + 15] = 0xaa;	/* marker at offset 3525 (frag0) */

-	err = bpf_prog_test_run(prog_fd, 1, buf, 9000,
-				buf, &size, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	/* test_xdp_update_frags: buf[3510,3525]: 0xaa -> 0xbb */
 	ASSERT_OK(err, "xdp_update_frag");
-	ASSERT_EQ(retval, XDP_PASS, "xdp_update_frag retval");
+	ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval");
 	ASSERT_EQ(buf[3510], 0xbb, "xdp_update_frag buf[3510]");
 	ASSERT_EQ(buf[3525], 0xbb, "xdp_update_frag buf[3525]");

@@ -83,12 +92,11 @@ void test_xdp_update_frags(void)
 	buf[*offset] = 0xaa;		/* marker at offset 7606 (frag0) */
 	buf[*offset + 15] = 0xaa;	/* marker at offset 7621 (frag1) */

-	err = bpf_prog_test_run(prog_fd, 1, buf, 9000,
-				buf, &size, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	/* test_xdp_update_frags: buf[7606,7621]: 0xaa -> 0xbb */
 	ASSERT_OK(err, "xdp_update_frag");
-	ASSERT_EQ(retval, XDP_PASS, "xdp_update_frag retval");
+	ASSERT_EQ(topts.retval, XDP_PASS, "xdp_update_frag retval");
 	ASSERT_EQ(buf[7606], 0xbb, "xdp_update_frag buf[7606]");
 	ASSERT_EQ(buf[7621], 0xbb, "xdp_update_frag buf[7621]");

diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
index ccc9e63254a8..bb1b833318d3 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
@@ -5,26 +5,34 @@
 static void test_xdp_adjust_tail_shrink(void)
 {
 	const char *file = "./test_xdp_adjust_tail_shrink.o";
-	__u32 duration, retval, size, expect_sz;
+	__u32 expect_sz;
 	struct bpf_object *obj;
 	int err, prog_fd;
 	char buf[128];
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
 	if (ASSERT_OK(err, "test_xdp_adjust_tail_shrink"))
 		return;

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				buf, &size, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "ipv4");
-	ASSERT_EQ(retval, XDP_DROP, "ipv4 retval");
+	ASSERT_EQ(topts.retval, XDP_DROP, "ipv4 retval");

 	expect_sz = sizeof(pkt_v6) - 20;  /* Test shrink with 20 bytes */
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6),
-				buf, &size, &retval, &duration);
+	topts.data_in = &pkt_v6;
+	topts.data_size_in = sizeof(pkt_v6);
+	topts.data_size_out = sizeof(buf);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "ipv6");
-	ASSERT_EQ(retval, XDP_TX, "ipv6 retval");
-	ASSERT_EQ(size, expect_sz, "ipv6 size");
+	ASSERT_EQ(topts.retval, XDP_TX, "ipv6 retval");
+	ASSERT_EQ(topts.data_size_out, expect_sz, "ipv6 size");

 	bpf_object__close(obj);
 }
@@ -34,24 +42,31 @@ static void test_xdp_adjust_tail_grow(void)
 	const char *file = "./test_xdp_adjust_tail_grow.o";
 	struct bpf_object *obj;
 	char buf[4096]; /* avoid segfault: large buf to hold grow results */
-	__u32 duration, retval, size, expect_sz;
+	__u32 expect_sz;
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = 1,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
 	if (ASSERT_OK(err, "test_xdp_adjust_tail_grow"))
 		return;

-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
-				buf, &size, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "ipv4");
-	ASSERT_EQ(retval, XDP_DROP, "ipv4 retval");
+	ASSERT_EQ(topts.retval, XDP_DROP, "ipv4 retval");

 	expect_sz = sizeof(pkt_v6) + 40; /* Test grow with 40 bytes */
-	err = bpf_prog_test_run(prog_fd, 1, &pkt_v6, sizeof(pkt_v6) /* 74 */,
-				buf, &size, &retval, &duration);
+	topts.data_in = &pkt_v6;
+	topts.data_size_in = sizeof(pkt_v6);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_OK(err, "ipv6");
-	ASSERT_EQ(retval, XDP_TX, "ipv6 retval");
-	ASSERT_EQ(size, expect_sz, "ipv6 size");
+	ASSERT_EQ(topts.retval, XDP_TX, "ipv6 retval");
+	ASSERT_EQ(topts.data_size_out, expect_sz, "ipv6 size");

 	bpf_object__close(obj);
 }
@@ -121,11 +136,14 @@ static void test_xdp_adjust_tail_grow2(void)
 void test_xdp_adjust_frags_tail_shrink(void)
 {
 	const char *file = "./test_xdp_adjust_tail_shrink.o";
-	__u32 duration, retval, size, exp_size;
+	__u32 exp_size;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	int err, prog_fd;
 	__u8 *buf;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	/* For the individual test cases, the first byte in the packet
 	 * indicates which test will be run.
@@ -148,32 +166,36 @@ void test_xdp_adjust_frags_tail_shrink(void)

 	/* Test case removing 10 bytes from last frag, NOT freeing it */
 	exp_size = 8990; /* 9000 - 10 */
-	err = bpf_prog_test_run(prog_fd, 1, buf, 9000,
-				buf, &size, &retval, &duration);
+	topts.data_in = buf;
+	topts.data_out = buf;
+	topts.data_size_in = 9000;
+	topts.data_size_out = 9000;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_OK(err, "9Kb-10b");
-	ASSERT_EQ(retval, XDP_TX, "9Kb-10b retval");
-	ASSERT_EQ(size, exp_size, "9Kb-10b size");
+	ASSERT_EQ(topts.retval, XDP_TX, "9Kb-10b retval");
+	ASSERT_EQ(topts.data_size_out, exp_size, "9Kb-10b size");

 	/* Test case removing one of two pages, assuming 4K pages */
 	buf[0] = 1;
 	exp_size = 4900; /* 9000 - 4100 */
-	err = bpf_prog_test_run(prog_fd, 1, buf, 9000,
-				buf, &size, &retval, &duration);
+
+	topts.data_size_out = 9000; /* reset from previous invocation */
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_OK(err, "9Kb-4Kb");
-	ASSERT_EQ(retval, XDP_TX, "9Kb-4Kb retval");
-	ASSERT_EQ(size, exp_size, "9Kb-4Kb size");
+	ASSERT_EQ(topts.retval, XDP_TX, "9Kb-4Kb retval");
+	ASSERT_EQ(topts.data_size_out, exp_size, "9Kb-4Kb size");

 	/* Test case removing two pages resulting in a linear xdp_buff */
 	buf[0] = 2;
 	exp_size = 800; /* 9000 - 8200 */
-	err = bpf_prog_test_run(prog_fd, 1, buf, 9000,
-				buf, &size, &retval, &duration);
+	topts.data_size_out = 9000; /* reset from previous invocation */
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_OK(err, "9Kb-9Kb");
-	ASSERT_EQ(retval, XDP_TX, "9Kb-9Kb retval");
-	ASSERT_EQ(size, exp_size, "9Kb-9Kb size");
+	ASSERT_EQ(topts.retval, XDP_TX, "9Kb-9Kb retval");
+	ASSERT_EQ(topts.data_size_out, exp_size, "9Kb-9Kb size");

 	free(buf);
 out:
@@ -183,11 +205,14 @@ void test_xdp_adjust_frags_tail_shrink(void)
 void test_xdp_adjust_frags_tail_grow(void)
 {
 	const char *file = "./test_xdp_adjust_tail_grow.o";
-	__u32 duration, retval, size, exp_size;
+	__u32 exp_size;
 	struct bpf_program *prog;
 	struct bpf_object *obj;
 	int err, i, prog_fd;
 	__u8 *buf;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	obj = bpf_object__open(file);
 	if (libbpf_get_error(obj))
@@ -205,14 +230,17 @@ void test_xdp_adjust_frags_tail_grow(void)

 	/* Test case add 10 bytes to last frag */
 	memset(buf, 1, 16384);
-	size = 9000;
-	exp_size = size + 10;
-	err = bpf_prog_test_run(prog_fd, 1, buf, size,
-				buf, &size, &retval, &duration);
+	exp_size = 9000 + 10;
+
+	topts.data_in = buf;
+	topts.data_out = buf;
+	topts.data_size_in = 9000;
+	topts.data_size_out = 16384;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_OK(err, "9Kb+10b");
-	ASSERT_EQ(retval, XDP_TX, "9Kb+10b retval");
-	ASSERT_EQ(size, exp_size, "9Kb+10b size");
+	ASSERT_EQ(topts.retval, XDP_TX, "9Kb+10b retval");
+	ASSERT_EQ(topts.data_size_out, exp_size, "9Kb+10b size");

 	for (i = 0; i < 9000; i++)
 		ASSERT_EQ(buf[i], 1, "9Kb+10b-old");
@@ -225,14 +253,16 @@ void test_xdp_adjust_frags_tail_grow(void)

 	/* Test a too large grow */
 	memset(buf, 1, 16384);
-	size = 9001;
-	exp_size = size;
-	err = bpf_prog_test_run(prog_fd, 1, buf, size,
-				buf, &size, &retval, &duration);
+	exp_size = 9001;
+
+	topts.data_in = topts.data_out = buf;
+	topts.data_size_in = 9001;
+	topts.data_size_out = 16384;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_OK(err, "9Kb+10b");
-	ASSERT_EQ(retval, XDP_DROP, "9Kb+10b retval");
-	ASSERT_EQ(size, exp_size, "9Kb+10b size");
+	ASSERT_EQ(topts.retval, XDP_DROP, "9Kb+10b retval");
+	ASSERT_EQ(topts.data_size_out, exp_size, "9Kb+10b size");

 	free(buf);
 out:
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c
index 9c395ea680c6..836cbdafa527 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c
@@ -45,9 +45,11 @@ static void run_xdp_bpf2bpf_pkt_size(int pkt_fd, struct perf_buffer *pb,
 				     struct test_xdp_bpf2bpf *ftrace_skel,
 				     int pkt_size)
 {
-	__u32 duration = 0, retval, size;
 	__u8 *buf, *buf_in;
 	int err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1,
+	);

 	if (!ASSERT_LE(pkt_size, BUF_SZ, "pkt_size") ||
 	    !ASSERT_GE(pkt_size, sizeof(pkt_v4), "pkt_size"))
@@ -73,12 +75,16 @@ static void run_xdp_bpf2bpf_pkt_size(int pkt_fd, struct perf_buffer *pb,
 	}

 	/* Run test program */
-	err = bpf_prog_test_run(pkt_fd, 1, buf_in, pkt_size,
-				buf, &size, &retval, &duration);
+	topts.data_in = buf_in;
+	topts.data_size_in = pkt_size;
+	topts.data_out = buf;
+	topts.data_size_out = BUF_SZ;
+
+	err = bpf_prog_test_run_opts(pkt_fd, &topts);

 	ASSERT_OK(err, "ipv4");
-	ASSERT_EQ(retval, XDP_PASS, "ipv4 retval");
-	ASSERT_EQ(size, pkt_size, "ipv4 size");
+	ASSERT_EQ(topts.retval, XDP_PASS, "ipv4 retval");
+	ASSERT_EQ(topts.data_size_out, pkt_size, "ipv4 size");

 	/* Make sure bpf_xdp_output() was triggered and it sent the expected
 	 * data to the perf ring buffer.
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c b/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c
index 0281095de266..ec1d6e68bdbc 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_noinline.c
@@ -25,43 +25,50 @@ void test_xdp_noinline(void)
 		__u8 flags;
 	} real_def = {.dst = MAGIC_VAL};
 	__u32 ch_key = 11, real_num = 3;
-	__u32 duration = 0, retval, size;
 	int err, i;
 	__u64 bytes = 0, pkts = 0;
 	char buf[128];
 	u32 *magic = (u32 *)buf;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = &pkt_v4,
+		.data_size_in = sizeof(pkt_v4),
+		.data_out = buf,
+		.data_size_out = sizeof(buf),
+		.repeat = NUM_ITER,
+	);

 	skel = test_xdp_noinline__open_and_load();
-	if (CHECK(!skel, "skel_open_and_load", "failed\n"))
+	if (CHECK_OPTS(!skel, "skel_open_and_load", "failed\n"))
 		return;

 	bpf_map_update_elem(bpf_map__fd(skel->maps.vip_map), &key, &value, 0);
 	bpf_map_update_elem(bpf_map__fd(skel->maps.ch_rings), &ch_key, &real_num, 0);
 	bpf_map_update_elem(bpf_map__fd(skel->maps.reals), &real_num, &real_def, 0);

-	err = bpf_prog_test_run(bpf_program__fd(skel->progs.balancer_ingress_v4),
-				NUM_ITER, &pkt_v4, sizeof(pkt_v4),
-				buf, &size, &retval, &duration);
-	CHECK(err || retval != 1 || size != 54 ||
-	      *magic != MAGIC_VAL, "ipv4",
-	      "err %d errno %d retval %d size %d magic %x\n",
-	      err, errno, retval, size, *magic);
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.balancer_ingress_v4), &topts);
+	CHECK_OPTS(err || topts.retval != 1 || topts.data_size_out != 54 ||
+		   *magic != MAGIC_VAL,
+		   "ipv4", "err %d errno %d retval %d size %d magic %x\n", err,
+		   errno, topts.retval, topts.data_size_out, *magic);

-	err = bpf_prog_test_run(bpf_program__fd(skel->progs.balancer_ingress_v6),
-				NUM_ITER, &pkt_v6, sizeof(pkt_v6),
-				buf, &size, &retval, &duration);
-	CHECK(err || retval != 1 || size != 74 ||
-	      *magic != MAGIC_VAL, "ipv6",
-	      "err %d errno %d retval %d size %d magic %x\n",
-	      err, errno, retval, size, *magic);
+	topts.data_in = &pkt_v6;
+	topts.data_size_in = sizeof(pkt_v6);
+	topts.data_out = buf;
+	topts.data_size_out = sizeof(buf);
+
+	err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.balancer_ingress_v6), &topts);
+	CHECK_OPTS(err || topts.retval != 1 || topts.data_size_out != 74 ||
+		   *magic != MAGIC_VAL,
+		   "ipv6", "err %d errno %d retval %d size %d magic %x\n", err,
+		   errno, topts.retval, topts.data_size_out, *magic);

 	bpf_map_lookup_elem(bpf_map__fd(skel->maps.stats), &stats_key, stats);
 	for (i = 0; i < nr_cpus; i++) {
 		bytes += stats[i].bytes;
 		pkts += stats[i].pkts;
 	}
-	CHECK(bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2,
-	      "stats", "bytes %lld pkts %lld\n",
-	      (unsigned long long)bytes, (unsigned long long)pkts);
+	CHECK_OPTS(bytes != MAGIC_BYTES * NUM_ITER * 2 || pkts != NUM_ITER * 2,
+		   "stats", "bytes %lld pkts %lld\n", (unsigned long long)bytes,
+		   (unsigned long long)pkts);
 	test_xdp_noinline__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_perf.c b/tools/testing/selftests/bpf/prog_tests/xdp_perf.c
index 15a3900e4370..d46ebbda7b49 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_perf.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_perf.c
@@ -4,22 +4,27 @@
 void test_xdp_perf(void)
 {
 	const char *file = "./xdp_dummy.o";
-	__u32 duration, retval, size;
 	struct bpf_object *obj;
 	char in[128], out[128];
 	int err, prog_fd;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = in,
+		.data_size_in = sizeof(in),
+		.data_out = out,
+		.data_size_out = sizeof(out),
+		.repeat = 1000000,
+	);

 	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
 	if (CHECK_FAIL(err))
 		return;

-	err = bpf_prog_test_run(prog_fd, 1000000, &in[0], 128,
-				out, &size, &retval, &duration);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

-	CHECK(err || retval != XDP_PASS || size != 128,
-	      "xdp-perf",
-	      "err %d errno %d retval %d size %d\n",
-	      err, errno, retval, size);
+	CHECK_OPTS(err || topts.retval != XDP_PASS ||
+		   topts.data_size_out != 128,
+		   "xdp-perf", "err %d errno %d retval %d size %d\n", err,
+		   errno, topts.retval, topts.data_size_out);

 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/test_lru_map.c b/tools/testing/selftests/bpf/test_lru_map.c
index b9f1bbbc8aba..6e6235185a86 100644
--- a/tools/testing/selftests/bpf/test_lru_map.c
+++ b/tools/testing/selftests/bpf/test_lru_map.c
@@ -61,7 +61,11 @@ static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
 	};
 	__u8 data[64] = {};
 	int mfd, pfd, ret, zero = 0;
-	__u32 retval = 0;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = data,
+		.data_size_in = sizeof(data),
+		.repeat = 1,
+	);

 	mfd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), sizeof(__u64), 1, NULL);
 	if (mfd < 0)
@@ -75,9 +79,8 @@ static int bpf_map_lookup_elem_with_ref_bit(int fd, unsigned long long key,
 		return -1;
 	}

-	ret = bpf_prog_test_run(pfd, 1, data, sizeof(data),
-				NULL, NULL, &retval, NULL);
-	if (ret < 0 || retval != 42) {
+	ret = bpf_prog_test_run_opts(pfd, &topts);
+	if (ret < 0 || topts.retval != 42) {
 		ret = -1;
 	} else {
 		assert(!bpf_map_lookup_elem(mfd, &zero, value));
diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h
index 93c1ff705533..1afd90965afe 100644
--- a/tools/testing/selftests/bpf/test_progs.h
+++ b/tools/testing/selftests/bpf/test_progs.h
@@ -163,6 +163,8 @@ extern int test__join_cgroup(const char *path);
 	_CHECK(condition, tag, duration, format)
 #define CHECK_ATTR(condition, tag, format...) \
 	_CHECK(condition, tag, tattr.duration, format)
+#define CHECK_OPTS(condition, tag, format...) \
+	_CHECK(condition, tag, topts.duration, format)

 #define ASSERT_TRUE(actual, name) ({					\
 	static int duration = 0;					\
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index 29bbaa58233c..163b303e8a2a 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -1021,13 +1021,18 @@ static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
 {
 	__u8 tmp[TEST_DATA_LEN << 2];
 	__u32 size_tmp = sizeof(tmp);
-	uint32_t retval;
 	int err, saved_errno;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = data,
+		.data_size_in = size_data,
+		.data_out = tmp,
+		.data_size_out = size_tmp,
+		.repeat = 1,
+	);

 	if (unpriv)
 		set_admin(true);
-	err = bpf_prog_test_run(fd_prog, 1, data, size_data,
-				tmp, &size_tmp, &retval, NULL);
+	err = bpf_prog_test_run_opts(fd_prog, &topts);
 	saved_errno = errno;

 	if (unpriv)
@@ -1051,9 +1056,8 @@ static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
 		}
 	}

-	if (retval != expected_val &&
-	    expected_val != POINTER_VALUE) {
-		printf("FAIL retval %d != %d ", retval, expected_val);
+	if (topts.retval != expected_val && expected_val != POINTER_VALUE) {
+		printf("FAIL retval %d != %d ", topts.retval, expected_val);
 		return 1;
 	}

--
2.30.2

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

* [PATCH bpf-next v2 2/4] selftests: bpf: migrate from bpf_prog_test_run_xattr
  2022-01-28  1:23 [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Delyan Kratunov
  2022-01-28  1:23 ` [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run Delyan Kratunov
@ 2022-01-28  1:23 ` Delyan Kratunov
  2022-02-01  1:32   ` Andrii Nakryiko
  2022-01-28  1:23 ` [PATCH bpf-next v2 3/4] bpftool: " Delyan Kratunov
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Delyan Kratunov @ 2022-01-28  1:23 UTC (permalink / raw)
  To: bpf, ast, andrii, daniel; +Cc: Delyan Kratunov

Signed-off-by: Delyan Kratunov <delyank@fb.com>
---
 .../selftests/bpf/prog_tests/check_mtu.c      | 47 +++++-----
 .../selftests/bpf/prog_tests/cls_redirect.c   | 30 +++---
 .../selftests/bpf/prog_tests/dummy_st_ops.c   | 31 +++----
 .../selftests/bpf/prog_tests/flow_dissector.c | 75 ++++++++-------
 .../selftests/bpf/prog_tests/kfree_skb.c      | 16 ++--
 .../selftests/bpf/prog_tests/prog_run_xattr.c |  5 +
 .../bpf/prog_tests/raw_tp_test_run.c          | 85 ++++++++---------
 .../selftests/bpf/prog_tests/skb_ctx.c        | 93 ++++++++-----------
 .../selftests/bpf/prog_tests/skb_helpers.c    | 16 ++--
 .../selftests/bpf/prog_tests/sockmap_basic.c  | 19 ++--
 .../selftests/bpf/prog_tests/syscall.c        | 12 +--
 .../selftests/bpf/prog_tests/test_profiler.c  | 16 ++--
 .../bpf/prog_tests/xdp_adjust_tail.c          | 34 +++----
 13 files changed, 227 insertions(+), 252 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/check_mtu.c b/tools/testing/selftests/bpf/prog_tests/check_mtu.c
index f73e6e36b74d..b5ab3ef7e9d3 100644
--- a/tools/testing/selftests/bpf/prog_tests/check_mtu.c
+++ b/tools/testing/selftests/bpf/prog_tests/check_mtu.c
@@ -83,24 +83,22 @@ static void test_check_mtu_run_xdp(struct test_check_mtu *skel,
 	int retval_expect = XDP_PASS;
 	__u32 mtu_result = 0;
 	char buf[256] = {};
-	int err;
-	struct bpf_prog_test_run_attr tattr = {
-		.repeat = 1,
-		.data_in = &pkt_v4,
+	int err, prog_fd = bpf_program__fd(prog);
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.repeat = 1, .data_in = &pkt_v4,
 		.data_size_in = sizeof(pkt_v4),
 		.data_out = buf,
 		.data_size_out = sizeof(buf),
-		.prog_fd = bpf_program__fd(prog),
-	};
+	);

-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err != 0, "bpf_prog_test_run",
-		   "prog_name:%s (err %d errno %d retval %d)\n",
-		   prog_name, err, errno, tattr.retval);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err != 0, "bpf_prog_test_run",
+		   "prog_name:%s (err %d errno %d retval %d)\n", prog_name, err,
+		   errno, topts.retval);

-	CHECK(tattr.retval != retval_expect, "retval",
-	      "progname:%s unexpected retval=%d expected=%d\n",
-	      prog_name, tattr.retval, retval_expect);
+	CHECK(topts.retval != retval_expect, "retval",
+	      "progname:%s unexpected retval=%d expected=%d\n", prog_name,
+	      topts.retval, retval_expect);

 	/* Extract MTU that BPF-prog got */
 	mtu_result = skel->bss->global_bpf_mtu_xdp;
@@ -143,24 +141,23 @@ static void test_check_mtu_run_tc(struct test_check_mtu *skel,
 	int retval_expect = BPF_OK;
 	__u32 mtu_result = 0;
 	char buf[256] = {};
-	int err;
-	struct bpf_prog_test_run_attr tattr = {
-		.repeat = 1,
+	int err, prog_fd = bpf_program__fd(prog);
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.data_in = &pkt_v4,
 		.data_size_in = sizeof(pkt_v4),
 		.data_out = buf,
 		.data_size_out = sizeof(buf),
-		.prog_fd = bpf_program__fd(prog),
-	};
+		.repeat = 1,
+	);

-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err != 0, "bpf_prog_test_run",
-		   "prog_name:%s (err %d errno %d retval %d)\n",
-		   prog_name, err, errno, tattr.retval);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err != 0, "bpf_prog_test_run",
+		   "prog_name:%s (err %d errno %d retval %d)\n", prog_name, err,
+		   errno, topts.retval);

-	CHECK(tattr.retval != retval_expect, "retval",
-	      "progname:%s unexpected retval=%d expected=%d\n",
-	      prog_name, tattr.retval, retval_expect);
+	CHECK(topts.retval != retval_expect, "retval",
+	      "progname:%s unexpected retval=%d expected=%d\n", prog_name,
+	      topts.retval, retval_expect);

 	/* Extract MTU that BPF-prog got */
 	mtu_result = skel->bss->global_bpf_mtu_tc;
diff --git a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c
index e075d03ab630..fec0d3dd00ce 100644
--- a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c
+++ b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c
@@ -161,9 +161,9 @@ static socklen_t prepare_addr(struct sockaddr_storage *addr, int family)
 	}
 }

-static bool was_decapsulated(struct bpf_prog_test_run_attr *tattr)
+static bool was_decapsulated(struct bpf_test_run_opts *topts)
 {
-	return tattr->data_size_out < tattr->data_size_in;
+	return topts->data_size_out < topts->data_size_in;
 }

 enum type {
@@ -367,12 +367,12 @@ static void close_fds(int *fds, int n)

 static void test_cls_redirect_common(struct bpf_program *prog)
 {
-	struct bpf_prog_test_run_attr tattr = {};
+	LIBBPF_OPTS(bpf_test_run_opts, topts);
 	int families[] = { AF_INET, AF_INET6 };
 	struct sockaddr_storage ss;
 	struct sockaddr *addr;
 	socklen_t slen;
-	int i, j, err;
+	int i, j, err, prog_fd;
 	int servers[__NR_KIND][ARRAY_SIZE(families)] = {};
 	int conns[__NR_KIND][ARRAY_SIZE(families)] = {};
 	struct tuple tuples[__NR_KIND][ARRAY_SIZE(families)];
@@ -394,7 +394,7 @@ static void test_cls_redirect_common(struct bpf_program *prog)
 			goto cleanup;
 	}

-	tattr.prog_fd = bpf_program__fd(prog);
+	prog_fd = bpf_program__fd(prog);
 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 		struct test_cfg *test = &tests[i];

@@ -407,31 +407,31 @@ static void test_cls_redirect_common(struct bpf_program *prog)
 			if (!test__start_subtest(tmp))
 				continue;

-			tattr.data_out = tmp;
-			tattr.data_size_out = sizeof(tmp);
+			topts.data_out = tmp;
+			topts.data_size_out = sizeof(tmp);

-			tattr.data_in = input;
-			tattr.data_size_in = build_input(test, input, tuple);
-			if (CHECK_FAIL(!tattr.data_size_in))
+			topts.data_in = input;
+			topts.data_size_in = build_input(test, input, tuple);
+			if (CHECK_FAIL(!topts.data_size_in))
 				continue;

-			err = bpf_prog_test_run_xattr(&tattr);
+			err = bpf_prog_test_run_opts(prog_fd, &topts);
 			if (CHECK_FAIL(err))
 				continue;

-			if (tattr.retval != TC_ACT_REDIRECT) {
+			if (topts.retval != TC_ACT_REDIRECT) {
 				PRINT_FAIL("expected TC_ACT_REDIRECT, got %d\n",
-					   tattr.retval);
+					   topts.retval);
 				continue;
 			}

 			switch (test->result) {
 			case ACCEPT:
-				if (CHECK_FAIL(!was_decapsulated(&tattr)))
+				if (CHECK_FAIL(!was_decapsulated(&topts)))
 					continue;
 				break;
 			case FORWARD:
-				if (CHECK_FAIL(was_decapsulated(&tattr)))
+				if (CHECK_FAIL(was_decapsulated(&topts)))
 					continue;
 				break;
 			default:
diff --git a/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c b/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c
index cbaa44ffb8c6..81b2f670526a 100644
--- a/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c
+++ b/tools/testing/selftests/bpf/prog_tests/dummy_st_ops.c
@@ -26,10 +26,10 @@ static void test_dummy_st_ops_attach(void)
 static void test_dummy_init_ret_value(void)
 {
 	__u64 args[1] = {0};
-	struct bpf_prog_test_run_attr attr = {
-		.ctx_size_in = sizeof(args),
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.ctx_in = args,
-	};
+		.ctx_size_in = sizeof(args),
+	);
 	struct dummy_st_ops *skel;
 	int fd, err;

@@ -38,10 +38,9 @@ static void test_dummy_init_ret_value(void)
 		return;

 	fd = bpf_program__fd(skel->progs.test_1);
-	attr.prog_fd = fd;
-	err = bpf_prog_test_run_xattr(&attr);
+	err = bpf_prog_test_run_opts(fd, &topts);
 	ASSERT_OK(err, "test_run");
-	ASSERT_EQ(attr.retval, 0xf2f3f4f5, "test_ret");
+	ASSERT_EQ(topts.retval, 0xf2f3f4f5, "test_ret");

 	dummy_st_ops__destroy(skel);
 }
@@ -53,10 +52,10 @@ static void test_dummy_init_ptr_arg(void)
 		.val = exp_retval,
 	};
 	__u64 args[1] = {(unsigned long)&in_state};
-	struct bpf_prog_test_run_attr attr = {
-		.ctx_size_in = sizeof(args),
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.ctx_in = args,
-	};
+		.ctx_size_in = sizeof(args),
+	);
 	struct dummy_st_ops *skel;
 	int fd, err;

@@ -65,11 +64,10 @@ static void test_dummy_init_ptr_arg(void)
 		return;

 	fd = bpf_program__fd(skel->progs.test_1);
-	attr.prog_fd = fd;
-	err = bpf_prog_test_run_xattr(&attr);
+	err = bpf_prog_test_run_opts(fd, &topts);
 	ASSERT_OK(err, "test_run");
 	ASSERT_EQ(in_state.val, 0x5a, "test_ptr_ret");
-	ASSERT_EQ(attr.retval, exp_retval, "test_ret");
+	ASSERT_EQ(topts.retval, exp_retval, "test_ret");

 	dummy_st_ops__destroy(skel);
 }
@@ -77,10 +75,10 @@ static void test_dummy_init_ptr_arg(void)
 static void test_dummy_multiple_args(void)
 {
 	__u64 args[5] = {0, -100, 0x8a5f, 'c', 0x1234567887654321ULL};
-	struct bpf_prog_test_run_attr attr = {
-		.ctx_size_in = sizeof(args),
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.ctx_in = args,
-	};
+		.ctx_size_in = sizeof(args),
+	);
 	struct dummy_st_ops *skel;
 	int fd, err;
 	size_t i;
@@ -91,8 +89,7 @@ static void test_dummy_multiple_args(void)
 		return;

 	fd = bpf_program__fd(skel->progs.test_2);
-	attr.prog_fd = fd;
-	err = bpf_prog_test_run_xattr(&attr);
+	err = bpf_prog_test_run_opts(fd, &topts);
 	ASSERT_OK(err, "test_run");
 	for (i = 0; i < ARRAY_SIZE(args); i++) {
 		snprintf(name, sizeof(name), "arg %zu", i);
diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
index dfafd62df50b..0bcf8e503c5f 100644
--- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
+++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c
@@ -13,30 +13,30 @@
 #endif

 #define CHECK_FLOW_KEYS(desc, got, expected)				\
-	CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0,		\
-	      desc,							\
-	      "nhoff=%u/%u "						\
-	      "thoff=%u/%u "						\
-	      "addr_proto=0x%x/0x%x "					\
-	      "is_frag=%u/%u "						\
-	      "is_first_frag=%u/%u "					\
-	      "is_encap=%u/%u "						\
-	      "ip_proto=0x%x/0x%x "					\
-	      "n_proto=0x%x/0x%x "					\
-	      "flow_label=0x%x/0x%x "					\
-	      "sport=%u/%u "						\
-	      "dport=%u/%u\n",						\
-	      got.nhoff, expected.nhoff,				\
-	      got.thoff, expected.thoff,				\
-	      got.addr_proto, expected.addr_proto,			\
-	      got.is_frag, expected.is_frag,				\
-	      got.is_first_frag, expected.is_first_frag,		\
-	      got.is_encap, expected.is_encap,				\
-	      got.ip_proto, expected.ip_proto,				\
-	      got.n_proto, expected.n_proto,				\
-	      got.flow_label, expected.flow_label,			\
-	      got.sport, expected.sport,				\
-	      got.dport, expected.dport)
+	CHECK_OPTS(memcmp(&got, &expected, sizeof(got)) != 0,		\
+			   desc,							\
+			   "nhoff=%u/%u "						\
+			   "thoff=%u/%u "						\
+			   "addr_proto=0x%x/0x%x "					\
+			   "is_frag=%u/%u "						\
+			   "is_first_frag=%u/%u "					\
+			   "is_encap=%u/%u "						\
+			   "ip_proto=0x%x/0x%x "					\
+			   "n_proto=0x%x/0x%x "					\
+			   "flow_label=0x%x/0x%x "					\
+			   "sport=%u/%u "						\
+			   "dport=%u/%u\n",						\
+			   got.nhoff, expected.nhoff,				\
+			   got.thoff, expected.thoff,				\
+			   got.addr_proto, expected.addr_proto,			\
+			   got.is_frag, expected.is_frag,				\
+			   got.is_first_frag, expected.is_first_frag,		\
+			   got.is_encap, expected.is_encap,				\
+			   got.ip_proto, expected.ip_proto,				\
+			   got.n_proto, expected.n_proto,				\
+			   got.flow_label, expected.flow_label,			\
+			   got.sport, expected.sport,				\
+			   got.dport, expected.dport)

 struct ipv4_pkt {
 	struct ethhdr eth;
@@ -487,7 +487,7 @@ static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
 		/* Keep in sync with 'flags' from eth_get_headlen. */
 		__u32 eth_get_headlen_flags =
 			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
-		struct bpf_prog_test_run_attr tattr = {};
+		LIBBPF_OPTS(bpf_test_run_opts, topts);
 		struct bpf_flow_keys flow_keys = {};
 		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
 			    tests[i].keys.dport;
@@ -503,13 +503,13 @@ static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
 		CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);

 		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
-		CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
+		CHECK_OPTS(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);

-		CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
+		CHECK_OPTS(err, tests[i].name, "skb-less err %d\n", err);
 		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);

 		err = bpf_map_delete_elem(keys_fd, &key);
-		CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
+		CHECK_OPTS(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
 	}
 }

@@ -573,27 +573,26 @@ void test_flow_dissector(void)

 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 		struct bpf_flow_keys flow_keys;
-		struct bpf_prog_test_run_attr tattr = {
-			.prog_fd = prog_fd,
+		LIBBPF_OPTS(bpf_test_run_opts, topts,
 			.data_in = &tests[i].pkt,
 			.data_size_in = sizeof(tests[i].pkt),
 			.data_out = &flow_keys,
-		};
+		);
 		static struct bpf_flow_keys ctx = {};

 		if (tests[i].flags) {
-			tattr.ctx_in = &ctx;
-			tattr.ctx_size_in = sizeof(ctx);
+			topts.ctx_in = &ctx;
+			topts.ctx_size_in = sizeof(ctx);
 			ctx.flags = tests[i].flags;
 		}

-		err = bpf_prog_test_run_xattr(&tattr);
-		CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
-			   err || tattr.retval != 1,
+		err = bpf_prog_test_run_opts(prog_fd, &topts);
+		CHECK_OPTS(topts.data_size_out != sizeof(flow_keys) ||
+			   err || topts.retval != 1,
 			   tests[i].name,
 			   "err %d errno %d retval %d duration %d size %u/%zu\n",
-			   err, errno, tattr.retval, tattr.duration,
-			   tattr.data_size_out, sizeof(flow_keys));
+			   err, errno, topts.retval, topts.duration,
+			   topts.data_size_out, sizeof(flow_keys));
 		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
 	}

diff --git a/tools/testing/selftests/bpf/prog_tests/kfree_skb.c b/tools/testing/selftests/bpf/prog_tests/kfree_skb.c
index ce10d2fc3a6c..7e50d6147e08 100644
--- a/tools/testing/selftests/bpf/prog_tests/kfree_skb.c
+++ b/tools/testing/selftests/bpf/prog_tests/kfree_skb.c
@@ -53,24 +53,24 @@ static void on_sample(void *ctx, int cpu, void *data, __u32 size)
 void serial_test_kfree_skb(void)
 {
 	struct __sk_buff skb = {};
-	struct bpf_prog_test_run_attr tattr = {
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.data_in = &pkt_v6,
 		.data_size_in = sizeof(pkt_v6),
 		.ctx_in = &skb,
 		.ctx_size_in = sizeof(skb),
-	};
+	);
 	struct kfree_skb *skel = NULL;
 	struct bpf_link *link;
 	struct bpf_object *obj;
 	struct perf_buffer *pb = NULL;
-	int err;
+	int err, prog_fd;
 	bool passed = false;
 	__u32 duration = 0;
 	const int zero = 0;
 	bool test_ok[2];

 	err = bpf_prog_test_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS,
-			    &obj, &tattr.prog_fd);
+				 &obj, &prog_fd);
 	if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno))
 		return;

@@ -100,11 +100,11 @@ void serial_test_kfree_skb(void)
 		goto close_prog;

 	memcpy(skb.cb, &cb, sizeof(cb));
-	err = bpf_prog_test_run_xattr(&tattr);
-	duration = tattr.duration;
-	CHECK(err || tattr.retval, "ipv6",
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	duration = topts.duration;
+	CHECK(err || topts.retval, "ipv6",
 	      "err %d errno %d retval %d duration %d\n",
-	      err, errno, tattr.retval, duration);
+	      err, errno, topts.retval, duration);

 	/* read perf buffer */
 	err = perf_buffer__poll(pb, 100);
diff --git a/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c b/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c
index 89fc98faf19e..53c75fa7acac 100644
--- a/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c
+++ b/tools/testing/selftests/bpf/prog_tests/prog_run_xattr.c
@@ -20,6 +20,9 @@ static void check_run_cnt(int prog_fd, __u64 run_cnt)
 	      "incorrect number of repetitions, want %llu have %llu\n", run_cnt, info.run_cnt);
 }

+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
 void test_prog_run_xattr(void)
 {
 	struct test_pkt_access *skel;
@@ -81,3 +84,5 @@ void test_prog_run_xattr(void)
 	if (stats_fd >= 0)
 		close(stats_fd);
 }
+
+#pragma GCC diagnostic pop
diff --git a/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c b/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c
index 41720a62c4fa..8ba2681c9ffa 100644
--- a/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c
+++ b/tools/testing/selftests/bpf/prog_tests/raw_tp_test_run.c
@@ -5,89 +5,84 @@
 #include "bpf/libbpf_internal.h"
 #include "test_raw_tp_test_run.skel.h"

-static int duration;
-
 void test_raw_tp_test_run(void)
 {
-	struct bpf_prog_test_run_attr test_attr = {};
 	int comm_fd = -1, err, nr_online, i, prog_fd;
 	__u64 args[2] = {0x1234ULL, 0x5678ULL};
 	int expected_retval = 0x1234 + 0x5678;
 	struct test_raw_tp_test_run *skel;
 	char buf[] = "new_name";
 	bool *online = NULL;
-	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
-			    .ctx_in = args,
-			    .ctx_size_in = sizeof(args),
-			    .flags = BPF_F_TEST_RUN_ON_CPU,
-		);
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.ctx_in = args,
+		.ctx_size_in = sizeof(args),
+	);

 	err = parse_cpu_mask_file("/sys/devices/system/cpu/online", &online,
 				  &nr_online);
-	if (CHECK(err, "parse_cpu_mask_file", "err %d\n", err))
+	if (CHECK_OPTS(err, "parse_cpu_mask_file", "err %d\n", err))
 		return;

 	skel = test_raw_tp_test_run__open_and_load();
-	if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
+	if (CHECK_OPTS(!skel, "skel_open", "failed to open skeleton\n"))
 		goto cleanup;

 	err = test_raw_tp_test_run__attach(skel);
-	if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
+	if (CHECK_OPTS(err, "skel_attach", "skeleton attach failed: %d\n", err))
 		goto cleanup;

 	comm_fd = open("/proc/self/comm", O_WRONLY|O_TRUNC);
-	if (CHECK(comm_fd < 0, "open /proc/self/comm", "err %d\n", errno))
+	if (CHECK_OPTS(comm_fd < 0, "open /proc/self/comm", "err %d\n", errno))
 		goto cleanup;

 	err = write(comm_fd, buf, sizeof(buf));
-	CHECK(err < 0, "task rename", "err %d", errno);
+	CHECK_OPTS(err < 0, "task rename", "err %d", errno);

-	CHECK(skel->bss->count == 0, "check_count", "didn't increase\n");
-	CHECK(skel->data->on_cpu != 0xffffffff, "check_on_cpu", "got wrong value\n");
+	CHECK_OPTS(skel->bss->count == 0, "check_count", "didn't increase\n");
+	CHECK_OPTS(skel->data->on_cpu != 0xffffffff, "check_on_cpu",
+		   "got wrong value\n");

 	prog_fd = bpf_program__fd(skel->progs.rename);
-	test_attr.prog_fd = prog_fd;
-	test_attr.ctx_in = args;
-	test_attr.ctx_size_in = sizeof(__u64);
+	topts.ctx_in = args;
+	topts.ctx_size_in = sizeof(__u64);

-	err = bpf_prog_test_run_xattr(&test_attr);
-	CHECK(err == 0, "test_run", "should fail for too small ctx\n");
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "test_run", "should fail for too small ctx\n");

-	test_attr.ctx_size_in = sizeof(args);
-	err = bpf_prog_test_run_xattr(&test_attr);
-	CHECK(err < 0, "test_run", "err %d\n", errno);
-	CHECK(test_attr.retval != expected_retval, "check_retval",
-	      "expect 0x%x, got 0x%x\n", expected_retval, test_attr.retval);
+	topts.ctx_size_in = sizeof(args);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err < 0, "test_run", "err %d\n", errno);
+	CHECK_OPTS(topts.retval != expected_retval, "check_retval",
+		   "expect 0x%x, got 0x%x\n", expected_retval, topts.retval);

 	for (i = 0; i < nr_online; i++) {
 		if (!online[i])
 			continue;

-		opts.cpu = i;
-		opts.retval = 0;
-		err = bpf_prog_test_run_opts(prog_fd, &opts);
-		CHECK(err < 0, "test_run_opts", "err %d\n", errno);
-		CHECK(skel->data->on_cpu != i, "check_on_cpu",
-		      "expect %d got %d\n", i, skel->data->on_cpu);
-		CHECK(opts.retval != expected_retval,
-		      "check_retval", "expect 0x%x, got 0x%x\n",
-		      expected_retval, opts.retval);
+		topts.flags = BPF_F_TEST_RUN_ON_CPU;
+		topts.cpu = i;
+		topts.retval = 0;
+		err = bpf_prog_test_run_opts(prog_fd, &topts);
+		CHECK_OPTS(err < 0, "test_run_opts", "err %d\n", errno);
+		CHECK_OPTS(skel->data->on_cpu != i, "check_on_cpu",
+			   "expect %d got %d\n", i, skel->data->on_cpu);
+		CHECK_OPTS(topts.retval != expected_retval, "check_retval",
+			   "expect 0x%x, got 0x%x\n", expected_retval,
+			   topts.retval);
 	}

 	/* invalid cpu ID should fail with ENXIO */
-	opts.cpu = 0xffffffff;
-	err = bpf_prog_test_run_opts(prog_fd, &opts);
-	CHECK(err >= 0 || errno != ENXIO,
-	      "test_run_opts_fail",
-	      "should failed with ENXIO\n");
+	topts.cpu = 0xffffffff;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err >= 0 || errno != ENXIO, "test_run_opts_fail",
+		   "should failed with ENXIO\n");

 	/* non-zero cpu w/o BPF_F_TEST_RUN_ON_CPU should fail with EINVAL */
-	opts.cpu = 1;
-	opts.flags = 0;
-	err = bpf_prog_test_run_opts(prog_fd, &opts);
-	CHECK(err >= 0 || errno != EINVAL,
-	      "test_run_opts_fail",
-	      "should failed with EINVAL\n");
+	topts.cpu = 1;
+	topts.flags = 0;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err >= 0 || errno != EINVAL, "test_run_opts_fail",
+		   "should failed with EINVAL\n");

 cleanup:
 	close(comm_fd);
diff --git a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c
index b5319ba2ee27..b6fba0b93733 100644
--- a/tools/testing/selftests/bpf/prog_tests/skb_ctx.c
+++ b/tools/testing/selftests/bpf/prog_tests/skb_ctx.c
@@ -20,97 +20,82 @@ void test_skb_ctx(void)
 		.gso_size = 10,
 		.hwtstamp = 11,
 	};
-	struct bpf_prog_test_run_attr tattr = {
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.data_in = &pkt_v4,
 		.data_size_in = sizeof(pkt_v4),
 		.ctx_in = &skb,
 		.ctx_size_in = sizeof(skb),
 		.ctx_out = &skb,
 		.ctx_size_out = sizeof(skb),
-	};
+	);
 	struct bpf_object *obj;
-	int err;
-	int i;
+	int err, prog_fd, i;

-	err = bpf_prog_test_load("./test_skb_ctx.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
-			    &tattr.prog_fd);
-	if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno))
+	err = bpf_prog_test_load("./test_skb_ctx.o", BPF_PROG_TYPE_SCHED_CLS,
+				 &obj, &prog_fd);
+	if (CHECK_OPTS(err, "load", "err %d errno %d\n", err, errno))
 		return;

 	/* ctx_in != NULL, ctx_size_in == 0 */

-	tattr.ctx_size_in = 0;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err == 0, "ctx_size_in", "err %d errno %d\n", err, errno);
-	tattr.ctx_size_in = sizeof(skb);
+	topts.ctx_size_in = 0;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "ctx_size_in", "err %d errno %d\n", err, errno);
+	topts.ctx_size_in = sizeof(skb);

 	/* ctx_out != NULL, ctx_size_out == 0 */

-	tattr.ctx_size_out = 0;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err == 0, "ctx_size_out", "err %d errno %d\n", err, errno);
-	tattr.ctx_size_out = sizeof(skb);
+	topts.ctx_size_out = 0;
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "ctx_size_out", "err %d errno %d\n", err, errno);
+	topts.ctx_size_out = sizeof(skb);

 	/* non-zero [len, tc_index] fields should be rejected*/

 	skb.len = 1;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err == 0, "len", "err %d errno %d\n", err, errno);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "len", "err %d errno %d\n", err, errno);
 	skb.len = 0;

 	skb.tc_index = 1;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err == 0, "tc_index", "err %d errno %d\n", err, errno);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "tc_index", "err %d errno %d\n", err, errno);
 	skb.tc_index = 0;

 	/* non-zero [hash, sk] fields should be rejected */

 	skb.hash = 1;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err == 0, "hash", "err %d errno %d\n", err, errno);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "hash", "err %d errno %d\n", err, errno);
 	skb.hash = 0;

 	skb.sk = (struct bpf_sock *)1;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err == 0, "sk", "err %d errno %d\n", err, errno);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err == 0, "sk", "err %d errno %d\n", err, errno);
 	skb.sk = 0;

-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err != 0 || tattr.retval,
-		   "run",
-		   "err %d errno %d retval %d\n",
-		   err, errno, tattr.retval);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err != 0 || topts.retval, "run",
+		   "err %d errno %d retval %d\n", err, errno, topts.retval);

-	CHECK_ATTR(tattr.ctx_size_out != sizeof(skb),
-		   "ctx_size_out",
-		   "incorrect output size, want %zu have %u\n",
-		   sizeof(skb), tattr.ctx_size_out);
+	CHECK_OPTS(topts.ctx_size_out != sizeof(skb), "ctx_size_out",
+		   "incorrect output size, want %zu have %u\n", sizeof(skb),
+		   topts.ctx_size_out);

 	for (i = 0; i < 5; i++)
-		CHECK_ATTR(skb.cb[i] != i + 2,
-			   "ctx_out_cb",
-			   "skb->cb[i] == %d, expected %d\n",
-			   skb.cb[i], i + 2);
-	CHECK_ATTR(skb.priority != 7,
-		   "ctx_out_priority",
-		   "skb->priority == %d, expected %d\n",
-		   skb.priority, 7);
-	CHECK_ATTR(skb.ifindex != 1,
-		   "ctx_out_ifindex",
-		   "skb->ifindex == %d, expected %d\n",
-		   skb.ifindex, 1);
-	CHECK_ATTR(skb.ingress_ifindex != 11,
-		   "ctx_out_ingress_ifindex",
+		CHECK_OPTS(skb.cb[i] != i + 2, "ctx_out_cb",
+			   "skb->cb[i] == %d, expected %d\n", skb.cb[i], i + 2);
+	CHECK_OPTS(skb.priority != 7, "ctx_out_priority",
+		   "skb->priority == %d, expected %d\n", skb.priority, 7);
+	CHECK_OPTS(skb.ifindex != 1, "ctx_out_ifindex",
+		   "skb->ifindex == %d, expected %d\n", skb.ifindex, 1);
+	CHECK_OPTS(skb.ingress_ifindex != 11, "ctx_out_ingress_ifindex",
 		   "skb->ingress_ifindex == %d, expected %d\n",
 		   skb.ingress_ifindex, 11);
-	CHECK_ATTR(skb.tstamp != 8,
-		   "ctx_out_tstamp",
-		   "skb->tstamp == %lld, expected %d\n",
-		   skb.tstamp, 8);
-	CHECK_ATTR(skb.mark != 10,
-		   "ctx_out_mark",
-		   "skb->mark == %u, expected %d\n",
-		   skb.mark, 10);
+	CHECK_OPTS(skb.tstamp != 8, "ctx_out_tstamp",
+		   "skb->tstamp == %lld, expected %d\n", skb.tstamp, 8);
+	CHECK_OPTS(skb.mark != 10, "ctx_out_mark",
+		   "skb->mark == %u, expected %d\n", skb.mark, 10);

 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/skb_helpers.c b/tools/testing/selftests/bpf/prog_tests/skb_helpers.c
index 6f802a1c0800..d7516180925a 100644
--- a/tools/testing/selftests/bpf/prog_tests/skb_helpers.c
+++ b/tools/testing/selftests/bpf/prog_tests/skb_helpers.c
@@ -9,22 +9,22 @@ void test_skb_helpers(void)
 		.gso_segs = 8,
 		.gso_size = 10,
 	};
-	struct bpf_prog_test_run_attr tattr = {
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.data_in = &pkt_v4,
 		.data_size_in = sizeof(pkt_v4),
 		.ctx_in = &skb,
 		.ctx_size_in = sizeof(skb),
 		.ctx_out = &skb,
 		.ctx_size_out = sizeof(skb),
-	};
+	);
 	struct bpf_object *obj;
-	int err;
+	int err, prog_fd;

-	err = bpf_prog_test_load("./test_skb_helpers.o", BPF_PROG_TYPE_SCHED_CLS, &obj,
-			    &tattr.prog_fd);
-	if (CHECK_ATTR(err, "load", "err %d errno %d\n", err, errno))
+	err = bpf_prog_test_load("./test_skb_helpers.o",
+				 BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd);
+	if (CHECK_OPTS(err, "load", "err %d errno %d\n", err, errno))
 		return;
-	err = bpf_prog_test_run_xattr(&tattr);
-	CHECK_ATTR(err, "len", "err %d errno %d\n", err, errno);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	CHECK_OPTS(err, "len", "err %d errno %d\n", err, errno);
 	bpf_object__close(obj);
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
index b97a8f236b3a..546ad5a6455e 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_basic.c
@@ -140,12 +140,16 @@ static void test_skmsg_helpers(enum bpf_map_type map_type)

 static void test_sockmap_update(enum bpf_map_type map_type)
 {
-	struct bpf_prog_test_run_attr tattr;
 	int err, prog, src, duration = 0;
 	struct test_sockmap_update *skel;
 	struct bpf_map *dst_map;
 	const __u32 zero = 0;
 	char dummy[14] = {0};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = dummy,
+		.data_size_in = sizeof(dummy),
+		.repeat = 1,
+	);
 	__s64 sk;

 	sk = connected_socket_v4();
@@ -167,16 +171,9 @@ static void test_sockmap_update(enum bpf_map_type map_type)
 	if (CHECK(err, "update_elem(src)", "errno=%u\n", errno))
 		goto out;

-	tattr = (struct bpf_prog_test_run_attr){
-		.prog_fd = prog,
-		.repeat = 1,
-		.data_in = dummy,
-		.data_size_in = sizeof(dummy),
-	};
-
-	err = bpf_prog_test_run_xattr(&tattr);
-	if (CHECK_ATTR(err || !tattr.retval, "bpf_prog_test_run",
-		       "errno=%u retval=%u\n", errno, tattr.retval))
+	err = bpf_prog_test_run_opts(prog, &topts);
+	if (CHECK_OPTS(err || !topts.retval, "bpf_prog_test_run",
+		       "errno=%u retval=%u\n", errno, topts.retval))
 		goto out;

 	compare_cookies(skel->maps.src, dst_map);
diff --git a/tools/testing/selftests/bpf/prog_tests/syscall.c b/tools/testing/selftests/bpf/prog_tests/syscall.c
index 81e997a69f7a..542c9b2a860a 100644
--- a/tools/testing/selftests/bpf/prog_tests/syscall.c
+++ b/tools/testing/selftests/bpf/prog_tests/syscall.c
@@ -20,22 +20,22 @@ void test_syscall(void)
 		.log_buf = (uintptr_t) verifier_log,
 		.log_size = sizeof(verifier_log),
 	};
-	struct bpf_prog_test_run_attr tattr = {
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.ctx_in = &ctx,
 		.ctx_size_in = sizeof(ctx),
-	};
+	);
 	struct syscall *skel = NULL;
 	__u64 key = 12, value = 0;
-	int err;
+	int err, prog_fd;

 	skel = syscall__open_and_load();
 	if (!ASSERT_OK_PTR(skel, "skel_load"))
 		goto cleanup;

-	tattr.prog_fd = bpf_program__fd(skel->progs.bpf_prog);
-	err = bpf_prog_test_run_xattr(&tattr);
+	prog_fd = bpf_program__fd(skel->progs.bpf_prog);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
 	ASSERT_EQ(err, 0, "err");
-	ASSERT_EQ(tattr.retval, 1, "retval");
+	ASSERT_EQ(topts.retval, 1, "retval");
 	ASSERT_GT(ctx.map_fd, 0, "ctx.map_fd");
 	ASSERT_GT(ctx.prog_fd, 0, "ctx.prog_fd");
 	ASSERT_OK(memcmp(verifier_log, "processed", sizeof("processed") - 1),
diff --git a/tools/testing/selftests/bpf/prog_tests/test_profiler.c b/tools/testing/selftests/bpf/prog_tests/test_profiler.c
index 4ca275101ee0..67e32df1212f 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_profiler.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_profiler.c
@@ -8,19 +8,19 @@

 static int sanity_run(struct bpf_program *prog)
 {
-	struct bpf_prog_test_run_attr test_attr = {};
 	__u64 args[] = {1, 2, 3};
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.ctx_in = args,
+		.ctx_size_in = sizeof(args),
+	);
 	__u32 duration = 0;
 	int err, prog_fd;

 	prog_fd = bpf_program__fd(prog);
-	test_attr.prog_fd = prog_fd;
-	test_attr.ctx_in = args;
-	test_attr.ctx_size_in = sizeof(args);
-	err = bpf_prog_test_run_xattr(&test_attr);
-	if (CHECK(err || test_attr.retval, "test_run",
-		  "err %d errno %d retval %d duration %d\n",
-		  err, errno, test_attr.retval, duration))
+	err = bpf_prog_test_run_opts(prog_fd, &topts);
+	if (CHECK(err || topts.retval, "test_run",
+		  "err %d errno %d retval %d duration %d\n", err, errno,
+		  topts.retval, duration))
 		return -1;
 	return 0;
 }
diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
index bb1b833318d3..f638cbf983e7 100644
--- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
+++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c
@@ -78,30 +78,30 @@ static void test_xdp_adjust_tail_grow2(void)
 	int tailroom = 320; /* SKB_DATA_ALIGN(sizeof(struct skb_shared_info))*/;
 	struct bpf_object *obj;
 	int err, cnt, i;
-	int max_grow;
+	int max_grow, prog_fd;

-	struct bpf_prog_test_run_attr tattr = {
-		.repeat		= 1,
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
 		.data_in	= &buf,
 		.data_out	= &buf,
 		.data_size_in	= 0, /* Per test */
 		.data_size_out	= 0, /* Per test */
-	};
+		.repeat		= 1,
+	);

-	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &tattr.prog_fd);
+	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);
 	if (ASSERT_OK(err, "test_xdp_adjust_tail_grow"))
 		return;

 	/* Test case-64 */
 	memset(buf, 1, sizeof(buf));
-	tattr.data_size_in  =  64; /* Determine test case via pkt size */
-	tattr.data_size_out = 128; /* Limit copy_size */
+	topts.data_size_in  =  64; /* Determine test case via pkt size */
+	topts.data_size_out = 128; /* Limit copy_size */
 	/* Kernel side alloc packet memory area that is zero init */
-	err = bpf_prog_test_run_xattr(&tattr);
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	ASSERT_EQ(errno, ENOSPC, "case-64 errno"); /* Due limit copy_size in bpf_test_finish */
-	ASSERT_EQ(tattr.retval, XDP_TX, "case-64 retval");
-	ASSERT_EQ(tattr.data_size_out, 192, "case-64 data_size_out"); /* Expected grow size */
+	ASSERT_EQ(topts.retval, XDP_TX, "case-64 retval");
+	ASSERT_EQ(topts.data_size_out, 192, "case-64 data_size_out"); /* Expected grow size */

 	/* Extra checks for data contents */
 	ASSERT_EQ(buf[0], 1, "case-64-data buf[0]"); /*  0-63  memset to 1 */
@@ -113,22 +113,22 @@ static void test_xdp_adjust_tail_grow2(void)

 	/* Test case-128 */
 	memset(buf, 2, sizeof(buf));
-	tattr.data_size_in  = 128; /* Determine test case via pkt size */
-	tattr.data_size_out = sizeof(buf);   /* Copy everything */
-	err = bpf_prog_test_run_xattr(&tattr);
+	topts.data_size_in  = 128; /* Determine test case via pkt size */
+	topts.data_size_out = sizeof(buf);   /* Copy everything */
+	err = bpf_prog_test_run_opts(prog_fd, &topts);

 	max_grow = 4096 - XDP_PACKET_HEADROOM -	tailroom; /* 3520 */
 	ASSERT_OK(err, "case-128");
-	ASSERT_EQ(tattr.retval, XDP_TX, "case-128 retval");
-	ASSERT_EQ(tattr.data_size_out, max_grow, "case-128 data_size_out"); /* Expect max grow */
+	ASSERT_EQ(topts.retval, XDP_TX, "case-128 retval");
+	ASSERT_EQ(topts.data_size_out, max_grow, "case-128 data_size_out"); /* Expect max grow */

 	/* Extra checks for data content: Count grow size, will contain zeros */
 	for (i = 0, cnt = 0; i < sizeof(buf); i++) {
 		if (buf[i] == 0)
 			cnt++;
 	}
-	ASSERT_EQ(cnt, max_grow - tattr.data_size_in, "case-128-data cnt"); /* Grow increase */
-	ASSERT_EQ(tattr.data_size_out, max_grow, "case-128-data data_size_out"); /* Total grow */
+	ASSERT_EQ(cnt, max_grow - topts.data_size_in, "case-128-data cnt"); /* Grow increase */
+	ASSERT_EQ(topts.data_size_out, max_grow, "case-128-data data_size_out"); /* Total grow */

 	bpf_object__close(obj);
 }
--
2.30.2

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

* [PATCH bpf-next v2 3/4] bpftool: migrate from bpf_prog_test_run_xattr
  2022-01-28  1:23 [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Delyan Kratunov
  2022-01-28  1:23 ` [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run Delyan Kratunov
  2022-01-28  1:23 ` [PATCH bpf-next v2 2/4] selftests: bpf: migrate from bpf_prog_test_run_xattr Delyan Kratunov
@ 2022-01-28  1:23 ` Delyan Kratunov
  2022-02-01  1:33   ` Andrii Nakryiko
  2022-01-28  1:23 ` [PATCH bpf-next v2 4/4] libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run Delyan Kratunov
  2022-01-28 13:07 ` [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Daniel Borkmann
  4 siblings, 1 reply; 14+ messages in thread
From: Delyan Kratunov @ 2022-01-28  1:23 UTC (permalink / raw)
  To: bpf, ast, andrii, daniel; +Cc: Delyan Kratunov

bpf_prog_test_run_xattr is being deprecated in favor of bpf_prog_test_run_opts.

Signed-off-by: Delyan Kratunov <delyank@fb.com>
---
 tools/bpf/bpftool/prog.c | 55 ++++++++++++++++++++--------------------
 1 file changed, 27 insertions(+), 28 deletions(-)

diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 87593f98d2d1..4f96c229ba77 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -1272,12 +1272,12 @@ static int do_run(int argc, char **argv)
 {
 	char *data_fname_in = NULL, *data_fname_out = NULL;
 	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
-	struct bpf_prog_test_run_attr test_attr = {0};
 	const unsigned int default_size = SZ_32K;
 	void *data_in = NULL, *data_out = NULL;
 	void *ctx_in = NULL, *ctx_out = NULL;
 	unsigned int repeat = 1;
 	int fd, err;
+	LIBBPF_OPTS(bpf_test_run_opts, topts);
 
 	if (!REQ_ARGS(4))
 		return -1;
@@ -1315,7 +1315,7 @@ static int do_run(int argc, char **argv)
 			if (!REQ_ARGS(1))
 				return -1;
 
-			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
+			topts.data_size_out = strtoul(*argv, &endptr, 0);
 			if (*endptr) {
 				p_err("can't parse %s as output data size",
 				      *argv);
@@ -1343,7 +1343,7 @@ static int do_run(int argc, char **argv)
 			if (!REQ_ARGS(1))
 				return -1;
 
-			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
+			topts.ctx_size_out = strtoul(*argv, &endptr, 0);
 			if (*endptr) {
 				p_err("can't parse %s as output context size",
 				      *argv);
@@ -1371,38 +1371,37 @@ static int do_run(int argc, char **argv)
 		}
 	}
 
-	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
+	err = get_run_data(data_fname_in, &data_in, &topts.data_size_in);
 	if (err)
 		return -1;
 
 	if (data_in) {
-		if (!test_attr.data_size_out)
-			test_attr.data_size_out = default_size;
-		err = alloc_run_data(&data_out, test_attr.data_size_out);
+		if (!topts.data_size_out)
+			topts.data_size_out = default_size;
+		err = alloc_run_data(&data_out, topts.data_size_out);
 		if (err)
 			goto free_data_in;
 	}
 
-	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
+	err = get_run_data(ctx_fname_in, &ctx_in, &topts.ctx_size_in);
 	if (err)
 		goto free_data_out;
 
 	if (ctx_in) {
-		if (!test_attr.ctx_size_out)
-			test_attr.ctx_size_out = default_size;
-		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
+		if (!topts.ctx_size_out)
+			topts.ctx_size_out = default_size;
+		err = alloc_run_data(&ctx_out, topts.ctx_size_out);
 		if (err)
 			goto free_ctx_in;
 	}
 
-	test_attr.prog_fd	= fd;
-	test_attr.repeat	= repeat;
-	test_attr.data_in	= data_in;
-	test_attr.data_out	= data_out;
-	test_attr.ctx_in	= ctx_in;
-	test_attr.ctx_out	= ctx_out;
+	topts.repeat	= repeat;
+	topts.data_in	= data_in;
+	topts.data_out	= data_out;
+	topts.ctx_in	= ctx_in;
+	topts.ctx_out	= ctx_out;
 
-	err = bpf_prog_test_run_xattr(&test_attr);
+	err = bpf_prog_test_run_opts(fd, &topts);
 	if (err) {
 		p_err("failed to run program: %s", strerror(errno));
 		goto free_ctx_out;
@@ -1416,23 +1415,23 @@ static int do_run(int argc, char **argv)
 	/* Do not exit on errors occurring when printing output data/context,
 	 * we still want to print return value and duration for program run.
 	 */
-	if (test_attr.data_size_out)
-		err += print_run_output(test_attr.data_out,
-					test_attr.data_size_out,
+	if (topts.data_size_out)
+		err += print_run_output(topts.data_out,
+					topts.data_size_out,
 					data_fname_out, "data_out");
-	if (test_attr.ctx_size_out)
-		err += print_run_output(test_attr.ctx_out,
-					test_attr.ctx_size_out,
+	if (topts.ctx_size_out)
+		err += print_run_output(topts.ctx_out,
+					topts.ctx_size_out,
 					ctx_fname_out, "ctx_out");
 
 	if (json_output) {
-		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
-		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
+		jsonw_uint_field(json_wtr, "retval", topts.retval);
+		jsonw_uint_field(json_wtr, "duration", topts.duration);
 		jsonw_end_object(json_wtr);	/* root */
 	} else {
 		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
-			test_attr.retval,
-			repeat > 1 ? " (average)" : "", test_attr.duration);
+			topts.retval,
+			repeat > 1 ? " (average)" : "", topts.duration);
 	}
 
 free_ctx_out:
-- 
2.30.2


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

* [PATCH bpf-next v2 4/4] libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run
  2022-01-28  1:23 [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Delyan Kratunov
                   ` (2 preceding siblings ...)
  2022-01-28  1:23 ` [PATCH bpf-next v2 3/4] bpftool: " Delyan Kratunov
@ 2022-01-28  1:23 ` Delyan Kratunov
  2022-02-01  1:37   ` Andrii Nakryiko
  2022-01-28 13:07 ` [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Daniel Borkmann
  4 siblings, 1 reply; 14+ messages in thread
From: Delyan Kratunov @ 2022-01-28  1:23 UTC (permalink / raw)
  To: bpf, ast, andrii, daniel; +Cc: Delyan Kratunov

Closes: https://github.com/libbpf/libbpf/issues/286
Signed-off-by: Delyan Kratunov <delyank@fb.com>
---
 tools/lib/bpf/bpf.h |  8 +++++---
 tools/lib/bpf/xsk.c | 11 +++++++----
 2 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index c2e8327010f9..e3d87b35435d 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -453,13 +453,15 @@ struct bpf_prog_test_run_attr {
 			     * out: length of cxt_out */
 };
 
-LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
+LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_test_run_opts() instead")
+int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
 
 /*
  * bpf_prog_test_run does not check that data_out is large enough. Consider
- * using bpf_prog_test_run_xattr instead.
+ * using bpf_prog_test_run_opts instead.
  */
-LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
+LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_test_run_opts() instead")
+int bpf_prog_test_run(int prog_fd, int repeat, void *data,
 				 __u32 size, void *data_out, __u32 *size_out,
 				 __u32 *retval, __u32 *duration);
 LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
diff --git a/tools/lib/bpf/xsk.c b/tools/lib/bpf/xsk.c
index edafe56664f3..843d03b8f58c 100644
--- a/tools/lib/bpf/xsk.c
+++ b/tools/lib/bpf/xsk.c
@@ -369,8 +369,7 @@ int xsk_umem__create_v0_0_2(struct xsk_umem **umem_ptr, void *umem_area,
 static enum xsk_prog get_xsk_prog(void)
 {
 	enum xsk_prog detected = XSK_PROG_FALLBACK;
-	__u32 size_out, retval, duration;
-	char data_in = 0, data_out;
+	char data_in = 0;
 	struct bpf_insn insns[] = {
 		BPF_LD_MAP_FD(BPF_REG_1, 0),
 		BPF_MOV64_IMM(BPF_REG_2, 0),
@@ -378,6 +377,10 @@ static enum xsk_prog get_xsk_prog(void)
 		BPF_EMIT_CALL(BPF_FUNC_redirect_map),
 		BPF_EXIT_INSN(),
 	};
+	LIBBPF_OPTS(bpf_test_run_opts, test_opts,
+		.data_in = &data_in,
+		.data_size_in = 1,
+	);
 	int prog_fd, map_fd, ret, insn_cnt = ARRAY_SIZE(insns);
 
 	map_fd = bpf_map_create(BPF_MAP_TYPE_XSKMAP, NULL, sizeof(int), sizeof(int), 1, NULL);
@@ -392,8 +395,8 @@ static enum xsk_prog get_xsk_prog(void)
 		return detected;
 	}
 
-	ret = bpf_prog_test_run(prog_fd, 0, &data_in, 1, &data_out, &size_out, &retval, &duration);
-	if (!ret && retval == XDP_PASS)
+	ret = bpf_prog_test_run_opts(prog_fd, &test_opts);
+	if (!ret && test_opts.retval == XDP_PASS)
 		detected = XSK_PROG_REDIRECT_FLAGS;
 	close(prog_fd);
 	close(map_fd);
-- 
2.30.2


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

* Re: [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr}
  2022-01-28  1:23 [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Delyan Kratunov
                   ` (3 preceding siblings ...)
  2022-01-28  1:23 ` [PATCH bpf-next v2 4/4] libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run Delyan Kratunov
@ 2022-01-28 13:07 ` Daniel Borkmann
  2022-01-28 20:12   ` Delyan Kratunov
  4 siblings, 1 reply; 14+ messages in thread
From: Daniel Borkmann @ 2022-01-28 13:07 UTC (permalink / raw)
  To: Delyan Kratunov, bpf, ast, andrii; +Cc: lmb

On 1/28/22 2:23 AM, Delyan Kratunov wrote:
> Fairly straight-forward mechanical transformation from bpf_prog_test_run
> and bpf_prog_test_run_xattr to the bpf_prog_test_run_opts goodness.
> Most of the changes are in tests, though bpftool and libbpf (xsk.c) have one
> call site each as well.
> 
> The only aspect that's still a bit RFC is that prog_run_xattr is testing
> behavior specific to bpf_prog_test_run_xattr, which does not exist in prog_run_opts.
> Namely, -EINVAL return on data_out == NULL && data_size_out > 0.
> Adding this behavior to prog_test_run_opts is one option, keeping the test as-is
> and cloning it to use bpf_prog_test_run_opts is another possibility.

I would suggest to do the former rather than duplicating, if there's nothing
particularly blocking us from adding this to prog_test_run_opts.

> The current version just suppresses the deprecation warning.
> 
> As an aside, checkpatch really doesn't like that LIBBPF_OPTS looks like
> a function call but is formatted like a struct declaration. If anyone
> cares about formatting, now would be a good time to mention it.

As you have it looks good to me. One small nit, please also add a non-empty
commit message with rationale to each of the patches rather than just SoB alone.

Thanks a lot!

> v1 -> v2:
> Split selftests/bpf changes into two commits to appease the mailing list.
> 
> Delyan Kratunov (4):
>    selftests: bpf: migrate from bpf_prog_test_run
>    selftests: bpf: migrate from bpf_prog_test_run_xattr
>    bpftool: migrate from bpf_prog_test_run_xattr
>    libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run
> 

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

* Re: [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr}
  2022-01-28 13:07 ` [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Daniel Borkmann
@ 2022-01-28 20:12   ` Delyan Kratunov
  2022-02-01  1:04     ` Andrii Nakryiko
  0 siblings, 1 reply; 14+ messages in thread
From: Delyan Kratunov @ 2022-01-28 20:12 UTC (permalink / raw)
  To: daniel; +Cc: ast, lmb, andrii, bpf

Thanks for taking a look, Daniel!

On Fri, 2022-01-28 at 14:07 +0100, Daniel Borkmann wrote:
> On 1/28/22 2:23 AM, Delyan Kratunov wrote:
> > Adding this behavior to prog_test_run_opts is one option, keeping the test as-is
> > and cloning it to use bpf_prog_test_run_opts is another possibility.
> 
> I would suggest to do the former rather than duplicating, if there's nothing
> particularly blocking us from adding this to prog_test_run_opts.

I looked into this a bit more. Unfortunately, bpf_test_finish unconditionally
copies data_size_out back into the uattr, even if data_out is NULL.
(net/bpf/test_run.c:180)

This makes the ergonomics of reusing the same topts struct for multiple
bpf_prog_test_run calls pretty horrendous - you'd need to clear data_size_out
before every call, even if you don't care about it otherwise (and you don't, you
didn't set data_out!).

In practicality, adding the logic from _xattr to _opts results in a significant
number of test failures. I'm a bit worried it might break libbpf users if they
use similar opts reuse patterns.

> As you have it looks good to me. One small nit, please also add a non-empty
> commit message with rationale to each of the patches rather than just SoB alone.

Will do!


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

* Re: [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr}
  2022-01-28 20:12   ` Delyan Kratunov
@ 2022-02-01  1:04     ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2022-02-01  1:04 UTC (permalink / raw)
  To: Delyan Kratunov; +Cc: daniel, ast, lmb, andrii, bpf

On Fri, Jan 28, 2022 at 12:12 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> Thanks for taking a look, Daniel!
>
> On Fri, 2022-01-28 at 14:07 +0100, Daniel Borkmann wrote:
> > On 1/28/22 2:23 AM, Delyan Kratunov wrote:
> > > Adding this behavior to prog_test_run_opts is one option, keeping the test as-is
> > > and cloning it to use bpf_prog_test_run_opts is another possibility.
> >
> > I would suggest to do the former rather than duplicating, if there's nothing
> > particularly blocking us from adding this to prog_test_run_opts.
>
> I looked into this a bit more. Unfortunately, bpf_test_finish unconditionally
> copies data_size_out back into the uattr, even if data_out is NULL.
> (net/bpf/test_run.c:180)
>
> This makes the ergonomics of reusing the same topts struct for multiple
> bpf_prog_test_run calls pretty horrendous - you'd need to clear data_size_out
> before every call, even if you don't care about it otherwise (and you don't, you
> didn't set data_out!).
>
> In practicality, adding the logic from _xattr to _opts results in a significant
> number of test failures. I'm a bit worried it might break libbpf users if they
> use similar opts reuse patterns.
>

Seems like kernel doesn't enforce that data_size_out == 0 if data_out
is NULL, so I'd say let's just keep bpf_test_run_opts() as is and
defer to kernel for error checking (e.g., for raw_tp data_out isn't
allowed to be NULL, but libbpf doesn't check that, right, it just lets
kernel do all the nuanced error checking).

So I'd say let's keep _opts() as is and let's remove parts of
prog_run_xattr.c selftest that check this specific error check?

> > As you have it looks good to me. One small nit, please also add a non-empty
> > commit message with rationale to each of the patches rather than just SoB alone.
>
> Will do!
>

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

* Re: [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run
  2022-01-28  1:23 ` [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run Delyan Kratunov
@ 2022-02-01  1:24   ` Andrii Nakryiko
  2022-02-02  0:01     ` Delyan Kratunov
  0 siblings, 1 reply; 14+ messages in thread
From: Andrii Nakryiko @ 2022-02-01  1:24 UTC (permalink / raw)
  To: Delyan Kratunov; +Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann

On Thu, Jan 27, 2022 at 5:26 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---
>  .../selftests/bpf/prog_tests/atomics.c        |  86 +++---
>  .../testing/selftests/bpf/prog_tests/bpf_nf.c |  10 +-
>  .../selftests/bpf/prog_tests/fentry_fexit.c   |  33 ++-
>  .../selftests/bpf/prog_tests/fentry_test.c    |   9 +-
>  .../selftests/bpf/prog_tests/fexit_bpf2bpf.c  |  33 ++-
>  .../selftests/bpf/prog_tests/fexit_stress.c   |  26 +-
>  .../selftests/bpf/prog_tests/fexit_test.c     |   9 +-
>  .../prog_tests/flow_dissector_load_bytes.c    |  27 +-
>  .../selftests/bpf/prog_tests/for_each.c       |  32 ++-
>  .../bpf/prog_tests/get_func_args_test.c       |  14 +-
>  .../bpf/prog_tests/get_func_ip_test.c         |  12 +-
>  .../selftests/bpf/prog_tests/global_data.c    |  25 +-
>  .../bpf/prog_tests/global_func_args.c         |  13 +-
>  .../selftests/bpf/prog_tests/kfunc_call.c     |  46 ++--
>  .../selftests/bpf/prog_tests/ksyms_module.c   |  23 +-
>  .../selftests/bpf/prog_tests/l4lb_all.c       |  35 ++-
>  .../selftests/bpf/prog_tests/map_lock.c       |  15 +-
>  .../selftests/bpf/prog_tests/map_ptr.c        |  18 +-
>  .../selftests/bpf/prog_tests/modify_return.c  |  38 +--
>  .../selftests/bpf/prog_tests/pkt_access.c     |  27 +-
>  .../selftests/bpf/prog_tests/pkt_md_access.c  |  15 +-
>  .../bpf/prog_tests/queue_stack_map.c          |  43 +--
>  .../bpf/prog_tests/raw_tp_writable_test_run.c |  16 +-
>  .../selftests/bpf/prog_tests/signal_pending.c |  24 +-
>  .../selftests/bpf/prog_tests/spinlock.c       |  15 +-
>  .../selftests/bpf/prog_tests/tailcalls.c      | 245 ++++++++++--------
>  .../bpf/prog_tests/test_skb_pkt_end.c         |  15 +-
>  .../testing/selftests/bpf/prog_tests/timer.c  |   9 +-
>  .../selftests/bpf/prog_tests/timer_mim.c      |   9 +-
>  .../selftests/bpf/prog_tests/trace_ext.c      |  28 +-
>  tools/testing/selftests/bpf/prog_tests/xdp.c  |  35 ++-
>  .../bpf/prog_tests/xdp_adjust_frags.c         |  34 ++-
>  .../bpf/prog_tests/xdp_adjust_tail.c          | 114 +++++---
>  .../selftests/bpf/prog_tests/xdp_bpf2bpf.c    |  16 +-
>  .../selftests/bpf/prog_tests/xdp_noinline.c   |  45 ++--
>  .../selftests/bpf/prog_tests/xdp_perf.c       |  19 +-
>  tools/testing/selftests/bpf/test_lru_map.c    |  11 +-
>  tools/testing/selftests/bpf/test_progs.h      |   2 +
>  tools/testing/selftests/bpf/test_verifier.c   |  16 +-
>  39 files changed, 727 insertions(+), 515 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c b/tools/testing/selftests/bpf/prog_tests/atomics.c
> index 86b7d5d84eec..12ecb3ae5c28 100644
> --- a/tools/testing/selftests/bpf/prog_tests/atomics.c
> +++ b/tools/testing/selftests/bpf/prog_tests/atomics.c
> @@ -7,18 +7,20 @@
>  static void test_add(struct atomics_lskel *skel)
>  {
>         int err, prog_fd;
> -       __u32 duration = 0, retval;
>         int link_fd;
> +       LIBBPF_OPTS(bpf_test_run_opts, topts,
> +               .repeat = 1,
> +       );

for simple case like this you can just keep it single line:

LIBBPF_OPTS(bpf_test_run_opts, topts, .repeat = 1)

But, it seems like the kernel does `if (!repeat) repeat = 1;` logic
for program types that support repeat feature, so I'd suggest just
drop .repeat = 1 and keep it simple.

>
>         link_fd = atomics_lskel__add__attach(skel);
>         if (!ASSERT_GT(link_fd, 0, "attach(add)"))
>                 return;
>
>         prog_fd = skel->progs.add.prog_fd;
> -       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
> -                               NULL, NULL, &retval, &duration);
> -       if (CHECK(err || retval, "test_run add",
> -                 "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
> +       err = bpf_prog_test_run_opts(prog_fd, &topts);
> +       if (CHECK_OPTS(err || topts.retval, "test_run add",

let's not add new CHECK*() variants, CHECK() and its derivative are
deprecated, we are using ASSERT_xxx() macros for all new code.

In this case, I think it's best to replace CHECK() with just:

ASSERT_OK(err, "run_err");
ASSERT_OK(topts.retval, "run_ret_val");

I don't think logging duration is useful for most, if not all, tests,
so I wouldn't bother.


> +                      "err %d errno %d retval %d duration %d\n", err, errno,
> +                      topts.retval, topts.duration))
>                 goto cleanup;
>
>         ASSERT_EQ(skel->data->add64_value, 3, "add64_value");

[...]

> diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
> index 4374ac8a8a91..cb0bcd9bb950 100644
> --- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
> +++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
> @@ -9,38 +9,43 @@ void test_fentry_fexit(void)
>         struct fentry_test_lskel *fentry_skel = NULL;
>         struct fexit_test_lskel *fexit_skel = NULL;
>         __u64 *fentry_res, *fexit_res;
> -       __u32 duration = 0, retval;
>         int err, prog_fd, i;
> +       LIBBPF_OPTS(bpf_test_run_opts, topts,
> +               .repeat = 1,
> +       );
>
>         fentry_skel = fentry_test_lskel__open_and_load();
> -       if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n"))
> +       if (CHECK_OPTS(!fentry_skel, "fentry_skel_load",
> +                      "fentry skeleton failed\n"))

You didn't have to touch this code, you could have just kept duration
= 0 (which CHECK() internally assumes, unfortunately).

Alternatively, just switch to ASSERT_OK_PTR(fentry_skel,
"fentry_skel_load"); and be done with it. As I mentioned, we are in a
gradual process of removing CHECK()s, I was actually surprised how far
we've progressed already:

$ rg CHECK | wc -l
2024
$ rg ASSERT_ | wc -l
1782

At some point we'll do the final push and remove CHECK() altogether,
but it doesn't have to be part of this patch set (unless you are
interested in doing some more mechanical conversions, of course, it's
greatly appreciated, but I didn't yet have heart to ask anyone to do
those 2000 conversions...).

>                 goto close_prog;
>         fexit_skel = fexit_test_lskel__open_and_load();
> -       if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n"))
> +       if (CHECK_OPTS(!fexit_skel, "fexit_skel_load",
> +                      "fexit skeleton failed\n"))
>                 goto close_prog;
>

[...]

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

* Re: [PATCH bpf-next v2 2/4] selftests: bpf: migrate from bpf_prog_test_run_xattr
  2022-01-28  1:23 ` [PATCH bpf-next v2 2/4] selftests: bpf: migrate from bpf_prog_test_run_xattr Delyan Kratunov
@ 2022-02-01  1:32   ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2022-02-01  1:32 UTC (permalink / raw)
  To: Delyan Kratunov; +Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann

On Thu, Jan 27, 2022 at 5:23 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---

You can avoid a bunch of code churn if you name the new opts variable
as tattr, let's do that in this patch?

CHECK_FLOW_KEYS() is probably justified, so feel free to stick to
using CHECK() internally for extra formatting capabilities (we might
extend ASSERT_xxx() macros eventually to support vararg format args).

Also, forgot to mention in the previous patch reply, please use
"selftests/bpf: " subject prefix. We are never perfectly consistent,
but stats still favor selftests/bpf heavily:

$ git log --oneline -- . | rg 'selftests: bpf: ' | wc -l
102
$ git log --oneline -- . | rg 'selftests/bpf: ' | wc -l
1090

>  .../selftests/bpf/prog_tests/check_mtu.c      | 47 +++++-----
>  .../selftests/bpf/prog_tests/cls_redirect.c   | 30 +++---
>  .../selftests/bpf/prog_tests/dummy_st_ops.c   | 31 +++----
>  .../selftests/bpf/prog_tests/flow_dissector.c | 75 ++++++++-------
>  .../selftests/bpf/prog_tests/kfree_skb.c      | 16 ++--
>  .../selftests/bpf/prog_tests/prog_run_xattr.c |  5 +
>  .../bpf/prog_tests/raw_tp_test_run.c          | 85 ++++++++---------
>  .../selftests/bpf/prog_tests/skb_ctx.c        | 93 ++++++++-----------
>  .../selftests/bpf/prog_tests/skb_helpers.c    | 16 ++--
>  .../selftests/bpf/prog_tests/sockmap_basic.c  | 19 ++--
>  .../selftests/bpf/prog_tests/syscall.c        | 12 +--
>  .../selftests/bpf/prog_tests/test_profiler.c  | 16 ++--
>  .../bpf/prog_tests/xdp_adjust_tail.c          | 34 +++----
>  13 files changed, 227 insertions(+), 252 deletions(-)
>

[...]

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

* Re: [PATCH bpf-next v2 3/4] bpftool: migrate from bpf_prog_test_run_xattr
  2022-01-28  1:23 ` [PATCH bpf-next v2 3/4] bpftool: " Delyan Kratunov
@ 2022-02-01  1:33   ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2022-02-01  1:33 UTC (permalink / raw)
  To: Delyan Kratunov; +Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann

On Thu, Jan 27, 2022 at 5:23 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> bpf_prog_test_run_xattr is being deprecated in favor of bpf_prog_test_run_opts.
>
> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---
>  tools/bpf/bpftool/prog.c | 55 ++++++++++++++++++++--------------------
>  1 file changed, 27 insertions(+), 28 deletions(-)
>
> diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> index 87593f98d2d1..4f96c229ba77 100644
> --- a/tools/bpf/bpftool/prog.c
> +++ b/tools/bpf/bpftool/prog.c
> @@ -1272,12 +1272,12 @@ static int do_run(int argc, char **argv)
>  {
>         char *data_fname_in = NULL, *data_fname_out = NULL;
>         char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
> -       struct bpf_prog_test_run_attr test_attr = {0};
>         const unsigned int default_size = SZ_32K;
>         void *data_in = NULL, *data_out = NULL;
>         void *ctx_in = NULL, *ctx_out = NULL;
>         unsigned int repeat = 1;
>         int fd, err;
> +       LIBBPF_OPTS(bpf_test_run_opts, topts);

let's name it "test_attr" and avoid most of the code churn?

Otherwise it looks good, thanks!

>
>         if (!REQ_ARGS(4))
>                 return -1;

[...]

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

* Re: [PATCH bpf-next v2 4/4] libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run
  2022-01-28  1:23 ` [PATCH bpf-next v2 4/4] libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run Delyan Kratunov
@ 2022-02-01  1:37   ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2022-02-01  1:37 UTC (permalink / raw)
  To: Delyan Kratunov; +Cc: bpf, Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann

On Thu, Jan 27, 2022 at 5:23 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> Closes: https://github.com/libbpf/libbpf/issues/286

As Daniel mentioned, please add non-empty commit message. As for the
"Closes: ", it's not one of "supported" tags in Linux repo, so we've
been using a "reference" syntax to introduce it. So something like
this commit message would do the trick:

Deprecate non-extendable bpf_prog_test_run_xattr() in favor of
OPTS-based bpf_prog_test_run_opts() ([0]).

  [0] Closes: https://github.com/libbpf/libbpf/issues/286

> Signed-off-by: Delyan Kratunov <delyank@fb.com>
> ---
>  tools/lib/bpf/bpf.h |  8 +++++---
>  tools/lib/bpf/xsk.c | 11 +++++++----
>  2 files changed, 12 insertions(+), 7 deletions(-)
>
> diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
> index c2e8327010f9..e3d87b35435d 100644
> --- a/tools/lib/bpf/bpf.h
> +++ b/tools/lib/bpf/bpf.h
> @@ -453,13 +453,15 @@ struct bpf_prog_test_run_attr {
>                              * out: length of cxt_out */
>  };
>
> -LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
> +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_test_run_opts() instead")

please don't remove LIBBPF_API, it still has to be marked with
LIBBPF_API for proper shared library visibility

> +int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
>
>  /*
>   * bpf_prog_test_run does not check that data_out is large enough. Consider
> - * using bpf_prog_test_run_xattr instead.
> + * using bpf_prog_test_run_opts instead.
>   */
> -LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
> +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_test_run_opts() instead")
> +int bpf_prog_test_run(int prog_fd, int repeat, void *data,
>                                  __u32 size, void *data_out, __u32 *size_out,
>                                  __u32 *retval, __u32 *duration);
>  LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
> diff --git a/tools/lib/bpf/xsk.c b/tools/lib/bpf/xsk.c
> index edafe56664f3..843d03b8f58c 100644
> --- a/tools/lib/bpf/xsk.c
> +++ b/tools/lib/bpf/xsk.c

don't bother with xsk.c, entire file is marked as deprecated and will
be removed in libbpf v1.0. It already has deprecation suppressing
pragma, so you shouldn't see any compilation warning.

> @@ -369,8 +369,7 @@ int xsk_umem__create_v0_0_2(struct xsk_umem **umem_ptr, void *umem_area,
>  static enum xsk_prog get_xsk_prog(void)
>  {
>         enum xsk_prog detected = XSK_PROG_FALLBACK;
> -       __u32 size_out, retval, duration;
> -       char data_in = 0, data_out;
> +       char data_in = 0;
>         struct bpf_insn insns[] = {
>                 BPF_LD_MAP_FD(BPF_REG_1, 0),
>                 BPF_MOV64_IMM(BPF_REG_2, 0),
> @@ -378,6 +377,10 @@ static enum xsk_prog get_xsk_prog(void)
>                 BPF_EMIT_CALL(BPF_FUNC_redirect_map),
>                 BPF_EXIT_INSN(),
>         };
> +       LIBBPF_OPTS(bpf_test_run_opts, test_opts,
> +               .data_in = &data_in,
> +               .data_size_in = 1,
> +       );
>         int prog_fd, map_fd, ret, insn_cnt = ARRAY_SIZE(insns);
>
>         map_fd = bpf_map_create(BPF_MAP_TYPE_XSKMAP, NULL, sizeof(int), sizeof(int), 1, NULL);
> @@ -392,8 +395,8 @@ static enum xsk_prog get_xsk_prog(void)
>                 return detected;
>         }
>
> -       ret = bpf_prog_test_run(prog_fd, 0, &data_in, 1, &data_out, &size_out, &retval, &duration);
> -       if (!ret && retval == XDP_PASS)
> +       ret = bpf_prog_test_run_opts(prog_fd, &test_opts);
> +       if (!ret && test_opts.retval == XDP_PASS)
>                 detected = XSK_PROG_REDIRECT_FLAGS;
>         close(prog_fd);
>         close(map_fd);
> --
> 2.30.2
>

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

* Re: [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run
  2022-02-01  1:24   ` Andrii Nakryiko
@ 2022-02-02  0:01     ` Delyan Kratunov
  2022-02-02  0:12       ` Andrii Nakryiko
  0 siblings, 1 reply; 14+ messages in thread
From: Delyan Kratunov @ 2022-02-02  0:01 UTC (permalink / raw)
  To: andrii.nakryiko; +Cc: daniel, ast, andrii, bpf

On Mon, 2022-01-31 at 17:24 -0800, Andrii Nakryiko wrote:
> for simple case like this you can just keep it single line:
> 
> LIBBPF_OPTS(bpf_test_run_opts, topts, .repeat = 1)
> 
> But, it seems like the kernel does `if (!repeat) repeat = 1;` logic
> for program types that support repeat feature, so I'd suggest just
> drop .repeat = 1 and keep it simple.

Sure.

> 
> let's not add new CHECK*() variants, CHECK() and its derivative are
> deprecated, we are using ASSERT_xxx() macros for all new code.
> 
> In this case, I think it's best to replace CHECK() with just:
> 
> ASSERT_OK(err, "run_err");
> ASSERT_OK(topts.retval, "run_ret_val");
> 
> I don't think logging duration is useful for most, if not all, tests,
> so I wouldn't bother.

Ah, this makes a lot of sense, I was wondering what was going on with these
quite unreadable CHECK{,_ATTR} macros. I'll rewrite the ones I'm changing in
this series to use ASSERT_* if you're okay with that amount of churn?

> You didn't have to touch this code, you could have just kept duration
> = 0 (which CHECK() internally assumes, unfortunately).

I didn't *have* to but leaving the variable around to satisfy a macro feels
super awkward. Happy to minimize these changes, if they're going overboard but
I'd rather clean up the CHECK usage where I can.

> 
> Alternatively, just switch to ASSERT_OK_PTR(fentry_skel,
> "fentry_skel_load"); and be done with it. As I mentioned, we are in a
> gradual process of removing CHECK()s, 
> [...]
> At some point we'll do the final push and remove CHECK() altogether,
> but it doesn't have to be part of this patch set (unless you are
> interested in doing some more mechanical conversions, of course, it's
> greatly appreciated, but I didn't yet have heart to ask anyone to do
> those 2000 conversions...).

I don't think I have it in me to volunteer for the whole refactor but I'll do my
bit in the usages this series touches. 

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

* Re: [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run
  2022-02-02  0:01     ` Delyan Kratunov
@ 2022-02-02  0:12       ` Andrii Nakryiko
  0 siblings, 0 replies; 14+ messages in thread
From: Andrii Nakryiko @ 2022-02-02  0:12 UTC (permalink / raw)
  To: Delyan Kratunov; +Cc: daniel, ast, andrii, bpf

On Tue, Feb 1, 2022 at 4:01 PM Delyan Kratunov <delyank@fb.com> wrote:
>
> On Mon, 2022-01-31 at 17:24 -0800, Andrii Nakryiko wrote:
> > for simple case like this you can just keep it single line:
> >
> > LIBBPF_OPTS(bpf_test_run_opts, topts, .repeat = 1)
> >
> > But, it seems like the kernel does `if (!repeat) repeat = 1;` logic
> > for program types that support repeat feature, so I'd suggest just
> > drop .repeat = 1 and keep it simple.
>
> Sure.
>
> >
> > let's not add new CHECK*() variants, CHECK() and its derivative are
> > deprecated, we are using ASSERT_xxx() macros for all new code.
> >
> > In this case, I think it's best to replace CHECK() with just:
> >
> > ASSERT_OK(err, "run_err");
> > ASSERT_OK(topts.retval, "run_ret_val");
> >
> > I don't think logging duration is useful for most, if not all, tests,
> > so I wouldn't bother.
>
> Ah, this makes a lot of sense, I was wondering what was going on with these
> quite unreadable CHECK{,_ATTR} macros. I'll rewrite the ones I'm changing in
> this series to use ASSERT_* if you're okay with that amount of churn?

No, it's fine with me. As I said, I'd like all CHECK()s to be gone, so
the sooner and closer we get to that the better. We have BPF CI setup
to make sure that we don't accidentally screw up checks in a major
way, so it's pretty safe to do.

>
> > You didn't have to touch this code, you could have just kept duration
> > = 0 (which CHECK() internally assumes, unfortunately).
>
> I didn't *have* to but leaving the variable around to satisfy a macro feels
> super awkward. Happy to minimize these changes, if they're going overboard but
> I'd rather clean up the CHECK usage where I can.

Heh, we do have some selftests doing `static int duration;` in a file
to satisfy CHECK() :) But I don't mind more selftest code churn to
convert to ASSERT_xxx() macros.

>
> >
> > Alternatively, just switch to ASSERT_OK_PTR(fentry_skel,
> > "fentry_skel_load"); and be done with it. As I mentioned, we are in a
> > gradual process of removing CHECK()s,
> > [...]
> > At some point we'll do the final push and remove CHECK() altogether,
> > but it doesn't have to be part of this patch set (unless you are
> > interested in doing some more mechanical conversions, of course, it's
> > greatly appreciated, but I didn't yet have heart to ask anyone to do
> > those 2000 conversions...).
>
> I don't think I have it in me to volunteer for the whole refactor but I'll do my
> bit in the usages this series touches.

No worries and thanks!

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

end of thread, other threads:[~2022-02-02  0:12 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-28  1:23 [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Delyan Kratunov
2022-01-28  1:23 ` [PATCH bpf-next v2 1/4] selftests: bpf: migrate from bpf_prog_test_run Delyan Kratunov
2022-02-01  1:24   ` Andrii Nakryiko
2022-02-02  0:01     ` Delyan Kratunov
2022-02-02  0:12       ` Andrii Nakryiko
2022-01-28  1:23 ` [PATCH bpf-next v2 2/4] selftests: bpf: migrate from bpf_prog_test_run_xattr Delyan Kratunov
2022-02-01  1:32   ` Andrii Nakryiko
2022-01-28  1:23 ` [PATCH bpf-next v2 3/4] bpftool: " Delyan Kratunov
2022-02-01  1:33   ` Andrii Nakryiko
2022-01-28  1:23 ` [PATCH bpf-next v2 4/4] libbpf: Deprecate bpf_prog_test_run_xattr and bpf_prog_test_run Delyan Kratunov
2022-02-01  1:37   ` Andrii Nakryiko
2022-01-28 13:07 ` [PATCH bpf-next v2 0/4] migrate from bpf_prog_test_run{,_xattr} Daniel Borkmann
2022-01-28 20:12   ` Delyan Kratunov
2022-02-01  1:04     ` Andrii Nakryiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).