* [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags
@ 2026-03-26 17:39 Eduard Zingerman
2026-03-26 17:39 ` [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name Eduard Zingerman
` (4 more replies)
0 siblings, 5 replies; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 17:39 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87,
cupertino.miranda
Impose global ordering for all decl tags used by test_loader.c based
tests: __success, __failure, __msg, etc. The tags are now sorted by
testing framework to be processed in the same order they appear in the
C source code of the test.
The ordering is necessary for gcc-bpf. Neither GCC nor the C standard
defines the order in which function attributes are consumed.
While Clang tends to preserve tags definition order in the output BTF,
GCC does not. This inconsistency causes BPF tests with multiple __msg
entries to fail when compiled with GCC.
This is based on a patch [1] from Cupertino Miranda (see patch #3) and
includes some additional cleanups for test_loader.c decl tags
declaration and processing (see patches #1, #2, #4).
[1] https://lore.kernel.org/bpf/20260305130035.192080-1-cupertino.miranda@oracle.com/
---
Eduard Zingerman (4):
selftests/bpf: fix __jited_unpriv tag name
selftests/bpf: make str_has_pfx return pointer past the prefix
selftests/bpf: impose global ordering for test decl_tags
selftests/bpf: inline TEST_TAG constants in test_loader.c
tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++----
tools/testing/selftests/bpf/test_loader.c | 199 +++++++++++++--------------
2 files changed, 126 insertions(+), 133 deletions(-)
---
base-commit: 400ff899c336c24bf4d34479f98cef2fd2d3482a
change-id: 20260326-selftests-global-tags-ordering-8f324323d9c8
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name
2026-03-26 17:39 [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
@ 2026-03-26 17:39 ` Eduard Zingerman
2026-03-27 21:52 ` Ihor Solodrai
2026-03-26 17:39 ` [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix Eduard Zingerman
` (3 subsequent siblings)
4 siblings, 1 reply; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 17:39 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87,
cupertino.miranda
__jited_unpriv was using "test_jited=" as its tag name, same as the
priv variant __jited. Fix by using "test_jited_unpriv=".
Fixes: 7d743e4c759c ("selftests/bpf: __jited test tag to check disassembly after jit")
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
tools/testing/selftests/bpf/progs/bpf_misc.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index 0904fe14ad1d45aba2f2c8a9a12881a0d248685e..52cf9f3cccd0ccd8034b9786d787e53992a4a6d8 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -140,7 +140,7 @@
#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
#define __not_msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
+#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited_unpriv=" XSTR(__COUNTER__) "=" msg)))
#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
--
2.53.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix
2026-03-26 17:39 [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
2026-03-26 17:39 ` [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name Eduard Zingerman
@ 2026-03-26 17:39 ` Eduard Zingerman
2026-03-27 21:54 ` Ihor Solodrai
2026-03-26 17:39 ` [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
` (2 subsequent siblings)
4 siblings, 1 reply; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 17:39 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87,
cupertino.miranda
Change str_has_pfx() to return a pointer to the first character after
the prefix, thus eliminating the repetitive (s + sizeof(PFX) - 1)
patterns.
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
tools/testing/selftests/bpf/test_loader.c | 41 ++++++++++++++-----------------
1 file changed, 18 insertions(+), 23 deletions(-)
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index 338c035c36884c02ac57cd17110122c10b4858e4..f1eb99f829e112f0e738bfe44e7e61a49120ec85 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -11,8 +11,12 @@
#include "cap_helpers.h"
#include "jit_disasm_helpers.h"
-#define str_has_pfx(str, pfx) \
- (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
+static inline const char *str_has_pfx(const char *str, const char *pfx)
+{
+ size_t len = strlen(pfx);
+
+ return strncmp(str, pfx, len) == 0 ? str + len : NULL;
+}
#define TEST_LOADER_LOG_BUF_SZ 2097152
@@ -452,8 +456,8 @@ static int parse_test_spec(struct test_loader *tester,
continue;
s = btf__str_by_offset(btf, t->name_off);
- if (str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX)) {
- description = s + sizeof(TEST_TAG_DESCRIPTION_PFX) - 1;
+ if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) {
+ description = val;
} else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) {
spec->priv.expect_failure = true;
spec->mode_mask |= PRIV;
@@ -530,29 +534,24 @@ static int parse_test_spec(struct test_loader *tester,
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX)) {
- val = s + sizeof(TEST_TAG_RETVAL_PFX) - 1;
+ } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX))) {
err = parse_retval(val, &spec->priv.retval, "__retval");
if (err)
goto cleanup;
spec->priv.execute = true;
spec->mode_mask |= PRIV;
- } else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV)) {
- val = s + sizeof(TEST_TAG_RETVAL_PFX_UNPRIV) - 1;
+ } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV))) {
err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv");
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
spec->unpriv.execute = true;
has_unpriv_retval = true;
- } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) {
- val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1;
+ } else if ((val = str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX))) {
err = parse_int(val, &spec->log_level, "test log level");
if (err)
goto cleanup;
- } else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) {
- val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1;
-
+ } else if ((val = str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX))) {
clear = val[0] == '!';
if (clear)
val++;
@@ -577,8 +576,7 @@ static int parse_test_spec(struct test_loader *tester,
goto cleanup;
update_flags(&spec->prog_flags, flags, clear);
}
- } else if (str_has_pfx(s, TEST_TAG_ARCH)) {
- val = s + sizeof(TEST_TAG_ARCH) - 1;
+ } else if ((val = str_has_pfx(s, TEST_TAG_ARCH))) {
if (strcmp(val, "X86_64") == 0) {
arch = ARCH_X86_64;
} else if (strcmp(val, "ARM64") == 0) {
@@ -596,16 +594,14 @@ static int parse_test_spec(struct test_loader *tester,
collect_jit = get_current_arch() == arch;
unpriv_jit_on_next_line = true;
jit_on_next_line = true;
- } else if (str_has_pfx(s, TEST_BTF_PATH)) {
- spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
- } else if (str_has_pfx(s, TEST_TAG_CAPS_UNPRIV)) {
- val = s + sizeof(TEST_TAG_CAPS_UNPRIV) - 1;
+ } else if ((val = str_has_pfx(s, TEST_BTF_PATH))) {
+ spec->btf_custom_path = val;
+ } else if ((val = str_has_pfx(s, TEST_TAG_CAPS_UNPRIV))) {
err = parse_caps(val, &spec->unpriv.caps, "test caps");
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if (str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX)) {
- val = s + sizeof(TEST_TAG_LOAD_MODE_PFX) - 1;
+ } else if ((val = str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX))) {
if (strcmp(val, "jited") == 0) {
load_mask = JITED;
} else if (strcmp(val, "no_jited") == 0) {
@@ -635,12 +631,11 @@ static int parse_test_spec(struct test_loader *tester,
&spec->unpriv.stdout);
if (err)
goto cleanup;
- } else if (str_has_pfx(s, TEST_TAG_LINEAR_SIZE)) {
+ } else if ((val = str_has_pfx(s, TEST_TAG_LINEAR_SIZE))) {
switch (bpf_program__type(prog)) {
case BPF_PROG_TYPE_SCHED_ACT:
case BPF_PROG_TYPE_SCHED_CLS:
case BPF_PROG_TYPE_CGROUP_SKB:
- val = s + sizeof(TEST_TAG_LINEAR_SIZE) - 1;
err = parse_int(val, &spec->linear_sz, "test linear size");
if (err)
goto cleanup;
--
2.53.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 17:39 [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
2026-03-26 17:39 ` [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name Eduard Zingerman
2026-03-26 17:39 ` [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix Eduard Zingerman
@ 2026-03-26 17:39 ` Eduard Zingerman
2026-03-26 22:47 ` Andrii Nakryiko
2026-03-27 22:00 ` Ihor Solodrai
2026-03-26 17:39 ` [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c Eduard Zingerman
2026-03-26 22:28 ` [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
4 siblings, 2 replies; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 17:39 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87,
cupertino.miranda
Impose global ordering for all decl tags used by test_loader.c based
tests (__success, __failure, __msg, etc):
- change every tag to expand as
__attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...)))
- change parse_test_spec() to collect all decl tags before
processing and sort them using strverscmp().
The ordering is necessary for gcc-bpf.
Neither GCC nor the C standard defines the order in which function
attributes are consumed. While Clang tends to preserve definition order,
GCC may process them out of sequence. This inconsistency causes BPF
tests with multiple __msg entries to fail when compiled with GCC.
Co-developed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++-----
tools/testing/selftests/bpf/test_loader.c | 174 +++++++++++++++------------
2 files changed, 130 insertions(+), 104 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index 52cf9f3cccd0ccd8034b9786d787e53992a4a6d8..32e06c575fbe1f36264c8965cbd84e4432a434d8 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -130,39 +130,41 @@
* __linear_size Specify the size of the linear area of non-linear skbs, or
* 0 for linear skbs.
*/
-#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
-#define __not_msg(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg=" XSTR(__COUNTER__) "=" msg)))
-#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
-#define __jited(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
-#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
-#define __success __attribute__((btf_decl_tag("comment:test_expect_success")))
-#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc)))
-#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __not_msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
-#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
-#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
-#define __flag(flag) __attribute__((btf_decl_tag("comment:test_prog_flags="#flag)))
-#define __retval(val) __attribute__((btf_decl_tag("comment:test_retval="XSTR(val))))
-#define __retval_unpriv(val) __attribute__((btf_decl_tag("comment:test_retval_unpriv="XSTR(val))))
-#define __auxiliary __attribute__((btf_decl_tag("comment:test_auxiliary")))
-#define __auxiliary_unpriv __attribute__((btf_decl_tag("comment:test_auxiliary_unpriv")))
-#define __btf_path(path) __attribute__((btf_decl_tag("comment:test_btf_path=" path)))
-#define __arch(arch) __attribute__((btf_decl_tag("comment:test_arch=" arch)))
+#define __test_tag(tag) __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ":" tag)))
+
+#define __msg(msg) __test_tag("test_expect_msg=" msg)
+#define __not_msg(msg) __test_tag("test_expect_not_msg=" msg)
+#define __xlated(msg) __test_tag("test_expect_xlated=" msg)
+#define __jited(msg) __test_tag("test_jited=" msg)
+#define __failure __test_tag("test_expect_failure")
+#define __success __test_tag("test_expect_success")
+#define __description(desc) __test_tag("test_description=" desc)
+#define __msg_unpriv(msg) __test_tag("test_expect_msg_unpriv=" msg)
+#define __not_msg_unpriv(msg) __test_tag("test_expect_not_msg_unpriv=" msg)
+#define __xlated_unpriv(msg) __test_tag("test_expect_xlated_unpriv=" msg)
+#define __jited_unpriv(msg) __test_tag("test_jited_unpriv=" msg)
+#define __failure_unpriv __test_tag("test_expect_failure_unpriv")
+#define __success_unpriv __test_tag("test_expect_success_unpriv")
+#define __log_level(lvl) __test_tag("test_log_level=" #lvl)
+#define __flag(flag) __test_tag("test_prog_flags=" #flag)
+#define __retval(val) __test_tag("test_retval=" XSTR(val))
+#define __retval_unpriv(val) __test_tag("test_retval_unpriv=" XSTR(val))
+#define __auxiliary __test_tag("test_auxiliary")
+#define __auxiliary_unpriv __test_tag("test_auxiliary_unpriv")
+#define __btf_path(path) __test_tag("test_btf_path=" path)
+#define __arch(arch) __test_tag("test_arch=" arch)
#define __arch_x86_64 __arch("X86_64")
#define __arch_arm64 __arch("ARM64")
#define __arch_riscv64 __arch("RISCV64")
#define __arch_s390x __arch("s390x")
-#define __caps_unpriv(caps) __attribute__((btf_decl_tag("comment:test_caps_unpriv=" EXPAND_QUOTE(caps))))
-#define __load_if_JITed() __attribute__((btf_decl_tag("comment:load_mode=jited")))
-#define __load_if_no_JITed() __attribute__((btf_decl_tag("comment:load_mode=no_jited")))
-#define __stderr(msg) __attribute__((btf_decl_tag("comment:test_expect_stderr=" XSTR(__COUNTER__) "=" msg)))
-#define __stderr_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_stderr_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __stdout(msg) __attribute__((btf_decl_tag("comment:test_expect_stdout=" XSTR(__COUNTER__) "=" msg)))
-#define __stdout_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_stdout_unpriv=" XSTR(__COUNTER__) "=" msg)))
-#define __linear_size(sz) __attribute__((btf_decl_tag("comment:test_linear_size=" XSTR(sz))))
+#define __caps_unpriv(caps) __test_tag("test_caps_unpriv=" EXPAND_QUOTE(caps))
+#define __load_if_JITed() __test_tag("load_mode=jited")
+#define __load_if_no_JITed() __test_tag("load_mode=no_jited")
+#define __stderr(msg) __test_tag("test_expect_stderr=" msg)
+#define __stderr_unpriv(msg) __test_tag("test_expect_stderr_unpriv=" msg)
+#define __stdout(msg) __test_tag("test_expect_stdout=" msg)
+#define __stdout_unpriv(msg) __test_tag("test_expect_stdout_unpriv=" msg)
+#define __linear_size(sz) __test_tag("test_linear_size=" XSTR(sz))
/* Define common capabilities tested using __caps_unpriv */
#define CAP_NET_ADMIN 12
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index f1eb99f829e112f0e738bfe44e7e61a49120ec85..2779f0ecf0ada9985a5b1dfbc049c0d0ed824a76 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include <linux/capability.h>
+#include <linux/err.h>
#include <stdlib.h>
#include <test_progs.h>
#include <bpf/btf.h>
@@ -20,34 +21,34 @@ static inline const char *str_has_pfx(const char *str, const char *pfx)
#define TEST_LOADER_LOG_BUF_SZ 2097152
-#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure"
-#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
-#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
-#define TEST_TAG_EXPECT_NOT_MSG_PFX "comment:test_expect_not_msg="
-#define TEST_TAG_EXPECT_XLATED_PFX "comment:test_expect_xlated="
-#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv"
-#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv"
-#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv="
-#define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "comment:test_expect_not_msg_unpriv="
-#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "comment:test_expect_xlated_unpriv="
-#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
-#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
-#define TEST_TAG_DESCRIPTION_PFX "comment:test_description="
-#define TEST_TAG_RETVAL_PFX "comment:test_retval="
-#define TEST_TAG_RETVAL_PFX_UNPRIV "comment:test_retval_unpriv="
-#define TEST_TAG_AUXILIARY "comment:test_auxiliary"
-#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv"
-#define TEST_BTF_PATH "comment:test_btf_path="
-#define TEST_TAG_ARCH "comment:test_arch="
-#define TEST_TAG_JITED_PFX "comment:test_jited="
-#define TEST_TAG_JITED_PFX_UNPRIV "comment:test_jited_unpriv="
-#define TEST_TAG_CAPS_UNPRIV "comment:test_caps_unpriv="
-#define TEST_TAG_LOAD_MODE_PFX "comment:load_mode="
-#define TEST_TAG_EXPECT_STDERR_PFX "comment:test_expect_stderr="
-#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "comment:test_expect_stderr_unpriv="
-#define TEST_TAG_EXPECT_STDOUT_PFX "comment:test_expect_stdout="
-#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "comment:test_expect_stdout_unpriv="
-#define TEST_TAG_LINEAR_SIZE "comment:test_linear_size="
+#define TEST_TAG_EXPECT_FAILURE "test_expect_failure"
+#define TEST_TAG_EXPECT_SUCCESS "test_expect_success"
+#define TEST_TAG_EXPECT_MSG_PFX "test_expect_msg="
+#define TEST_TAG_EXPECT_NOT_MSG_PFX "test_expect_not_msg="
+#define TEST_TAG_EXPECT_XLATED_PFX "test_expect_xlated="
+#define TEST_TAG_EXPECT_FAILURE_UNPRIV "test_expect_failure_unpriv"
+#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "test_expect_success_unpriv"
+#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "test_expect_msg_unpriv="
+#define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "test_expect_not_msg_unpriv="
+#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "test_expect_xlated_unpriv="
+#define TEST_TAG_LOG_LEVEL_PFX "test_log_level="
+#define TEST_TAG_PROG_FLAGS_PFX "test_prog_flags="
+#define TEST_TAG_DESCRIPTION_PFX "test_description="
+#define TEST_TAG_RETVAL_PFX "test_retval="
+#define TEST_TAG_RETVAL_PFX_UNPRIV "test_retval_unpriv="
+#define TEST_TAG_AUXILIARY "test_auxiliary"
+#define TEST_TAG_AUXILIARY_UNPRIV "test_auxiliary_unpriv"
+#define TEST_BTF_PATH "test_btf_path="
+#define TEST_TAG_ARCH "test_arch="
+#define TEST_TAG_JITED_PFX "test_jited="
+#define TEST_TAG_JITED_PFX_UNPRIV "test_jited_unpriv="
+#define TEST_TAG_CAPS_UNPRIV "test_caps_unpriv="
+#define TEST_TAG_LOAD_MODE_PFX "load_mode="
+#define TEST_TAG_EXPECT_STDERR_PFX "test_expect_stderr="
+#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "test_expect_stderr_unpriv="
+#define TEST_TAG_EXPECT_STDOUT_PFX "test_expect_stdout="
+#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "test_expect_stdout_unpriv="
+#define TEST_TAG_LINEAR_SIZE "test_linear_size="
/* Warning: duplicated in bpf_misc.h */
#define POINTER_VALUE 0xbadcafe
@@ -347,33 +348,54 @@ static void update_flags(int *flags, int flag, bool clear)
*flags |= flag;
}
-/* Matches a string of form '<pfx>[^=]=.*' and returns it's suffix.
- * Used to parse btf_decl_tag values.
- * Such values require unique prefix because compiler does not add
- * same __attribute__((btf_decl_tag(...))) twice.
- * Test suite uses two-component tags for such cases:
- *
- * <pfx> __COUNTER__ '='
- *
- * For example, two consecutive __msg tags '__msg("foo") __msg("foo")'
- * would be encoded as:
- *
- * [18] DECL_TAG 'comment:test_expect_msg=0=foo' type_id=15 component_idx=-1
- * [19] DECL_TAG 'comment:test_expect_msg=1=foo' type_id=15 component_idx=-1
- *
- * And the purpose of this function is to extract 'foo' from the above.
+static const char *skip_decl_tag_pfx(const char *s)
+{
+ int n = 0;
+
+ if (sscanf(s, "comment:%*d:%n", &n) < 0 || !n)
+ return s;
+ return s + n;
+}
+
+static void *realloc_or_free(void *ptr, size_t size)
+{
+ void *new = realloc(ptr, size);
+
+ if (!new)
+ free(ptr);
+ return new;
+}
+
+static int compare_decl_tags(const void *a, const void *b)
+{
+ return strverscmp(*(const char **)a, *(const char **)b);
+}
+
+/*
+ * Compilers don't guarantee order in which BTF attributes would be generated,
+ * while order is important for test tags like __msg.
+ * Each test tag has the following prefix: "comment:" __COUNTER__,
+ * when sorted using strverscmp this gives same order as in the original C code.
*/
-static const char *skip_dynamic_pfx(const char *s, const char *pfx)
+static const char **collect_decl_tags(struct btf *btf, int id, int *cnt)
{
- const char *msg;
-
- if (strncmp(s, pfx, strlen(pfx)) != 0)
- return NULL;
- msg = s + strlen(pfx);
- msg = strchr(msg, '=');
- if (!msg)
- return NULL;
- return msg + 1;
+ const struct btf_type *t;
+ const char **tags = NULL;
+ int i;
+
+ *cnt = 0;
+ for (i = 1; i < btf__type_cnt(btf); i++) {
+ t = btf__type_by_id(btf, i);
+ if (!btf_is_decl_tag(t) || t->type != id || btf_decl_tag(t)->component_idx != -1)
+ continue;
+ tags = realloc_or_free(tags, (*cnt + 1) * sizeof(*tags));
+ if (!tags)
+ return ERR_PTR(-ENOMEM);
+ tags[(*cnt)++] = btf__str_by_offset(btf, t->name_off);
+ }
+
+ qsort(tags, *cnt, sizeof(*tags), compare_decl_tags);
+ return tags;
}
enum arch {
@@ -419,7 +441,9 @@ static int parse_test_spec(struct test_loader *tester,
bool stdout_on_next_line = true;
bool unpriv_stdout_on_next_line = true;
bool collect_jit = false;
- int func_id, i, err = 0;
+ const char **tags = NULL;
+ int func_id, i, nr_tags;
+ int err = 0;
u32 arch_mask = 0;
u32 load_mask = 0;
struct btf *btf;
@@ -442,20 +466,18 @@ static int parse_test_spec(struct test_loader *tester,
return -EINVAL;
}
- for (i = 1; i < btf__type_cnt(btf); i++) {
+ tags = collect_decl_tags(btf, func_id, &nr_tags);
+ if (IS_ERR(tags))
+ return PTR_ERR(tags);
+
+ for (i = 0; i < nr_tags; i++) {
const char *s, *val, *msg;
- const struct btf_type *t;
bool clear;
int flags;
- t = btf__type_by_id(btf, i);
- if (!btf_is_decl_tag(t))
+ s = skip_decl_tag_pfx(tags[i]);
+ if (!s)
continue;
-
- if (t->type != func_id || btf_decl_tag(t)->component_idx != -1)
- continue;
-
- s = btf__str_by_offset(btf, t->name_off);
if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) {
description = val;
} else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) {
@@ -478,27 +500,27 @@ static int parse_test_spec(struct test_loader *tester,
} else if (strcmp(s, TEST_TAG_AUXILIARY_UNPRIV) == 0) {
spec->auxiliary = true;
spec->mode_mask |= UNPRIV;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) {
err = push_msg(msg, false, &spec->priv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= PRIV;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) {
err = push_msg(msg, true, &spec->priv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= PRIV;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) {
err = push_msg(msg, false, &spec->unpriv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) {
err = push_msg(msg, true, &spec->unpriv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX))) {
if (arch_mask == 0) {
PRINT_FAIL("__jited used before __arch_*");
goto cleanup;
@@ -510,7 +532,7 @@ static int parse_test_spec(struct test_loader *tester,
goto cleanup;
spec->mode_mask |= PRIV;
}
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) {
if (arch_mask == 0) {
PRINT_FAIL("__unpriv_jited used before __arch_*");
goto cleanup;
@@ -522,13 +544,13 @@ static int parse_test_spec(struct test_loader *tester,
goto cleanup;
spec->mode_mask |= UNPRIV;
}
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
err = push_disasm_msg(msg, &xlated_on_next_line,
&spec->priv.expect_xlated);
if (err)
goto cleanup;
spec->mode_mask |= PRIV;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) {
err = push_disasm_msg(msg, &unpriv_xlated_on_next_line,
&spec->unpriv.expect_xlated);
if (err)
@@ -611,22 +633,22 @@ static int parse_test_spec(struct test_loader *tester,
err = -EINVAL;
goto cleanup;
}
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDERR_PFX))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX))) {
err = push_disasm_msg(msg, &stderr_on_next_line,
&spec->priv.stderr);
if (err)
goto cleanup;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDERR_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX_UNPRIV))) {
err = push_disasm_msg(msg, &unpriv_stderr_on_next_line,
&spec->unpriv.stderr);
if (err)
goto cleanup;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX))) {
err = push_disasm_msg(msg, &stdout_on_next_line,
&spec->priv.stdout);
if (err)
goto cleanup;
- } else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV))) {
err = push_disasm_msg(msg, &unpriv_stdout_on_next_line,
&spec->unpriv.stdout);
if (err)
@@ -706,9 +728,11 @@ static int parse_test_spec(struct test_loader *tester,
spec->valid = true;
+ free(tags);
return 0;
cleanup:
+ free(tags);
free_test_spec(spec);
return err;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c
2026-03-26 17:39 [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
` (2 preceding siblings ...)
2026-03-26 17:39 ` [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
@ 2026-03-26 17:39 ` Eduard Zingerman
2026-03-27 22:04 ` Ihor Solodrai
2026-03-26 22:28 ` [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
4 siblings, 1 reply; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 17:39 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, eddyz87,
cupertino.miranda
After str_has_pfx() refactoring each TEST_TAG_* / TEST_BTF_PATH
constant is used exactly once. Since constant definitions are not
shared between BPF-side bpf_misc.h and userspace side test_loader.c,
there is no need in the additional redirection layer.
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
tools/testing/selftests/bpf/test_loader.c | 84 +++++++++++--------------------
1 file changed, 28 insertions(+), 56 deletions(-)
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index 2779f0ecf0ada9985a5b1dfbc049c0d0ed824a76..6a2211f21fe13b37e0dacb62f404b8aedc5bcd58 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -21,34 +21,6 @@ static inline const char *str_has_pfx(const char *str, const char *pfx)
#define TEST_LOADER_LOG_BUF_SZ 2097152
-#define TEST_TAG_EXPECT_FAILURE "test_expect_failure"
-#define TEST_TAG_EXPECT_SUCCESS "test_expect_success"
-#define TEST_TAG_EXPECT_MSG_PFX "test_expect_msg="
-#define TEST_TAG_EXPECT_NOT_MSG_PFX "test_expect_not_msg="
-#define TEST_TAG_EXPECT_XLATED_PFX "test_expect_xlated="
-#define TEST_TAG_EXPECT_FAILURE_UNPRIV "test_expect_failure_unpriv"
-#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "test_expect_success_unpriv"
-#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "test_expect_msg_unpriv="
-#define TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV "test_expect_not_msg_unpriv="
-#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "test_expect_xlated_unpriv="
-#define TEST_TAG_LOG_LEVEL_PFX "test_log_level="
-#define TEST_TAG_PROG_FLAGS_PFX "test_prog_flags="
-#define TEST_TAG_DESCRIPTION_PFX "test_description="
-#define TEST_TAG_RETVAL_PFX "test_retval="
-#define TEST_TAG_RETVAL_PFX_UNPRIV "test_retval_unpriv="
-#define TEST_TAG_AUXILIARY "test_auxiliary"
-#define TEST_TAG_AUXILIARY_UNPRIV "test_auxiliary_unpriv"
-#define TEST_BTF_PATH "test_btf_path="
-#define TEST_TAG_ARCH "test_arch="
-#define TEST_TAG_JITED_PFX "test_jited="
-#define TEST_TAG_JITED_PFX_UNPRIV "test_jited_unpriv="
-#define TEST_TAG_CAPS_UNPRIV "test_caps_unpriv="
-#define TEST_TAG_LOAD_MODE_PFX "load_mode="
-#define TEST_TAG_EXPECT_STDERR_PFX "test_expect_stderr="
-#define TEST_TAG_EXPECT_STDERR_PFX_UNPRIV "test_expect_stderr_unpriv="
-#define TEST_TAG_EXPECT_STDOUT_PFX "test_expect_stdout="
-#define TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV "test_expect_stdout_unpriv="
-#define TEST_TAG_LINEAR_SIZE "test_linear_size="
/* Warning: duplicated in bpf_misc.h */
#define POINTER_VALUE 0xbadcafe
@@ -478,49 +450,49 @@ static int parse_test_spec(struct test_loader *tester,
s = skip_decl_tag_pfx(tags[i]);
if (!s)
continue;
- if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) {
+ if ((val = str_has_pfx(s, "test_description="))) {
description = val;
- } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) {
+ } else if (strcmp(s, "test_expect_failure") == 0) {
spec->priv.expect_failure = true;
spec->mode_mask |= PRIV;
- } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) {
+ } else if (strcmp(s, "test_expect_success") == 0) {
spec->priv.expect_failure = false;
spec->mode_mask |= PRIV;
- } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE_UNPRIV) == 0) {
+ } else if (strcmp(s, "test_expect_failure_unpriv") == 0) {
spec->unpriv.expect_failure = true;
spec->mode_mask |= UNPRIV;
has_unpriv_result = true;
- } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS_UNPRIV) == 0) {
+ } else if (strcmp(s, "test_expect_success_unpriv") == 0) {
spec->unpriv.expect_failure = false;
spec->mode_mask |= UNPRIV;
has_unpriv_result = true;
- } else if (strcmp(s, TEST_TAG_AUXILIARY) == 0) {
+ } else if (strcmp(s, "test_auxiliary") == 0) {
spec->auxiliary = true;
spec->mode_mask |= PRIV;
- } else if (strcmp(s, TEST_TAG_AUXILIARY_UNPRIV) == 0) {
+ } else if (strcmp(s, "test_auxiliary_unpriv") == 0) {
spec->auxiliary = true;
spec->mode_mask |= UNPRIV;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_msg="))) {
err = push_msg(msg, false, &spec->priv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= PRIV;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_not_msg="))) {
err = push_msg(msg, true, &spec->priv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= PRIV;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_msg_unpriv="))) {
err = push_msg(msg, false, &spec->unpriv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_NOT_MSG_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_not_msg_unpriv="))) {
err = push_msg(msg, true, &spec->unpriv.expect_msgs);
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX))) {
+ } else if ((msg = str_has_pfx(s, "test_jited="))) {
if (arch_mask == 0) {
PRINT_FAIL("__jited used before __arch_*");
goto cleanup;
@@ -532,7 +504,7 @@ static int parse_test_spec(struct test_loader *tester,
goto cleanup;
spec->mode_mask |= PRIV;
}
- } else if ((msg = str_has_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, "test_jited_unpriv="))) {
if (arch_mask == 0) {
PRINT_FAIL("__unpriv_jited used before __arch_*");
goto cleanup;
@@ -544,36 +516,36 @@ static int parse_test_spec(struct test_loader *tester,
goto cleanup;
spec->mode_mask |= UNPRIV;
}
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_xlated="))) {
err = push_disasm_msg(msg, &xlated_on_next_line,
&spec->priv.expect_xlated);
if (err)
goto cleanup;
spec->mode_mask |= PRIV;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_xlated_unpriv="))) {
err = push_disasm_msg(msg, &unpriv_xlated_on_next_line,
&spec->unpriv.expect_xlated);
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX))) {
+ } else if ((val = str_has_pfx(s, "test_retval="))) {
err = parse_retval(val, &spec->priv.retval, "__retval");
if (err)
goto cleanup;
spec->priv.execute = true;
spec->mode_mask |= PRIV;
- } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV))) {
+ } else if ((val = str_has_pfx(s, "test_retval_unpriv="))) {
err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv");
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
spec->unpriv.execute = true;
has_unpriv_retval = true;
- } else if ((val = str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX))) {
+ } else if ((val = str_has_pfx(s, "test_log_level="))) {
err = parse_int(val, &spec->log_level, "test log level");
if (err)
goto cleanup;
- } else if ((val = str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX))) {
+ } else if ((val = str_has_pfx(s, "test_prog_flags="))) {
clear = val[0] == '!';
if (clear)
val++;
@@ -598,7 +570,7 @@ static int parse_test_spec(struct test_loader *tester,
goto cleanup;
update_flags(&spec->prog_flags, flags, clear);
}
- } else if ((val = str_has_pfx(s, TEST_TAG_ARCH))) {
+ } else if ((val = str_has_pfx(s, "test_arch="))) {
if (strcmp(val, "X86_64") == 0) {
arch = ARCH_X86_64;
} else if (strcmp(val, "ARM64") == 0) {
@@ -616,14 +588,14 @@ static int parse_test_spec(struct test_loader *tester,
collect_jit = get_current_arch() == arch;
unpriv_jit_on_next_line = true;
jit_on_next_line = true;
- } else if ((val = str_has_pfx(s, TEST_BTF_PATH))) {
+ } else if ((val = str_has_pfx(s, "test_btf_path="))) {
spec->btf_custom_path = val;
- } else if ((val = str_has_pfx(s, TEST_TAG_CAPS_UNPRIV))) {
+ } else if ((val = str_has_pfx(s, "test_caps_unpriv="))) {
err = parse_caps(val, &spec->unpriv.caps, "test caps");
if (err)
goto cleanup;
spec->mode_mask |= UNPRIV;
- } else if ((val = str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX))) {
+ } else if ((val = str_has_pfx(s, "load_mode="))) {
if (strcmp(val, "jited") == 0) {
load_mask = JITED;
} else if (strcmp(val, "no_jited") == 0) {
@@ -633,27 +605,27 @@ static int parse_test_spec(struct test_loader *tester,
err = -EINVAL;
goto cleanup;
}
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_stderr="))) {
err = push_disasm_msg(msg, &stderr_on_next_line,
&spec->priv.stderr);
if (err)
goto cleanup;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDERR_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_stderr_unpriv="))) {
err = push_disasm_msg(msg, &unpriv_stderr_on_next_line,
&spec->unpriv.stderr);
if (err)
goto cleanup;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_stdout="))) {
err = push_disasm_msg(msg, &stdout_on_next_line,
&spec->priv.stdout);
if (err)
goto cleanup;
- } else if ((msg = str_has_pfx(s, TEST_TAG_EXPECT_STDOUT_PFX_UNPRIV))) {
+ } else if ((msg = str_has_pfx(s, "test_expect_stdout_unpriv="))) {
err = push_disasm_msg(msg, &unpriv_stdout_on_next_line,
&spec->unpriv.stdout);
if (err)
goto cleanup;
- } else if ((val = str_has_pfx(s, TEST_TAG_LINEAR_SIZE))) {
+ } else if ((val = str_has_pfx(s, "test_linear_size="))) {
switch (bpf_program__type(prog)) {
case BPF_PROG_TYPE_SCHED_ACT:
case BPF_PROG_TYPE_SCHED_CLS:
--
2.53.0
^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 17:39 [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
` (3 preceding siblings ...)
2026-03-26 17:39 ` [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c Eduard Zingerman
@ 2026-03-26 22:28 ` Eduard Zingerman
4 siblings, 0 replies; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 22:28 UTC (permalink / raw)
To: bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On Thu, 2026-03-26 at 10:39 -0700, Eduard Zingerman wrote:
> Impose global ordering for all decl tags used by test_loader.c based
> tests: __success, __failure, __msg, etc. The tags are now sorted by
> testing framework to be processed in the same order they appear in the
> C source code of the test.
>
> The ordering is necessary for gcc-bpf. Neither GCC nor the C standard
> defines the order in which function attributes are consumed.
> While Clang tends to preserve tags definition order in the output BTF,
> GCC does not. This inconsistency causes BPF tests with multiple __msg
> entries to fail when compiled with GCC.
>
> This is based on a patch [1] from Cupertino Miranda (see patch #3) and
> includes some additional cleanups for test_loader.c decl tags
> declaration and processing (see patches #1, #2, #4).
>
> [1] https://lore.kernel.org/bpf/20260305130035.192080-1-cupertino.miranda@oracle.com/
>
> ---
I did not specify the bpf-next tag :(
Will wait a bit and resend.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 17:39 ` [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
@ 2026-03-26 22:47 ` Andrii Nakryiko
2026-03-26 22:51 ` Eduard Zingerman
2026-03-27 22:00 ` Ihor Solodrai
1 sibling, 1 reply; 16+ messages in thread
From: Andrii Nakryiko @ 2026-03-26 22:47 UTC (permalink / raw)
To: Eduard Zingerman
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
cupertino.miranda
On Thu, Mar 26, 2026 at 10:39 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> Impose global ordering for all decl tags used by test_loader.c based
> tests (__success, __failure, __msg, etc):
> - change every tag to expand as
> __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...)))
> - change parse_test_spec() to collect all decl tags before
> processing and sort them using strverscmp().
>
> The ordering is necessary for gcc-bpf.
> Neither GCC nor the C standard defines the order in which function
> attributes are consumed. While Clang tends to preserve definition order,
> GCC may process them out of sequence. This inconsistency causes BPF
> tests with multiple __msg entries to fail when compiled with GCC.
>
> Co-developed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
> tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++-----
> tools/testing/selftests/bpf/test_loader.c | 174 +++++++++++++++------------
> 2 files changed, 130 insertions(+), 104 deletions(-)
[...]
> -/* Matches a string of form '<pfx>[^=]=.*' and returns it's suffix.
> - * Used to parse btf_decl_tag values.
> - * Such values require unique prefix because compiler does not add
> - * same __attribute__((btf_decl_tag(...))) twice.
> - * Test suite uses two-component tags for such cases:
> - *
> - * <pfx> __COUNTER__ '='
> - *
> - * For example, two consecutive __msg tags '__msg("foo") __msg("foo")'
> - * would be encoded as:
> - *
> - * [18] DECL_TAG 'comment:test_expect_msg=0=foo' type_id=15 component_idx=-1
> - * [19] DECL_TAG 'comment:test_expect_msg=1=foo' type_id=15 component_idx=-1
> - *
> - * And the purpose of this function is to extract 'foo' from the above.
> +static const char *skip_decl_tag_pfx(const char *s)
> +{
> + int n = 0;
> +
> + if (sscanf(s, "comment:%*d:%n", &n) < 0 || !n)
> + return s;
> + return s + n;
> +}
> +
> +static void *realloc_or_free(void *ptr, size_t size)
but why?... why invent a new realloc usage pattern just to save two
lines of error handling code?
> +{
> + void *new = realloc(ptr, size);
> +
> + if (!new)
> + free(ptr);
> + return new;
> +}
> +
> +static int compare_decl_tags(const void *a, const void *b)
> +{
> + return strverscmp(*(const char **)a, *(const char **)b);
> +}
> +
[...]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 22:47 ` Andrii Nakryiko
@ 2026-03-26 22:51 ` Eduard Zingerman
2026-03-26 23:00 ` Andrii Nakryiko
0 siblings, 1 reply; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 22:51 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
cupertino.miranda
On Thu, 2026-03-26 at 15:47 -0700, Andrii Nakryiko wrote:
> On Thu, Mar 26, 2026 at 10:39 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
> >
> > Impose global ordering for all decl tags used by test_loader.c based
> > tests (__success, __failure, __msg, etc):
> > - change every tag to expand as
> > __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...)))
> > - change parse_test_spec() to collect all decl tags before
> > processing and sort them using strverscmp().
> >
> > The ordering is necessary for gcc-bpf.
> > Neither GCC nor the C standard defines the order in which function
> > attributes are consumed. While Clang tends to preserve definition order,
> > GCC may process them out of sequence. This inconsistency causes BPF
> > tests with multiple __msg entries to fail when compiled with GCC.
> >
> > Co-developed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
> > Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> > ---
> > tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++-----
> > tools/testing/selftests/bpf/test_loader.c | 174 +++++++++++++++------------
> > 2 files changed, 130 insertions(+), 104 deletions(-)
>
> [...]
>
> > -/* Matches a string of form '<pfx>[^=]=.*' and returns it's suffix.
> > - * Used to parse btf_decl_tag values.
> > - * Such values require unique prefix because compiler does not add
> > - * same __attribute__((btf_decl_tag(...))) twice.
> > - * Test suite uses two-component tags for such cases:
> > - *
> > - * <pfx> __COUNTER__ '='
> > - *
> > - * For example, two consecutive __msg tags '__msg("foo") __msg("foo")'
> > - * would be encoded as:
> > - *
> > - * [18] DECL_TAG 'comment:test_expect_msg=0=foo' type_id=15 component_idx=-1
> > - * [19] DECL_TAG 'comment:test_expect_msg=1=foo' type_id=15 component_idx=-1
> > - *
> > - * And the purpose of this function is to extract 'foo' from the above.
> > +static const char *skip_decl_tag_pfx(const char *s)
> > +{
> > + int n = 0;
> > +
> > + if (sscanf(s, "comment:%*d:%n", &n) < 0 || !n)
> > + return s;
> > + return s + n;
> > +}
> > +
> > +static void *realloc_or_free(void *ptr, size_t size)
>
> but why?... why invent a new realloc usage pattern just to save two
> lines of error handling code?
Yes. We have this in verifier.c:realloc_array() and I like it.
Can through this one away if you don't like it.
>
> > +{
> > + void *new = realloc(ptr, size);
> > +
> > + if (!new)
> > + free(ptr);
> > + return new;
> > +}
> > +
> > +static int compare_decl_tags(const void *a, const void *b)
> > +{
> > + return strverscmp(*(const char **)a, *(const char **)b);
> > +}
> > +
>
> [...]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 22:51 ` Eduard Zingerman
@ 2026-03-26 23:00 ` Andrii Nakryiko
2026-03-26 23:30 ` Eduard Zingerman
0 siblings, 1 reply; 16+ messages in thread
From: Andrii Nakryiko @ 2026-03-26 23:00 UTC (permalink / raw)
To: Eduard Zingerman
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
cupertino.miranda
On Thu, Mar 26, 2026 at 3:51 PM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Thu, 2026-03-26 at 15:47 -0700, Andrii Nakryiko wrote:
> > On Thu, Mar 26, 2026 at 10:39 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
> > >
> > > Impose global ordering for all decl tags used by test_loader.c based
> > > tests (__success, __failure, __msg, etc):
> > > - change every tag to expand as
> > > __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...)))
> > > - change parse_test_spec() to collect all decl tags before
> > > processing and sort them using strverscmp().
> > >
> > > The ordering is necessary for gcc-bpf.
> > > Neither GCC nor the C standard defines the order in which function
> > > attributes are consumed. While Clang tends to preserve definition order,
> > > GCC may process them out of sequence. This inconsistency causes BPF
> > > tests with multiple __msg entries to fail when compiled with GCC.
> > >
> > > Co-developed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
> > > Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> > > ---
> > > tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++-----
> > > tools/testing/selftests/bpf/test_loader.c | 174 +++++++++++++++------------
> > > 2 files changed, 130 insertions(+), 104 deletions(-)
> >
> > [...]
> >
> > > -/* Matches a string of form '<pfx>[^=]=.*' and returns it's suffix.
> > > - * Used to parse btf_decl_tag values.
> > > - * Such values require unique prefix because compiler does not add
> > > - * same __attribute__((btf_decl_tag(...))) twice.
> > > - * Test suite uses two-component tags for such cases:
> > > - *
> > > - * <pfx> __COUNTER__ '='
> > > - *
> > > - * For example, two consecutive __msg tags '__msg("foo") __msg("foo")'
> > > - * would be encoded as:
> > > - *
> > > - * [18] DECL_TAG 'comment:test_expect_msg=0=foo' type_id=15 component_idx=-1
> > > - * [19] DECL_TAG 'comment:test_expect_msg=1=foo' type_id=15 component_idx=-1
> > > - *
> > > - * And the purpose of this function is to extract 'foo' from the above.
> > > +static const char *skip_decl_tag_pfx(const char *s)
> > > +{
> > > + int n = 0;
> > > +
> > > + if (sscanf(s, "comment:%*d:%n", &n) < 0 || !n)
> > > + return s;
> > > + return s + n;
> > > +}
> > > +
> > > +static void *realloc_or_free(void *ptr, size_t size)
> >
> > but why?... why invent a new realloc usage pattern just to save two
> > lines of error handling code?
>
> Yes. We have this in verifier.c:realloc_array() and I like it.
> Can through this one away if you don't like it.
it just that you still have explicit free()s in that function for
clean up, so this realloc_or_free stands out as a deviation for no
good reason. So yeah, I don't think it's worth doing... but I'm also
not going to fight it, it just caught my "traditionalist"'s eye ;)
>
> >
> > > +{
> > > + void *new = realloc(ptr, size);
> > > +
> > > + if (!new)
> > > + free(ptr);
> > > + return new;
> > > +}
> > > +
> > > +static int compare_decl_tags(const void *a, const void *b)
> > > +{
> > > + return strverscmp(*(const char **)a, *(const char **)b);
> > > +}
> > > +
> >
> > [...]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 23:00 ` Andrii Nakryiko
@ 2026-03-26 23:30 ` Eduard Zingerman
0 siblings, 0 replies; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-26 23:30 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, ast, andrii, daniel, martin.lau, kernel-team, yonghong.song,
cupertino.miranda
On Thu, 2026-03-26 at 16:00 -0700, Andrii Nakryiko wrote:
[...]
> > > > +static void *realloc_or_free(void *ptr, size_t size)
> > >
> > > but why?... why invent a new realloc usage pattern just to save two
> > > lines of error handling code?
> >
> > Yes. We have this in verifier.c:realloc_array() and I like it.
> > Can through this one away if you don't like it.
>
> it just that you still have explicit free()s in that function for
> clean up, so this realloc_or_free stands out as a deviation for no
> good reason. So yeah, I don't think it's worth doing... but I'm also
> not going to fight it, it just caught my "traditionalist"'s eye ;)
I'll respin, no problem.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name
2026-03-26 17:39 ` [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name Eduard Zingerman
@ 2026-03-27 21:52 ` Ihor Solodrai
0 siblings, 0 replies; 16+ messages in thread
From: Ihor Solodrai @ 2026-03-27 21:52 UTC (permalink / raw)
To: Eduard Zingerman, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On 3/26/26 10:39 AM, Eduard Zingerman wrote:
> __jited_unpriv was using "test_jited=" as its tag name, same as the
> priv variant __jited. Fix by using "test_jited_unpriv=".
>
> Fixes: 7d743e4c759c ("selftests/bpf: __jited test tag to check disassembly after jit")
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
> tools/testing/selftests/bpf/progs/bpf_misc.h | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
> index 0904fe14ad1d45aba2f2c8a9a12881a0d248685e..52cf9f3cccd0ccd8034b9786d787e53992a4a6d8 100644
> --- a/tools/testing/selftests/bpf/progs/bpf_misc.h
> +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
> @@ -140,7 +140,7 @@
> #define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
> #define __not_msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_not_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
> #define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
> -#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
> +#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited_unpriv=" XSTR(__COUNTER__) "=" msg)))
Acked-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> #define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
> #define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
> #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix
2026-03-26 17:39 ` [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix Eduard Zingerman
@ 2026-03-27 21:54 ` Ihor Solodrai
2026-03-27 21:57 ` Eduard Zingerman
0 siblings, 1 reply; 16+ messages in thread
From: Ihor Solodrai @ 2026-03-27 21:54 UTC (permalink / raw)
To: Eduard Zingerman, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On 3/26/26 10:39 AM, Eduard Zingerman wrote:
> Change str_has_pfx() to return a pointer to the first character after
> the prefix, thus eliminating the repetitive (s + sizeof(PFX) - 1)
> patterns.
>
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
> tools/testing/selftests/bpf/test_loader.c | 41 ++++++++++++++-----------------
> 1 file changed, 18 insertions(+), 23 deletions(-)
>
> diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
> index 338c035c36884c02ac57cd17110122c10b4858e4..f1eb99f829e112f0e738bfe44e7e61a49120ec85 100644
> --- a/tools/testing/selftests/bpf/test_loader.c
> +++ b/tools/testing/selftests/bpf/test_loader.c
> @@ -11,8 +11,12 @@
> #include "cap_helpers.h"
> #include "jit_disasm_helpers.h"
>
> -#define str_has_pfx(str, pfx) \
> - (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
> +static inline const char *str_has_pfx(const char *str, const char *pfx)
> +{
> + size_t len = strlen(pfx);
> +
> + return strncmp(str, pfx, len) == 0 ? str + len : NULL;
> +}
>
> #define TEST_LOADER_LOG_BUF_SZ 2097152
>
> @@ -452,8 +456,8 @@ static int parse_test_spec(struct test_loader *tester,
> continue;
>
> s = btf__str_by_offset(btf, t->name_off);
> - if (str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX)) {
> - description = s + sizeof(TEST_TAG_DESCRIPTION_PFX) - 1;
> + if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) {
Personally, I don't like assignments in the if expressions.
For example, this is annoying when you are stepping in the debugger.
But who does that in 2026, right?..
Acked-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> + description = val;
> } else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) {
> spec->priv.expect_failure = true;
> spec->mode_mask |= PRIV;
> @@ -530,29 +534,24 @@ static int parse_test_spec(struct test_loader *tester,
> if (err)
> goto cleanup;
> spec->mode_mask |= UNPRIV;
> - } else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX)) {
> - val = s + sizeof(TEST_TAG_RETVAL_PFX) - 1;
> + } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX))) {
> err = parse_retval(val, &spec->priv.retval, "__retval");
> if (err)
> goto cleanup;
> spec->priv.execute = true;
> spec->mode_mask |= PRIV;
> - } else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV)) {
> - val = s + sizeof(TEST_TAG_RETVAL_PFX_UNPRIV) - 1;
> + } else if ((val = str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV))) {
> err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv");
> if (err)
> goto cleanup;
> spec->mode_mask |= UNPRIV;
> spec->unpriv.execute = true;
> has_unpriv_retval = true;
> - } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) {
> - val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1;
> + } else if ((val = str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX))) {
> err = parse_int(val, &spec->log_level, "test log level");
> if (err)
> goto cleanup;
> - } else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) {
> - val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1;
> -
> + } else if ((val = str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX))) {
> clear = val[0] == '!';
> if (clear)
> val++;
> @@ -577,8 +576,7 @@ static int parse_test_spec(struct test_loader *tester,
> goto cleanup;
> update_flags(&spec->prog_flags, flags, clear);
> }
> - } else if (str_has_pfx(s, TEST_TAG_ARCH)) {
> - val = s + sizeof(TEST_TAG_ARCH) - 1;
> + } else if ((val = str_has_pfx(s, TEST_TAG_ARCH))) {
> if (strcmp(val, "X86_64") == 0) {
> arch = ARCH_X86_64;
> } else if (strcmp(val, "ARM64") == 0) {
> @@ -596,16 +594,14 @@ static int parse_test_spec(struct test_loader *tester,
> collect_jit = get_current_arch() == arch;
> unpriv_jit_on_next_line = true;
> jit_on_next_line = true;
> - } else if (str_has_pfx(s, TEST_BTF_PATH)) {
> - spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
> - } else if (str_has_pfx(s, TEST_TAG_CAPS_UNPRIV)) {
> - val = s + sizeof(TEST_TAG_CAPS_UNPRIV) - 1;
> + } else if ((val = str_has_pfx(s, TEST_BTF_PATH))) {
> + spec->btf_custom_path = val;
> + } else if ((val = str_has_pfx(s, TEST_TAG_CAPS_UNPRIV))) {
> err = parse_caps(val, &spec->unpriv.caps, "test caps");
> if (err)
> goto cleanup;
> spec->mode_mask |= UNPRIV;
> - } else if (str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX)) {
> - val = s + sizeof(TEST_TAG_LOAD_MODE_PFX) - 1;
> + } else if ((val = str_has_pfx(s, TEST_TAG_LOAD_MODE_PFX))) {
> if (strcmp(val, "jited") == 0) {
> load_mask = JITED;
> } else if (strcmp(val, "no_jited") == 0) {
> @@ -635,12 +631,11 @@ static int parse_test_spec(struct test_loader *tester,
> &spec->unpriv.stdout);
> if (err)
> goto cleanup;
> - } else if (str_has_pfx(s, TEST_TAG_LINEAR_SIZE)) {
> + } else if ((val = str_has_pfx(s, TEST_TAG_LINEAR_SIZE))) {
> switch (bpf_program__type(prog)) {
> case BPF_PROG_TYPE_SCHED_ACT:
> case BPF_PROG_TYPE_SCHED_CLS:
> case BPF_PROG_TYPE_CGROUP_SKB:
> - val = s + sizeof(TEST_TAG_LINEAR_SIZE) - 1;
> err = parse_int(val, &spec->linear_sz, "test linear size");
> if (err)
> goto cleanup;
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix
2026-03-27 21:54 ` Ihor Solodrai
@ 2026-03-27 21:57 ` Eduard Zingerman
0 siblings, 0 replies; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-27 21:57 UTC (permalink / raw)
To: Ihor Solodrai, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On Fri, 2026-03-27 at 14:54 -0700, Ihor Solodrai wrote:
[...]
> > @@ -452,8 +456,8 @@ static int parse_test_spec(struct test_loader *tester,
> > continue;
> >
> > s = btf__str_by_offset(btf, t->name_off);
> > - if (str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX)) {
> > - description = s + sizeof(TEST_TAG_DESCRIPTION_PFX) - 1;
> > + if ((val = str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX))) {
>
> Personally, I don't like assignments in the if expressions.
Assignments in if-expressions are A-W-E-S-O-M-E. Long live walrus operator.
> For example, this is annoying when you are stepping in the debugger.
> But who does that in 2026, right?..
What's debugger?
> Acked-by: Ihor Solodrai <ihor.solodrai@linux.dev>
Thx.
[...]
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-26 17:39 ` [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
2026-03-26 22:47 ` Andrii Nakryiko
@ 2026-03-27 22:00 ` Ihor Solodrai
2026-03-27 22:07 ` Eduard Zingerman
1 sibling, 1 reply; 16+ messages in thread
From: Ihor Solodrai @ 2026-03-27 22:00 UTC (permalink / raw)
To: Eduard Zingerman, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On 3/26/26 10:39 AM, Eduard Zingerman wrote:
> Impose global ordering for all decl tags used by test_loader.c based
> tests (__success, __failure, __msg, etc):
> - change every tag to expand as
> __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...)))
> - change parse_test_spec() to collect all decl tags before
> processing and sort them using strverscmp().
>
> The ordering is necessary for gcc-bpf.
> Neither GCC nor the C standard defines the order in which function
> attributes are consumed. While Clang tends to preserve definition order,
> GCC may process them out of sequence. This inconsistency causes BPF
> tests with multiple __msg entries to fail when compiled with GCC.
>
> Co-developed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> ---
> tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++-----
> tools/testing/selftests/bpf/test_loader.c | 174 +++++++++++++++------------
> 2 files changed, 130 insertions(+), 104 deletions(-)
The change makes sense to me overall.
I am thinking with all this non-trivial parsing and sorting, maybe we
should have some smoke test for the test_loader itself? Extend
prog_tests_framework.c maybe? And then make that run first on CI?
wdyt?
>
> [...]
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c
2026-03-26 17:39 ` [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c Eduard Zingerman
@ 2026-03-27 22:04 ` Ihor Solodrai
0 siblings, 0 replies; 16+ messages in thread
From: Ihor Solodrai @ 2026-03-27 22:04 UTC (permalink / raw)
To: Eduard Zingerman, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On 3/26/26 10:39 AM, Eduard Zingerman wrote:
> After str_has_pfx() refactoring each TEST_TAG_* / TEST_BTF_PATH
> constant is used exactly once. Since constant definitions are not
> shared between BPF-side bpf_misc.h and userspace side test_loader.c,
> there is no need in the additional redirection layer.
>
> Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
> tools/testing/selftests/bpf/test_loader.c | 84 +++++++++++--------------------
> 1 file changed, 28 insertions(+), 56 deletions(-)
>
> [...]
>
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags
2026-03-27 22:00 ` Ihor Solodrai
@ 2026-03-27 22:07 ` Eduard Zingerman
0 siblings, 0 replies; 16+ messages in thread
From: Eduard Zingerman @ 2026-03-27 22:07 UTC (permalink / raw)
To: Ihor Solodrai, bpf, ast, andrii
Cc: daniel, martin.lau, kernel-team, yonghong.song, cupertino.miranda
On Fri, 2026-03-27 at 15:00 -0700, Ihor Solodrai wrote:
> On 3/26/26 10:39 AM, Eduard Zingerman wrote:
> > Impose global ordering for all decl tags used by test_loader.c based
> > tests (__success, __failure, __msg, etc):
> > - change every tag to expand as
> > __attribute__((btf_decl_tag("comment:" XSTR(__COUNTER__) ...)))
> > - change parse_test_spec() to collect all decl tags before
> > processing and sort them using strverscmp().
> >
> > The ordering is necessary for gcc-bpf.
> > Neither GCC nor the C standard defines the order in which function
> > attributes are consumed. While Clang tends to preserve definition order,
> > GCC may process them out of sequence. This inconsistency causes BPF
> > tests with multiple __msg entries to fail when compiled with GCC.
> >
> > Co-developed-by: Cupertino Miranda <cupertino.miranda@oracle.com>
> > Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
> > ---
> > tools/testing/selftests/bpf/progs/bpf_misc.h | 60 ++++-----
> > tools/testing/selftests/bpf/test_loader.c | 174 +++++++++++++++------------
> > 2 files changed, 130 insertions(+), 104 deletions(-)
>
> The change makes sense to me overall.
>
> I am thinking with all this non-trivial parsing and sorting, maybe we
> should have some smoke test for the test_loader itself? Extend
> prog_tests_framework.c maybe? And then make that run first on CI?
>
> wdyt?
So far the only part of the test_loader.c that has selftest is
prog_tests_framework.c:test_prog_tests_framework_expected_msgs.
Which checks __msg_not() processing, as it turned out a bit tricky.
Specifically for the ordering, I don't think any smoke tests
are necessary, if order is wrong lots of selftests will fail.
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2026-03-27 22:07 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-26 17:39 [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
2026-03-26 17:39 ` [PATCH 1/4] selftests/bpf: fix __jited_unpriv tag name Eduard Zingerman
2026-03-27 21:52 ` Ihor Solodrai
2026-03-26 17:39 ` [PATCH 2/4] selftests/bpf: make str_has_pfx return pointer past the prefix Eduard Zingerman
2026-03-27 21:54 ` Ihor Solodrai
2026-03-27 21:57 ` Eduard Zingerman
2026-03-26 17:39 ` [PATCH 3/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
2026-03-26 22:47 ` Andrii Nakryiko
2026-03-26 22:51 ` Eduard Zingerman
2026-03-26 23:00 ` Andrii Nakryiko
2026-03-26 23:30 ` Eduard Zingerman
2026-03-27 22:00 ` Ihor Solodrai
2026-03-27 22:07 ` Eduard Zingerman
2026-03-26 17:39 ` [PATCH 4/4] selftests/bpf: inline TEST_TAG constants in test_loader.c Eduard Zingerman
2026-03-27 22:04 ` Ihor Solodrai
2026-03-26 22:28 ` [PATCH 0/4] selftests/bpf: impose global ordering for test decl_tags Eduard Zingerman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox