Currently testing of userspace and in-kernel API use two different frameworks. kselftests for the userspace ones and Kunit for the in-kernel ones. Besides their different scopes, both have different strengths and limitations:
Kunit: * Tests are normal kernel code. * They use the regular kernel toolchain. * They can be packaged and distributed as modules conveniently.
Kselftests: * Tests are normal userspace code * They need a userspace toolchain. A kernel cross toolchain is likely not enough. * A fair amout of userland is required to run the tests, which means a full distro or handcrafted rootfs. * There is no way to conveniently package and run kselftests with a given kernel image. * The kselftests makefiles are not as powerful as regular kbuild. For example they are missing proper header dependency tracking or more complex compiler option modifications.
Therefore kunit is much easier to run against different kernel configurations and architectures. This series aims to combine kselftests and kunit, avoiding both their limitations. It works by compiling the userspace kselftests as part of the regular kernel build, embedding them into the kunit kernel or module and executing them from there. If the kernel toolchain is not fit to produce userspace because of a missing libc, the kernel's own nolibc can be used instead. The structured TAP output from the kselftest is integrated into the kunit KTAP output transparently, the kunit parser can parse the combined logs together.
Further room for improvements: * Call each test in its completely dedicated namespace * Handle additional test files besides the test executable through archives. CPIO, cramfs, etc. * Compatibility with kselftest_harness.h (in progress) * Expose the blobs in debugfs * Provide some convience wrappers around compat userprogs * Figure out a migration path/coexistence solution for kunit UAPI and tools/testing/selftests/
Output from the kunit example testcase, note the output of "example_uapi_tests".
$ ./tools/testing/kunit/kunit.py run --kunitconfig lib/kunit example ... Running tests with: $ .kunit/linux kunit.filter_glob=example kunit.enable=1 mem=1G console=tty kunit_shutdown=halt [11:53:53] ================== example (10 subtests) =================== [11:53:53] [PASSED] example_simple_test [11:53:53] [SKIPPED] example_skip_test [11:53:53] [SKIPPED] example_mark_skipped_test [11:53:53] [PASSED] example_all_expect_macros_test [11:53:53] [PASSED] example_static_stub_test [11:53:53] [PASSED] example_static_stub_using_fn_ptr_test [11:53:53] [PASSED] example_priv_test [11:53:53] =================== example_params_test =================== [11:53:53] [SKIPPED] example value 3 [11:53:53] [PASSED] example value 2 [11:53:53] [PASSED] example value 1 [11:53:53] [SKIPPED] example value 0 [11:53:53] =============== [PASSED] example_params_test =============== [11:53:53] [PASSED] example_slow_test [11:53:53] ======================= (4 subtests) ======================= [11:53:53] [PASSED] procfs [11:53:53] [PASSED] userspace test 2 [11:53:53] [SKIPPED] userspace test 3: some reason [11:53:53] [PASSED] userspace test 4 [11:53:53] ================ [PASSED] example_uapi_test ================ [11:53:53] ===================== [PASSED] example ===================== [11:53:53] ============================================================ [11:53:53] Testing complete. Ran 16 tests: passed: 11, skipped: 5 [11:53:53] Elapsed time: 67.543s total, 1.823s configuring, 65.655s building, 0.058s running
Based on v6.15-rc1.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Changes in v3: - Reintroduce CONFIG_CC_CAN_LINK_STATIC - Enable CONFIG_ARCH_HAS_NOLIBC for m68k and SPARC - Properly handle 'clean' target for userprogs - Use ramfs over tmpfs to reduce dependencies - Inherit userprogs byte order and ABI from kernel - Drop now unnecessary "#ifndef NOLIBC" - Pick up review tags - Drop usage of __private in blob.h, sparse complains and it is not really necessary - Fix execution on loongarch when using clang - Drop userprogs libgcc handling, it was ugly and is not yet necessary - Link to v2: https://lore.kernel.org/r/20250407-kunit-kselftests-v2-0-454114e287fd@linutr...
Changes in v2: - Rebase onto v6.15-rc1 - Add documentation and kernel docs - Resolve invalid kconfig breakages - Drop already applied patch "kbuild: implement CONFIG_HEADERS_INSTALL for Usermode Linux" - Drop userprogs CONFIG_WERROR integration, it doesn't need to be part of this series - Replace patch prefix "kconfig" with "kbuild" - Rename kunit_uapi_run_executable() to kunit_uapi_run_kselftest() - Generate private, conflict-free symbols in the blob framework - Handle kselftest exit codes - Handle SIGABRT - Forward output also to kunit debugfs log - Install a fd=0 stdin filedescriptor - Link to v1: https://lore.kernel.org/r/20250217-kunit-kselftests-v1-0-42b4524c3b0a@linutr...
--- Thomas Weißschuh (16): kbuild: userprogs: avoid duplicating of flags inherited from kernel kbuild: userprogs: also inherit byte order and ABI from kernel init: re-add CONFIG_CC_CAN_LINK_STATIC kbuild: userprogs: add nolibc support kbuild: introduce CONFIG_ARCH_HAS_NOLIBC kbuild: doc: add label for userprogs section kbuild: introduce blob framework kunit: tool: Add test for nested test result reporting kunit: tool: Don't overwrite test status based on subtest counts kunit: tool: Parse skipped tests from kselftest.h kunit: Always descend into kunit directory during build kunit: qemu_configs: loongarch: Enable LSX/LSAX kunit: Introduce UAPI testing framework kunit: uapi: Add example for UAPI tests kunit: uapi: Introduce preinit executable kunit: uapi: Validate usability of /proc
Documentation/dev-tools/kunit/api/index.rst | 5 + Documentation/dev-tools/kunit/api/uapi.rst | 12 + Documentation/kbuild/makefiles.rst | 38 ++- MAINTAINERS | 2 + Makefile | 7 +- include/kunit/uapi.h | 24 ++ include/linux/blob.h | 31 +++ init/Kconfig | 7 + lib/Makefile | 4 - lib/kunit/Kconfig | 10 + lib/kunit/Makefile | 20 +- lib/kunit/kunit-example-test.c | 15 ++ lib/kunit/kunit-example-uapi.c | 54 ++++ lib/kunit/uapi-preinit.c | 63 +++++ lib/kunit/uapi.c | 294 +++++++++++++++++++++ scripts/Makefile.blobs | 19 ++ scripts/Makefile.build | 6 + scripts/Makefile.clean | 2 +- scripts/Makefile.userprogs | 13 +- scripts/blob-wrap.c | 27 ++ tools/include/nolibc/Kconfig.nolibc | 15 ++ tools/testing/kunit/kunit_parser.py | 13 +- tools/testing/kunit/kunit_tool_test.py | 9 + tools/testing/kunit/qemu_configs/loongarch.py | 2 + .../test_is_test_passed-failure-nested.log | 10 + .../test_data/test_is_test_passed-kselftest.log | 3 +- 26 files changed, 686 insertions(+), 19 deletions(-) --- base-commit: f07a3558c4a5d76f3fea004075e5151c4516d055 change-id: 20241015-kunit-kselftests-56273bc40442
Best regards,
The duplication makes maintenance harder. Changes need to be done in two places and the lines will grow overly long.
Use an intermediary variable instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index 35e6e5240c61a8a329011929fcd0352b881dccdc..c4293cf91e968ca8ee64452841fb266e24df63f6 100644 --- a/Makefile +++ b/Makefile @@ -1130,8 +1130,9 @@ LDFLAGS_vmlinux += --emit-relocs --discard-none endif
# Align the bit size of userspace programs with the kernel -KBUILD_USERCFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) -KBUILD_USERLDFLAGS += $(filter -m32 -m64 --target=%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) +USERFLAGS_FROM_KERNEL := -m32 -m64 --target=% +KBUILD_USERCFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) +KBUILD_USERLDFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
# userspace programs are linked via the compiler, use the correct linker ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_LD_IS_LLD),yy)
On Wed, Jun 11, 2025 at 09:38:07AM +0200, Thomas Weißschuh wrote:
The duplication makes maintenance harder. Changes need to be done in two places and the lines will grow overly long.
Use an intermediary variable instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
Reviewed-by: Nicolas Schier n.schier@avm.de
Make sure the byte order and ABI of the userprogs matches the one of the kernel, similar to how the bit size is handled. Otherwise the userprogs may not be executable. This happens for example on powerpc little endian, or riscv32.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index c4293cf91e968ca8ee64452841fb266e24df63f6..b9aa1058321dabd3b3dd5610e45a2807dfa257f4 100644 --- a/Makefile +++ b/Makefile @@ -1129,8 +1129,8 @@ ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),) LDFLAGS_vmlinux += --emit-relocs --discard-none endif
-# Align the bit size of userspace programs with the kernel -USERFLAGS_FROM_KERNEL := -m32 -m64 --target=% +# Align the bit size, byte order and architecture of userspace programs with the kernel +USERFLAGS_FROM_KERNEL := -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=% KBUILD_USERCFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERLDFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
On Wed, Jun 11, 2025 at 09:38:08AM +0200, Thomas Weißschuh wrote:
Make sure the byte order and ABI of the userprogs matches the one of the kernel, similar to how the bit size is handled. Otherwise the userprogs may not be executable. This happens for example on powerpc little endian, or riscv32.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
Reviewed-by: Nicolas Schier n.schier@avm.de
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Make sure the byte order and ABI of the userprogs matches the one of the kernel, similar to how the bit size is handled. Otherwise the userprogs may not be executable. This happens for example on powerpc little endian, or riscv32.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index c4293cf91e968ca8ee64452841fb266e24df63f6..b9aa1058321dabd3b3dd5610e45a2807dfa257f4 100644 --- a/Makefile +++ b/Makefile @@ -1129,8 +1129,8 @@ ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),) LDFLAGS_vmlinux += --emit-relocs --discard-none endif
-# Align the bit size of userspace programs with the kernel -USERFLAGS_FROM_KERNEL := -m32 -m64 --target=% +# Align the bit size, byte order and architecture of userspace programs with the kernel +USERFLAGS_FROM_KERNEL := -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=% KBUILD_USERCFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERLDFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
Why didn't you do like this?
USERFLAGS_FROM_KERNEL := $(filter -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERCFLAGS += $(USERFLAGS_FROM_KERNEL) KBUILD_USERLDFLAGS += $(USERFLAGS_FROM_KERNEL)
-- Best Regards Masahiro Yamada
On Mon, Jun 16, 2025 at 11:49:41PM +0900, Masahiro Yamada wrote:
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Make sure the byte order and ABI of the userprogs matches the one of the kernel, similar to how the bit size is handled. Otherwise the userprogs may not be executable. This happens for example on powerpc little endian, or riscv32.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index c4293cf91e968ca8ee64452841fb266e24df63f6..b9aa1058321dabd3b3dd5610e45a2807dfa257f4 100644 --- a/Makefile +++ b/Makefile @@ -1129,8 +1129,8 @@ ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),) LDFLAGS_vmlinux += --emit-relocs --discard-none endif
-# Align the bit size of userspace programs with the kernel -USERFLAGS_FROM_KERNEL := -m32 -m64 --target=% +# Align the bit size, byte order and architecture of userspace programs with the kernel +USERFLAGS_FROM_KERNEL := -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=% KBUILD_USERCFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERLDFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
Why didn't you do like this?
USERFLAGS_FROM_KERNEL := $(filter -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERCFLAGS += $(USERFLAGS_FROM_KERNEL) KBUILD_USERLDFLAGS += $(USERFLAGS_FROM_KERNEL)
The idea was to keep the USERFLAGS_FROM_KERNEL line free of clutter, as it is probably going to change more often. To improve reviewability and gain some available horizontal space in case it should be needed at some point.
If you prefer the other layout I'll be happy to switch it around.
Thomas
On Tue, Jun 17, 2025 at 4:39 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
On Mon, Jun 16, 2025 at 11:49:41PM +0900, Masahiro Yamada wrote:
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Make sure the byte order and ABI of the userprogs matches the one of the kernel, similar to how the bit size is handled. Otherwise the userprogs may not be executable. This happens for example on powerpc little endian, or riscv32.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile index c4293cf91e968ca8ee64452841fb266e24df63f6..b9aa1058321dabd3b3dd5610e45a2807dfa257f4 100644 --- a/Makefile +++ b/Makefile @@ -1129,8 +1129,8 @@ ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),) LDFLAGS_vmlinux += --emit-relocs --discard-none endif
-# Align the bit size of userspace programs with the kernel -USERFLAGS_FROM_KERNEL := -m32 -m64 --target=% +# Align the bit size, byte order and architecture of userspace programs with the kernel +USERFLAGS_FROM_KERNEL := -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=% KBUILD_USERCFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERLDFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
Why didn't you do like this?
USERFLAGS_FROM_KERNEL := $(filter -m32 -m64 -mlittle-endian -mbig-endian --target=% -march=% -mabi=%, $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)) KBUILD_USERCFLAGS += $(USERFLAGS_FROM_KERNEL) KBUILD_USERLDFLAGS += $(USERFLAGS_FROM_KERNEL)
The idea was to keep the USERFLAGS_FROM_KERNEL line free of clutter, as it is probably going to change more often. To improve reviewability and gain some available horizontal space in case it should be needed at some point.
If you prefer the other layout I'll be happy to switch it around.
$(filter ... $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
is duplicated in the two lines.
That is the reason for my suggestion.
For the kunit UAPI functionality this feature is needed.
This reverts commit d1b99cdf22e0 ("init: remove unused CONFIG_CC_CAN_LINK_STATIC")
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- init/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/init/Kconfig b/init/Kconfig index af4c2f0854554bbcdf193852cf5c1d2c2accc64f..26cafbad4f1560fb56b4bef31ae29baf54175661 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -87,6 +87,11 @@ config CC_CAN_LINK default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m64-flag)) if 64BIT default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m32-flag))
+config CC_CAN_LINK_STATIC + bool + default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m64-flag) -static) if 64BIT + default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m32-flag) -static) + # Fixed in GCC 14, 13.3, 12.4 and 11.5 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921 config GCC_ASM_GOTO_OUTPUT_BROKEN
On Wed, Jun 11, 2025 at 09:38:09AM +0200, Thomas Weißschuh wrote:
For the kunit UAPI functionality this feature is needed.
This reverts commit d1b99cdf22e0 ("init: remove unused CONFIG_CC_CAN_LINK_STATIC")
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
init/Kconfig | 5 +++++ 1 file changed, 5 insertions(+)
Acked-by: Nicolas Schier n.schier@avm.de
Userprogs are built with the regular kernel compiler $CC. A kernel compiler does not necessarily contain a libc which is required for a normal userspace application. However the kernel tree does contain a minimal libc implementation "nolibc" which can be used to build userspace applications.
Introduce support to build userprogs against nolibc instead of the default libc of the compiler, which may not exist.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
--- This could probably be moved out of the generic kbuild makefiles. I think the ergonimics would suffer and this functionality could be used by other users of userprogs.
Also this does currently not support out-of-tree builds. For that tools/include/nolibc/*.h and usr/include/*.h would need to be installed into the build directory. --- Documentation/kbuild/makefiles.rst | 13 +++++++++++++ scripts/Makefile.userprogs | 13 ++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 8aef3650c1f32b6b197e0dc777e26775d371a081..4cc7a1b89f1803857a4723284613111e9ad71d92 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -974,6 +974,19 @@ When linking bpfilter_umh, it will be passed the extra option -static.
From command line, :ref:`USERCFLAGS and USERLDFLAGS <userkbuildflags>` will also be used.
+Building userprogs against nolibc +--------------------------------- + +Not all kernel toolchains provide a libc. +Simple userprogs can be built against a very simple libc call "nolibc" provided +by the kernel source tree. +This requires ``CONFIG_HEADERS_INSTALL=y``. + +Example:: + + # lib/kunit/Makefile + uapi-preinit-nolibc := $(CONFIG_ARCH_HAS_NOLIBC) + When userspace programs are actually built ------------------------------------------
diff --git a/scripts/Makefile.userprogs b/scripts/Makefile.userprogs index f3a7e1ef3753b54303718fae97f4b3c9d4eac07c..b1633a9de6c86a023c70a717bac0b80b89d01431 100644 --- a/scripts/Makefile.userprogs +++ b/scripts/Makefile.userprogs @@ -16,10 +16,17 @@ user-csingle := $(addprefix $(obj)/, $(user-csingle)) user-cmulti := $(addprefix $(obj)/, $(user-cmulti)) user-cobjs := $(addprefix $(obj)/, $(user-cobjs))
+user_nolibc_ccflags := -nostdlib -nostdinc -static -fno-ident -fno-asynchronous-unwind-tables \ + -ffreestanding -fno-stack-protector \ + -isystem $(objtree)/usr/include -include $(srctree)/tools/include/nolibc/nolibc.h -isystem $(srctree)/tools/include/nolibc/ +user_nolibc_ldflags := -nostdlib -nostdinc -static + user_ccflags = -Wp,-MMD,$(depfile) $(KBUILD_USERCFLAGS) $(userccflags) \ - $($(target-stem)-userccflags) -user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) -user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs) + $($(target-stem)-userccflags) $(if $($(target-stem)-nolibc),$(user_nolibc_ccflags)) +user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) \ + $(if $($(target-stem)-nolibc),$(user_nolibc_ldflags)) +user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs) \ + $(if $($(target-stem)-nolibc),$(user_nolibc_ldlibs))
# Create an executable from a single .c file quiet_cmd_user_cc_c = CC [U] $@
On Wed, Jun 11, 2025 at 09:38:10AM +0200, Thomas Weißschuh wrote:
Userprogs are built with the regular kernel compiler $CC. A kernel compiler does not necessarily contain a libc which is required for a normal userspace application. However the kernel tree does contain a minimal libc implementation "nolibc" which can be used to build userspace applications.
Introduce support to build userprogs against nolibc instead of the default libc of the compiler, which may not exist.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This could probably be moved out of the generic kbuild makefiles. I think the ergonimics would suffer and this functionality could be used by other users of userprogs.
Also this does currently not support out-of-tree builds. For that tools/include/nolibc/*.h and usr/include/*.h would need to be installed into the build directory.
Thanks!
Reviewed-by: Nicolas Schier n.schier@avm.de
Probably overkill, but might it make sense to abort *-nolibc compilation requests for out-of-tree builds?
Kind regards, Nicolas
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Userprogs are built with the regular kernel compiler $CC. A kernel compiler does not necessarily contain a libc which is required for a normal userspace application. However the kernel tree does contain a minimal libc implementation "nolibc" which can be used to build userspace applications.
Introduce support to build userprogs against nolibc instead of the default libc of the compiler, which may not exist.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This could probably be moved out of the generic kbuild makefiles. I think the ergonimics would suffer and this functionality could be used by other users of userprogs.
Also this does currently not support out-of-tree builds. For that tools/include/nolibc/*.h and usr/include/*.h would need to be installed into the build directory.
Documentation/kbuild/makefiles.rst | 13 +++++++++++++ scripts/Makefile.userprogs | 13 ++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 8aef3650c1f32b6b197e0dc777e26775d371a081..4cc7a1b89f1803857a4723284613111e9ad71d92 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -974,6 +974,19 @@ When linking bpfilter_umh, it will be passed the extra option -static.
From command line, :ref:`USERCFLAGS and USERLDFLAGS <userkbuildflags>` will also be used.
+Building userprogs against nolibc +---------------------------------
+Not all kernel toolchains provide a libc. +Simple userprogs can be built against a very simple libc call "nolibc" provided +by the kernel source tree. +This requires ``CONFIG_HEADERS_INSTALL=y``.
+Example::
- # lib/kunit/Makefile
- uapi-preinit-nolibc := $(CONFIG_ARCH_HAS_NOLIBC)
When userspace programs are actually built
diff --git a/scripts/Makefile.userprogs b/scripts/Makefile.userprogs index f3a7e1ef3753b54303718fae97f4b3c9d4eac07c..b1633a9de6c86a023c70a717bac0b80b89d01431 100644 --- a/scripts/Makefile.userprogs +++ b/scripts/Makefile.userprogs @@ -16,10 +16,17 @@ user-csingle := $(addprefix $(obj)/, $(user-csingle)) user-cmulti := $(addprefix $(obj)/, $(user-cmulti)) user-cobjs := $(addprefix $(obj)/, $(user-cobjs))
+user_nolibc_ccflags := -nostdlib -nostdinc -static -fno-ident -fno-asynchronous-unwind-tables \
-ffreestanding -fno-stack-protector \
-isystem $(objtree)/usr/include -include $(srctree)/tools/include/nolibc/nolibc.h -isystem $(srctree)/tools/include/nolibc/
The tools/ directory is a different world, and Kbuild scripts do not know anything about it.
And, you do not need to implement this in scripts/Makefile.userprogs because you can move this to lib/kunit/Makefile.kunit-uapi or somewhere.
+user_nolibc_ldflags := -nostdlib -nostdinc -static
user_ccflags = -Wp,-MMD,$(depfile) $(KBUILD_USERCFLAGS) $(userccflags) \
$($(target-stem)-userccflags)
-user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) -user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs)
$($(target-stem)-userccflags) $(if $($(target-stem)-nolibc),$(user_nolibc_ccflags))
+user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) \
$(if $($(target-stem)-nolibc),$(user_nolibc_ldflags))
+user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs) \
$(if $($(target-stem)-nolibc),$(user_nolibc_ldlibs))
# Create an executable from a single .c file quiet_cmd_user_cc_c = CC [U] $@
-- 2.49.0
-- Best Regards Masahiro Yamada
On Tue, Jun 17, 2025 at 12:35:07AM +0900, Masahiro Yamada wrote:
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Userprogs are built with the regular kernel compiler $CC. A kernel compiler does not necessarily contain a libc which is required for a normal userspace application. However the kernel tree does contain a minimal libc implementation "nolibc" which can be used to build userspace applications.
Introduce support to build userprogs against nolibc instead of the default libc of the compiler, which may not exist.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This could probably be moved out of the generic kbuild makefiles. I think the ergonimics would suffer and this functionality could be used by other users of userprogs.
Also this does currently not support out-of-tree builds. For that tools/include/nolibc/*.h and usr/include/*.h would need to be installed into the build directory.
<snip>
--- a/scripts/Makefile.userprogs +++ b/scripts/Makefile.userprogs @@ -16,10 +16,17 @@ user-csingle := $(addprefix $(obj)/, $(user-csingle)) user-cmulti := $(addprefix $(obj)/, $(user-cmulti)) user-cobjs := $(addprefix $(obj)/, $(user-cobjs))
+user_nolibc_ccflags := -nostdlib -nostdinc -static -fno-ident -fno-asynchronous-unwind-tables \
-ffreestanding -fno-stack-protector \
-isystem $(objtree)/usr/include -include $(srctree)/tools/include/nolibc/nolibc.h -isystem $(srctree)/tools/include/nolibc/
The tools/ directory is a different world, and Kbuild scripts do not know anything about it.
Ack.
How does this statement affect the next patch which creates tools/include/nolibc/Kconfig.nolibc ? Is it fine to create the Kconfig file in tools/ or should I move it? I do want to maintain this file as part of nolibc and not KUnit. The possibilities I see are init/Kconfig.nolibc or lib/Kconfig.nolibc.
And, you do not need to implement this in scripts/Makefile.userprogs because you can move this to lib/kunit/Makefile.kunit-uapi or somewhere.
Understood. This is not unexpected, as hinted in the commit message.
+user_nolibc_ldflags := -nostdlib -nostdinc -static
user_ccflags = -Wp,-MMD,$(depfile) $(KBUILD_USERCFLAGS) $(userccflags) \
$($(target-stem)-userccflags)
-user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) -user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs)
$($(target-stem)-userccflags) $(if $($(target-stem)-nolibc),$(user_nolibc_ccflags))
+user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) \
$(if $($(target-stem)-nolibc),$(user_nolibc_ldflags))
+user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs) \
$(if $($(target-stem)-nolibc),$(user_nolibc_ldlibs))
# Create an executable from a single .c file quiet_cmd_user_cc_c = CC [U] $@
Thomas
On Tue, Jun 17, 2025 at 4:59 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
On Tue, Jun 17, 2025 at 12:35:07AM +0900, Masahiro Yamada wrote:
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Userprogs are built with the regular kernel compiler $CC. A kernel compiler does not necessarily contain a libc which is required for a normal userspace application. However the kernel tree does contain a minimal libc implementation "nolibc" which can be used to build userspace applications.
Introduce support to build userprogs against nolibc instead of the default libc of the compiler, which may not exist.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This could probably be moved out of the generic kbuild makefiles. I think the ergonimics would suffer and this functionality could be used by other users of userprogs.
Also this does currently not support out-of-tree builds. For that tools/include/nolibc/*.h and usr/include/*.h would need to be installed into the build directory.
<snip>
--- a/scripts/Makefile.userprogs +++ b/scripts/Makefile.userprogs @@ -16,10 +16,17 @@ user-csingle := $(addprefix $(obj)/, $(user-csingle)) user-cmulti := $(addprefix $(obj)/, $(user-cmulti)) user-cobjs := $(addprefix $(obj)/, $(user-cobjs))
+user_nolibc_ccflags := -nostdlib -nostdinc -static -fno-ident -fno-asynchronous-unwind-tables \
-ffreestanding -fno-stack-protector \
-isystem $(objtree)/usr/include -include $(srctree)/tools/include/nolibc/nolibc.h -isystem $(srctree)/tools/include/nolibc/
The tools/ directory is a different world, and Kbuild scripts do not know anything about it.
Ack.
How does this statement affect the next patch which creates tools/include/nolibc/Kconfig.nolibc ? Is it fine to create the Kconfig file in tools/ or should I move it? I do want to maintain this file as part of nolibc and not KUnit. The possibilities I see are init/Kconfig.nolibc or lib/Kconfig.nolibc.
Personally I like init/Kconfig.nolibc. (I am even fine with adding this new entry to init/Kconfig) But, this file is not what I maintain. It is up to you after all.
And, you do not need to implement this in scripts/Makefile.userprogs because you can move this to lib/kunit/Makefile.kunit-uapi or somewhere.
Understood. This is not unexpected, as hinted in the commit message.
One existing example is lib/vdso/Makefile.include, which is included from multiple Makefiles.
+user_nolibc_ldflags := -nostdlib -nostdinc -static
user_ccflags = -Wp,-MMD,$(depfile) $(KBUILD_USERCFLAGS) $(userccflags) \
$($(target-stem)-userccflags)
-user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) -user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs)
$($(target-stem)-userccflags) $(if $($(target-stem)-nolibc),$(user_nolibc_ccflags))
+user_ldflags = $(KBUILD_USERLDFLAGS) $(userldflags) $($(target-stem)-userldflags) \
$(if $($(target-stem)-nolibc),$(user_nolibc_ldflags))
+user_ldlibs = $(userldlibs) $($(target-stem)-userldlibs) \
$(if $($(target-stem)-nolibc),$(user_nolibc_ldlibs))
# Create an executable from a single .c file quiet_cmd_user_cc_c = CC [U] $@
Thomas
-- Best Regards Masahiro Yamada
Nolibc does not support all architectures. Add a kconfig option, so users can know where it is available.
The new option is maintained inside tools/include/nolibc/ as only that directory is responsible for nolibc's availability.
Reviewed-by: Nicolas Schier n.schier@avm.de Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- init/Kconfig | 2 ++ tools/include/nolibc/Kconfig.nolibc | 15 +++++++++++++++ 2 files changed, 17 insertions(+)
diff --git a/init/Kconfig b/init/Kconfig index 26cafbad4f1560fb56b4bef31ae29baf54175661..6d10f2816e453d1d28e4b8249cbbd95c28baa0f1 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -92,6 +92,8 @@ config CC_CAN_LINK_STATIC default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m64-flag) -static) if 64BIT default $(success,$(srctree)/scripts/cc-can-link.sh $(CC) $(CLANG_FLAGS) $(USERCFLAGS) $(USERLDFLAGS) $(m32-flag) -static)
+source "tools/include/nolibc/Kconfig.nolibc" + # Fixed in GCC 14, 13.3, 12.4 and 11.5 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921 config GCC_ASM_GOTO_OUTPUT_BROKEN diff --git a/tools/include/nolibc/Kconfig.nolibc b/tools/include/nolibc/Kconfig.nolibc new file mode 100644 index 0000000000000000000000000000000000000000..29cbc5437e70cbc5e256f00b74d0ab4801b40de7 --- /dev/null +++ b/tools/include/nolibc/Kconfig.nolibc @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 + +config ARCH_HAS_NOLIBC + bool + default y if ARM + default y if ARM64 + default y if LOONGARCH + default y if M68K + default y if MIPS + default y if PPC + default y if RISCV + default y if S390 + default y if SPARC + default y if UML_X86 + default y if X86
Some upcoming new documentation should link directly to the userprogs section.
Add a label to the section so it can be referenced.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Documentation/kbuild/makefiles.rst | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 4cc7a1b89f1803857a4723284613111e9ad71d92..2adea36ac6ebf6c292e01a3e04c0b633e3c1b8ad 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -891,6 +891,8 @@ This is possible in two ways: This will tell kbuild to build lxdialog even if not referenced in any rule.
+.. _kbuild_userprogs: + Userspace Program support =========================
Various subsystems embed non-code build artifacts into the kernel, for example the initramfs, /proc/config.gz, vDSO image, etc. Currently each user has their own implementation for that.
Add a common "blob" framework to provide this functionality. It provides standard kbuild and C APIs to embed and later access non-code build artifacts into the kernel image or modules.
Reviewed-by: Nicolas Schier n.schier@avm.de Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- Due to its closeness to kbuild this is currently added to its MAINTAINER entry. But I can also maintain it on its own. --- Documentation/kbuild/makefiles.rst | 23 +++++++++++++++++++++-- MAINTAINERS | 2 ++ include/linux/blob.h | 31 +++++++++++++++++++++++++++++++ scripts/Makefile.blobs | 19 +++++++++++++++++++ scripts/Makefile.build | 6 ++++++ scripts/Makefile.clean | 2 +- scripts/blob-wrap.c | 27 +++++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 3 deletions(-)
diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst index 2adea36ac6ebf6c292e01a3e04c0b633e3c1b8ad..5d158780948ab6d7d21287231e310dc87d5e1dc7 100644 --- a/Documentation/kbuild/makefiles.rst +++ b/Documentation/kbuild/makefiles.rst @@ -525,8 +525,8 @@ otherwise the command line check will fail, and the target will always be built.
If the target is already listed in the recognized syntax such as -obj-y/m, lib-y/m, extra-y/m, always-y/m, hostprogs, userprogs, Kbuild -automatically adds it to $(targets). Otherwise, the target must be +obj-y/m, lib-y/m, extra-y/m, always-y/m, hostprogs, userprogs, blobs, +Kbuild automatically adds it to $(targets). Otherwise, the target must be explicitly added to $(targets).
Assignments to $(targets) are without $(obj)/ prefix. if_changed may be @@ -1019,6 +1019,25 @@ There are two ways to do this. This will tell Kbuild to build binderfs_example when it visits this Makefile.
+.. _kbuild_blobs: + +Blob framework +============== + +Kbuild supports wrapping source or generated files into object files which are linked +into the kernel and then accessed at runtime through ``include/linux/blob.h``. + +Example:: + + obj-m := some-module.o + userprogs := some-userprog + blobs := some-userprog.blob.o + some-userprog.blob-symbol := some_userprog + some-module-y += some-userprog.blob.o + +Kbuild will build the :ref:`userprog <kbuild_userprogs>` ``some-userprog`` and +link it into ``some-module`` from where it can be accessed as ``BLOB(some_userprog)``. + Kbuild clean infrastructure ===========================
diff --git a/MAINTAINERS b/MAINTAINERS index a92290fffa163f9fe8fe3f04bf66426f9a894409..435f8af750d40d859b48dd4f93f7991768f218e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13063,11 +13063,13 @@ Q: https://patchwork.kernel.org/project/linux-kbuild/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git F: Documentation/kbuild/ F: Makefile +F: include/linux/blob.h F: scripts/*vmlinux* F: scripts/Kbuild* F: scripts/Makefile* F: scripts/bash-completion/ F: scripts/basic/ +F: scripts/blob-wrap.c F: scripts/clang-tools/ F: scripts/dummy-tools/ F: scripts/include/ diff --git a/include/linux/blob.h b/include/linux/blob.h new file mode 100644 index 0000000000000000000000000000000000000000..4104d04e036fadce220e05fd2d9b996323dd06e8 --- /dev/null +++ b/include/linux/blob.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Linkable blob API. + * + * Copyright (C) 2025, Linutronix GmbH. + * Author: Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +#ifndef _LINUX_BLOB_H +#define _LINUX_BLOB_H + +#include <linux/args.h> +#include <linux/types.h> + +struct blob { + const char *const path; + const u8 *data; + const u8 *end; +}; + +#define BLOB(_symbol) ({ \ + extern const struct blob CONCATENATE(__blob_, _symbol); \ + &CONCATENATE(__blob_, _symbol); \ +}) + +static inline size_t blob_size(const struct blob *blob) +{ + return blob->end - blob->data; +} + +#endif /* _LINUX_BLOB_H */ diff --git a/scripts/Makefile.blobs b/scripts/Makefile.blobs new file mode 100644 index 0000000000000000000000000000000000000000..fd20ebb41c1d6509750debf7896a08a143d28759 --- /dev/null +++ b/scripts/Makefile.blobs @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Build linkable blobs +# + +blobs := $(addprefix $(obj)/, $(blobs)) + +blob-stem = $(subst -,_,$(subst .blob,,$(basename $(patsubst $(obj)/%,%,$@)))) +blob-symbol = $(or $($(target-stem)-symbol),$(blob-stem)) + +blob-flags = -DBLOB_SYMBOL="$(blob-symbol)" -DBLOB_INPUT=$< + +quiet_cmd_blob = BLOB $@ + cmd_blob = $(CC) $(c_flags) $(blob-flags) -c -o $@ $(srctree)/scripts/blob-wrap.c + +$(blobs): $(obj)/%.blob.o: $(obj)/% $(srctree)/scripts/blob-wrap.c FORCE + $(call if_changed_dep,blob) + +targets += $(blobs) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index a6461ea411f7a95a9dd156897bec43cb22ef1092..de000268a538875596aae597efaf06058474b17d 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -529,6 +529,12 @@ ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets) include $(srctree)/scripts/Makefile.dtbs endif
+# $(sort ...) is used here to remove duplicated words and excessive spaces. +blobs := $(sort $(blobs)) +ifneq ($(blobs),) +include $(srctree)/scripts/Makefile.blobs +endif + # Build # ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.clean b/scripts/Makefile.clean index 6ead00ec7313b3e4330a8de5f1342f2da1d6eb84..536972b0a528d117e17296da9936825c3903af6e 100644 --- a/scripts/Makefile.clean +++ b/scripts/Makefile.clean @@ -25,7 +25,7 @@ subdir-ymn := $(addprefix $(obj)/,$(subdir-ymn)) # directory
__clean-files := \ - $(clean-files) $(targets) $(hostprogs) $(userprogs) \ + $(clean-files) $(targets) $(hostprogs) $(userprogs) $(blobs) \ $(extra-y) $(extra-m) $(extra-) \ $(always-y) $(always-m) $(always-) \ $(hostprogs-always-y) $(hostprogs-always-m) $(hostprogs-always-) \ diff --git a/scripts/blob-wrap.c b/scripts/blob-wrap.c new file mode 100644 index 0000000000000000000000000000000000000000..82ab3bc641bd69ec35029c2f4a9dd6d6c6720a02 --- /dev/null +++ b/scripts/blob-wrap.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/args.h> +#include <linux/blob.h> +#include <linux/stringify.h> + +#define BLOB_SYMBOL_DATA CONCATENATE(__blob_data_, BLOB_SYMBOL) +#define BLOB_SYMBOL_END CONCATENATE(__blob_end_, BLOB_SYMBOL) + +asm ( +" .pushsection .rodata, "a"\n" +" .global " __stringify(BLOB_SYMBOL_DATA) "\n" +__stringify(BLOB_SYMBOL_DATA) ":\n" +" .incbin "" __stringify(BLOB_INPUT) ""\n" +" .global " __stringify(BLOB_SYMBOL_END) "\n" +__stringify(BLOB_SYMBOL_END) ":\n" +" .popsection\n" +); + +extern const u8 BLOB_SYMBOL_DATA; +extern const u8 BLOB_SYMBOL_END; + +const struct blob CONCATENATE(__blob_, BLOB_SYMBOL) = { + .path = __stringify(BLOB_INPUT), + .data = &BLOB_SYMBOL_DATA, + .end = &BLOB_SYMBOL_END, +};
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Various subsystems embed non-code build artifacts into the kernel, for example the initramfs, /proc/config.gz, vDSO image, etc. Currently each user has their own implementation for that.
Add a common "blob" framework to provide this functionality. It provides standard kbuild and C APIs to embed and later access non-code build artifacts into the kernel image or modules.
Reviewed-by: Nicolas Schier n.schier@avm.de Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Kbuild provides only a small set of syntaxes, yet it's flexible enough to allow each Makefile to implement what it needs. I aim to keep Kbuild scripts as simple as possible and avoid over-engineering.
Instead, you can implement this in lib/kunit/Makefile.kunit-uapi or somewhere. That way, I do not have to be worried about what you do.
Also, your separate blob approach looks questionable to me. In your approach, the blob (kunit-example-uapi.blob.o) and the entry point (kunit-example-test.o) can be separate modules. The entry point would be a small amount of boilerplate. I would keep the user-program blob and its entry point in the same C file. (and I may consider writing a macro for populating a blob + knit entry)
Due to its closeness to kbuild this is currently added to its MAINTAINER entry. But I can also maintain it on its own.
Or, maybe do not add this.
On Tue, Jun 17, 2025 at 12:38:21AM +0900, Masahiro Yamada wrote:
On Wed, Jun 11, 2025 at 4:38 PM Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Various subsystems embed non-code build artifacts into the kernel, for example the initramfs, /proc/config.gz, vDSO image, etc. Currently each user has their own implementation for that.
Add a common "blob" framework to provide this functionality. It provides standard kbuild and C APIs to embed and later access non-code build artifacts into the kernel image or modules.
Reviewed-by: Nicolas Schier n.schier@avm.de Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Kbuild provides only a small set of syntaxes, yet it's flexible enough to allow each Makefile to implement what it needs. I aim to keep Kbuild scripts as simple as possible and avoid over-engineering.
Instead, you can implement this in lib/kunit/Makefile.kunit-uapi or somewhere. That way, I do not have to be worried about what you do.
The goal was to have a framework that can be used independently of KUnit, for the usecases listed in the commit message (and a few unlisted ones). But I can go with a more specific solution, too.
Also, your separate blob approach looks questionable to me.
In your approach, the blob (kunit-example-uapi.blob.o) and the entry point (kunit-example-test.o) can be separate modules.
Indeed, however I don't see the issue for this specific point.
The entry point would be a small amount of boilerplate. I would keep the user-program blob and its entry point in the same C file.
A Makefile dependency between the C file and blob payload is also necessary.
(and I may consider writing a macro for populating a blob + knit entry)
That is what I had in during my early development, and I guess I'll go with it again.
Thomas
Currently there is no test validating the result reporting from nested tests. Add one, it will also be used to validate upcoming changes to the nested test parsing.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/kunit/kunit_tool_test.py | 9 +++++++++ .../kunit/test_data/test_is_test_passed-failure-nested.log | 7 +++++++ 2 files changed, 16 insertions(+)
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index bbba921e0eacb18663abfcabb2bccf330d8666f5..691cde9b030f7729128490c1bdb42ccee1967ad6 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -165,6 +165,15 @@ class KUnitParserTest(unittest.TestCase): self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status) self.assertEqual(result.counts.errors, 0)
+ def test_parse_failed_nested_tests_log(self): + nested_log = test_data_path('test_is_test_passed-failure-nested.log') + with open(nested_log) as file: + result = kunit_parser.parse_run_tests(file.readlines(), stdout) + self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status) + self.assertEqual(result.counts.failed, 2) + self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].status) + self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].status) + def test_no_header(self): empty_log = test_data_path('test_is_test_passed-no_tests_run_no_header.log') with open(empty_log) as file: diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log new file mode 100644 index 0000000000000000000000000000000000000000..835816e0a07715a514f5f5afab1b6250037feaf4 --- /dev/null +++ b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log @@ -0,0 +1,7 @@ +KTAP version 1 +1..2 +not ok 1 subtest 1 + KTAP version 1 + 1..1 + not ok 1 test 1 +not ok 2 subtest 2
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Currently there is no test validating the result reporting from nested tests. Add one, it will also be used to validate upcoming changes to the nested test parsing.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This looks good, modulo a couple of minor suggestions below.
Regardless, Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
tools/testing/kunit/kunit_tool_test.py | 9 +++++++++ .../kunit/test_data/test_is_test_passed-failure-nested.log | 7 +++++++ 2 files changed, 16 insertions(+)
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index bbba921e0eacb18663abfcabb2bccf330d8666f5..691cde9b030f7729128490c1bdb42ccee1967ad6 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -165,6 +165,15 @@ class KUnitParserTest(unittest.TestCase): self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status) self.assertEqual(result.counts.errors, 0)
def test_parse_failed_nested_tests_log(self):
nested_log = test_data_path('test_is_test_passed-failure-nested.log')
with open(nested_log) as file:
result = kunit_parser.parse_run_tests(file.readlines(), stdout)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
self.assertEqual(result.counts.failed, 2)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].status)
Is it worth also testing the value of the nested test's result here? i.e., self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].subtests[0].status)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].status)
def test_no_header(self): empty_log = test_data_path('test_is_test_passed-no_tests_run_no_header.log') with open(empty_log) as file:
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log new file mode 100644 index 0000000000000000000000000000000000000000..835816e0a07715a514f5f5afab1b6250037feaf4 --- /dev/null +++ b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log @@ -0,0 +1,7 @@ +KTAP version 1 +1..2 +not ok 1 subtest 1
- KTAP version 1
- 1..1
not ok 1 test 1
+not ok 2 subtest 2
Having these named 'subtest 1' and 'test 1' is a bit confusing to me (as it implies the outer tests are subtests of the inner ones, which isn't right).
Could we either swap 'subtest' and 'test' here, or -- if we want to preserve the match between 'subtest' here and the subtest in the python code -- label the inner one something like 'subsubtest'?
-- 2.49.0
On Fri, Jun 20, 2025 at 05:37:39PM +0800, David Gow wrote:
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Currently there is no test validating the result reporting from nested tests. Add one, it will also be used to validate upcoming changes to the nested test parsing.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This looks good, modulo a couple of minor suggestions below.
Regardless, Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
tools/testing/kunit/kunit_tool_test.py | 9 +++++++++ .../kunit/test_data/test_is_test_passed-failure-nested.log | 7 +++++++ 2 files changed, 16 insertions(+)
diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index bbba921e0eacb18663abfcabb2bccf330d8666f5..691cde9b030f7729128490c1bdb42ccee1967ad6 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -165,6 +165,15 @@ class KUnitParserTest(unittest.TestCase): self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status) self.assertEqual(result.counts.errors, 0)
def test_parse_failed_nested_tests_log(self):
nested_log = test_data_path('test_is_test_passed-failure-nested.log')
with open(nested_log) as file:
result = kunit_parser.parse_run_tests(file.readlines(), stdout)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
self.assertEqual(result.counts.failed, 2)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].status)
Is it worth also testing the value of the nested test's result here? i.e., self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].subtests[0].status)
This should be result.subtests[1].subtests[0].status. But Ack and done.
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].status)
def test_no_header(self): empty_log = test_data_path('test_is_test_passed-no_tests_run_no_header.log') with open(empty_log) as file:
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log new file mode 100644 index 0000000000000000000000000000000000000000..835816e0a07715a514f5f5afab1b6250037feaf4 --- /dev/null +++ b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log @@ -0,0 +1,7 @@ +KTAP version 1 +1..2 +not ok 1 subtest 1
- KTAP version 1
- 1..1
not ok 1 test 1
+not ok 2 subtest 2
Having these named 'subtest 1' and 'test 1' is a bit confusing to me (as it implies the outer tests are subtests of the inner ones, which isn't right).
Could we either swap 'subtest' and 'test' here, or -- if we want to preserve the match between 'subtest' here and the subtest in the python code -- label the inner one something like 'subsubtest'?
Ack.
If a subtest itself reports success, but the outer testcase fails, the whole testcase should be reported as a failure. However the status is recalculated based on the test counts, overwriting the outer test result. Synthesize a failed test in this case to make sure the failure is not swallowed.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/kunit/kunit_parser.py | 5 +++++ tools/testing/kunit/kunit_tool_test.py | 2 +- tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index c176487356e6c94882046b19ea696d750905b8d5..2478beb28fc3db825855ad46200340e884da7df1 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -686,6 +686,11 @@ def bubble_up_test_results(test: Test) -> None: counts.add_status(status) elif test.counts.get_status() == TestStatus.TEST_CRASHED: test.status = TestStatus.TEST_CRASHED + if not test.ok_status(): + for t in subtests: + if not t.ok_status(): + counts.add_status(t.status) + break
def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: bool, printer: Printer) -> Test: """ diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index 691cde9b030f7729128490c1bdb42ccee1967ad6..c25f52650837e83325b06bddd2aa665fd29f91d9 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -170,7 +170,7 @@ class KUnitParserTest(unittest.TestCase): with open(nested_log) as file: result = kunit_parser.parse_run_tests(file.readlines(), stdout) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status) - self.assertEqual(result.counts.failed, 2) + self.assertEqual(result.counts.failed, 3) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].status) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].status)
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log index 835816e0a07715a514f5f5afab1b6250037feaf4..cd9033c464792e6294905a5676346684182874ad 100644 --- a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log +++ b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log @@ -1,5 +1,8 @@ KTAP version 1 1..2 + KTAP version 1 + 1..1 + ok 1 test 1 not ok 1 subtest 1 KTAP version 1 1..1
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
If a subtest itself reports success, but the outer testcase fails, the whole testcase should be reported as a failure. However the status is recalculated based on the test counts, overwriting the outer test result. Synthesize a failed test in this case to make sure the failure is not swallowed.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Hmm... this is definitely a nasty edge-case. I don't completely like this solution, but none of the other options seem drastically better.
I think the more obvious options are either to _always_ count tests alongside their subtests, or to _never_ do so, but acknowledge that "test failed, but failure count is 0" is a valid option. But neither of those are especially satisfying, either greatly inflating test counts, or creating obvious contradictions.
So I'm tentatively in favour of this, but if anyone has a nicer way of doing it, I'm all ears.
The implementation looks good. If we can add the explicit checks for the sub(sub)test results as mentioned in the previous patch, that'd be even better.
Reviewed-by: David Gow davidgow@google.com
tools/testing/kunit/kunit_parser.py | 5 +++++ tools/testing/kunit/kunit_tool_test.py | 2 +- tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index c176487356e6c94882046b19ea696d750905b8d5..2478beb28fc3db825855ad46200340e884da7df1 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -686,6 +686,11 @@ def bubble_up_test_results(test: Test) -> None: counts.add_status(status) elif test.counts.get_status() == TestStatus.TEST_CRASHED: test.status = TestStatus.TEST_CRASHED
if not test.ok_status():
for t in subtests:
if not t.ok_status():
counts.add_status(t.status)
break
def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: bool, printer: Printer) -> Test: """ diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index 691cde9b030f7729128490c1bdb42ccee1967ad6..c25f52650837e83325b06bddd2aa665fd29f91d9 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -170,7 +170,7 @@ class KUnitParserTest(unittest.TestCase): with open(nested_log) as file: result = kunit_parser.parse_run_tests(file.readlines(), stdout) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
self.assertEqual(result.counts.failed, 2)
self.assertEqual(result.counts.failed, 3) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].status)
Could we add: self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.subtests[0].subtests[0].status)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].status)
and
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].subtests[0].status)
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log index 835816e0a07715a514f5f5afab1b6250037feaf4..cd9033c464792e6294905a5676346684182874ad 100644 --- a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log +++ b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log @@ -1,5 +1,8 @@ KTAP version 1 1..2
- KTAP version 1
- 1..1
ok 1 test 1
not ok 1 subtest 1 KTAP version 1 1..1
-- 2.49.0
On Fri, Jun 20, 2025 at 05:37:44PM +0800, David Gow wrote:
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
If a subtest itself reports success, but the outer testcase fails, the whole testcase should be reported as a failure. However the status is recalculated based on the test counts, overwriting the outer test result. Synthesize a failed test in this case to make sure the failure is not swallowed.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Hmm... this is definitely a nasty edge-case. I don't completely like this solution, but none of the other options seem drastically better.
I think the more obvious options are either to _always_ count tests alongside their subtests, or to _never_ do so, but acknowledge that "test failed, but failure count is 0" is a valid option. But neither of those are especially satisfying, either greatly inflating test counts, or creating obvious contradictions.
So I'm tentatively in favour of this, but if anyone has a nicer way of doing it, I'm all ears.
Agreed, it is not great. I'd also be happy for better ideas.
The implementation looks good. If we can add the explicit checks for the sub(sub)test results as mentioned in the previous patch, that'd be even better.
Reviewed-by: David Gow davidgow@google.com
tools/testing/kunit/kunit_parser.py | 5 +++++ tools/testing/kunit/kunit_tool_test.py | 2 +- tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index c176487356e6c94882046b19ea696d750905b8d5..2478beb28fc3db825855ad46200340e884da7df1 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -686,6 +686,11 @@ def bubble_up_test_results(test: Test) -> None: counts.add_status(status) elif test.counts.get_status() == TestStatus.TEST_CRASHED: test.status = TestStatus.TEST_CRASHED
if not test.ok_status():
for t in subtests:
if not t.ok_status():
counts.add_status(t.status)
break
def parse_test(lines: LineStream, expected_num: int, log: List[str], is_subtest: bool, printer: Printer) -> Test: """ diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py index 691cde9b030f7729128490c1bdb42ccee1967ad6..c25f52650837e83325b06bddd2aa665fd29f91d9 100755 --- a/tools/testing/kunit/kunit_tool_test.py +++ b/tools/testing/kunit/kunit_tool_test.py @@ -170,7 +170,7 @@ class KUnitParserTest(unittest.TestCase): with open(nested_log) as file: result = kunit_parser.parse_run_tests(file.readlines(), stdout) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.status)
self.assertEqual(result.counts.failed, 2)
self.assertEqual(result.counts.failed, 3) self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[0].status)
Could we add: self.assertEqual(kunit_parser.TestStatus.SUCCESS, result.subtests[0].subtests[0].status)
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].status)
Ack.
and
self.assertEqual(kunit_parser.TestStatus.FAILURE, result.subtests[1].subtests[0].status)
This is now already in the previous patch.
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log index 835816e0a07715a514f5f5afab1b6250037feaf4..cd9033c464792e6294905a5676346684182874ad 100644 --- a/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log +++ b/tools/testing/kunit/test_data/test_is_test_passed-failure-nested.log @@ -1,5 +1,8 @@ KTAP version 1 1..2
- KTAP version 1
- 1..1
ok 1 test 1
not ok 1 subtest 1 KTAP version 1 1..1
-- 2.49.0
Skipped tests reported by kselftest.h use a different format than KTAP, there is no explicit test name. Normally the test name is part of the free-form string after the SKIP keyword:
ok 3 # SKIP test: some reason
Extend the parser to handle those correctly. Use the free-form string as test name instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/kunit/kunit_parser.py | 8 +++++--- tools/testing/kunit/test_data/test_is_test_passed-kselftest.log | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 2478beb28fc3db825855ad46200340e884da7df1..4599d23c79b79f0e219d655c7053c8c3b34f8152 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -352,9 +352,9 @@ def parse_test_plan(lines: LineStream, test: Test) -> bool: lines.pop() return True
-TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?([^#]*)( # .*)?$') +TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+)? ?(- )?([^#]*)( # .*)?$')
-TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?(.*) # SKIP(.*)$') +TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+)? ?(- )?(.*) # SKIP ?(.*)$')
def peek_test_name_match(lines: LineStream, test: Test) -> bool: """ @@ -379,6 +379,8 @@ def peek_test_name_match(lines: LineStream, test: Test) -> bool: if not match: return False name = match.group(4) + if not name: + return False return name == test.name
def parse_test_result(lines: LineStream, test: Test, @@ -416,7 +418,7 @@ def parse_test_result(lines: LineStream, test: Test,
# Set name of test object if skip_match: - test.name = skip_match.group(4) + test.name = skip_match.group(4) or skip_match.group(5) else: test.name = match.group(4)
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log b/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log index 65d3f27feaf22a3f47ed831c4c24f6f11c625a92..30d9ef18bcec177067288d5242771236f29b7d56 100644 --- a/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log +++ b/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log @@ -1,5 +1,5 @@ TAP version 13 -1..2 +1..3 # selftests: membarrier: membarrier_test_single_thread # TAP version 13 # 1..2 @@ -12,3 +12,4 @@ ok 1 selftests: membarrier: membarrier_test_single_thread # ok 1 sys_membarrier available # ok 2 sys membarrier invalid command test: command = -1, flags = 0, errno = 22. Failed as expected ok 2 selftests: membarrier: membarrier_test_multi_thread +ok 3 # SKIP selftests: membarrier: membarrier_test_multi_thread
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Skipped tests reported by kselftest.h use a different format than KTAP, there is no explicit test name. Normally the test name is part of the free-form string after the SKIP keyword:
ok 3 # SKIP test: some reason
Extend the parser to handle those correctly. Use the free-form string as test name instead.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Nice: this is a great improvement.
Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
tools/testing/kunit/kunit_parser.py | 8 +++++--- tools/testing/kunit/test_data/test_is_test_passed-kselftest.log | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/tools/testing/kunit/kunit_parser.py b/tools/testing/kunit/kunit_parser.py index 2478beb28fc3db825855ad46200340e884da7df1..4599d23c79b79f0e219d655c7053c8c3b34f8152 100644 --- a/tools/testing/kunit/kunit_parser.py +++ b/tools/testing/kunit/kunit_parser.py @@ -352,9 +352,9 @@ def parse_test_plan(lines: LineStream, test: Test) -> bool: lines.pop() return True
-TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?([^#]*)( # .*)?$') +TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+)? ?(- )?([^#]*)( # .*)?$')
-TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?(.*) # SKIP(.*)$') +TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+)? ?(- )?(.*) # SKIP ?(.*)$')
def peek_test_name_match(lines: LineStream, test: Test) -> bool: """ @@ -379,6 +379,8 @@ def peek_test_name_match(lines: LineStream, test: Test) -> bool: if not match: return False name = match.group(4)
if not name:
return False return name == test.name
def parse_test_result(lines: LineStream, test: Test, @@ -416,7 +418,7 @@ def parse_test_result(lines: LineStream, test: Test,
# Set name of test object if skip_match:
test.name = skip_match.group(4)
test.name = skip_match.group(4) or skip_match.group(5) else: test.name = match.group(4)
diff --git a/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log b/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log index 65d3f27feaf22a3f47ed831c4c24f6f11c625a92..30d9ef18bcec177067288d5242771236f29b7d56 100644 --- a/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log +++ b/tools/testing/kunit/test_data/test_is_test_passed-kselftest.log @@ -1,5 +1,5 @@ TAP version 13 -1..2 +1..3 # selftests: membarrier: membarrier_test_single_thread # TAP version 13 # 1..2 @@ -12,3 +12,4 @@ ok 1 selftests: membarrier: membarrier_test_single_thread # ok 1 sys_membarrier available # ok 2 sys membarrier invalid command test: command = -1, flags = 0, errno = 22. Failed as expected ok 2 selftests: membarrier: membarrier_test_multi_thread +ok 3 # SKIP selftests: membarrier: membarrier_test_multi_thread
-- 2.49.0
An upcoming change will add 'userprogs' to the kunit subdirectory. For kbuild to properly clean up these build artifacts the subdirectory needs to be always processed.
Pushing the special logic for hook.o into the kunit Makefile also makes the logic easier to understand.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- lib/Makefile | 4 ---- lib/kunit/Makefile | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/lib/Makefile b/lib/Makefile index c38582f187dd81916113319072e5cfef26f26c84..698566135091cc3bf0054f1954b434dc3325364a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -108,11 +108,7 @@ test_fpu-y := test_fpu_glue.o test_fpu_impl.o CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU) CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
-# Some KUnit files (hooks.o) need to be built-in even when KUnit is a module, -# so we can't just use obj-$(CONFIG_KUNIT). -ifdef CONFIG_KUNIT obj-y += kunit/ -endif
ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 5aa51978e456ab3bb60c12071a26cf2bdcb1b508..656f1fa35abcc635e67d5b4cb1bc586b48415ac5 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -17,7 +17,7 @@ kunit-objs += debugfs.o endif
# KUnit 'hooks' are built-in even when KUnit is built as a module. -obj-y += hooks.o +obj-$(if $(CONFIG_KUNIT),y) += hooks.o
obj-$(CONFIG_KUNIT_TEST) += kunit-test.o obj-$(CONFIG_KUNIT_TEST) += platform-test.o
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
An upcoming change will add 'userprogs' to the kunit subdirectory. For kbuild to properly clean up these build artifacts the subdirectory needs to be always processed.
Pushing the special logic for hook.o into the kunit Makefile also makes the logic easier to understand.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
I'm quite happy with this.
Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
lib/Makefile | 4 ---- lib/kunit/Makefile | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/lib/Makefile b/lib/Makefile index c38582f187dd81916113319072e5cfef26f26c84..698566135091cc3bf0054f1954b434dc3325364a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -108,11 +108,7 @@ test_fpu-y := test_fpu_glue.o test_fpu_impl.o CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU) CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
-# Some KUnit files (hooks.o) need to be built-in even when KUnit is a module, -# so we can't just use obj-$(CONFIG_KUNIT). -ifdef CONFIG_KUNIT obj-y += kunit/ -endif
ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 5aa51978e456ab3bb60c12071a26cf2bdcb1b508..656f1fa35abcc635e67d5b4cb1bc586b48415ac5 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -17,7 +17,7 @@ kunit-objs += debugfs.o endif
# KUnit 'hooks' are built-in even when KUnit is built as a module. -obj-y += hooks.o +obj-$(if $(CONFIG_KUNIT),y) += hooks.o
obj-$(CONFIG_KUNIT_TEST) += kunit-test.o obj-$(CONFIG_KUNIT_TEST) += platform-test.o
-- 2.49.0
-- You received this message because you are subscribed to the Google Groups "KUnit Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to kunit-dev+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/kunit-dev/20250611-kunit-kselftests-v3-11-....
The upcoming kunit UAPI framework will run userspace executables as part of kunit. These may use the LSX or LASX instructions.
Make sure the kunit kernel can handle these instructions.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- tools/testing/kunit/qemu_configs/loongarch.py | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/tools/testing/kunit/qemu_configs/loongarch.py b/tools/testing/kunit/qemu_configs/loongarch.py index a92422967d1da9f1658ef1e80d0d7365ddbae307..1dba755284f11ffc94d8946105b0cfa49cb6f604 100644 --- a/tools/testing/kunit/qemu_configs/loongarch.py +++ b/tools/testing/kunit/qemu_configs/loongarch.py @@ -11,6 +11,8 @@ CONFIG_PVPANIC_PCI=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_CPU_HAS_LSX=y +CONFIG_CPU_HAS_LASX=y ''', qemu_arch='loongarch64', kernel_path='arch/loongarch/boot/vmlinux.elf',
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
The upcoming kunit UAPI framework will run userspace executables as part of kunit. These may use the LSX or LASX instructions.
Make sure the kunit kernel can handle these instructions.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Looks fine to me. I confess to not knowing much about the details of Loongarch CPUs, though, so while it hasn't broken anything here, I make no further guarantees.
Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
tools/testing/kunit/qemu_configs/loongarch.py | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/tools/testing/kunit/qemu_configs/loongarch.py b/tools/testing/kunit/qemu_configs/loongarch.py index a92422967d1da9f1658ef1e80d0d7365ddbae307..1dba755284f11ffc94d8946105b0cfa49cb6f604 100644 --- a/tools/testing/kunit/qemu_configs/loongarch.py +++ b/tools/testing/kunit/qemu_configs/loongarch.py @@ -11,6 +11,8 @@ CONFIG_PVPANIC_PCI=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_CPU_HAS_LSX=y +CONFIG_CPU_HAS_LASX=y ''', qemu_arch='loongarch64', kernel_path='arch/loongarch/boot/vmlinux.elf',
-- 2.49.0
Enable running UAPI tests as part of kunit. The selftests are embedded into the kernel image and their output is forwarded to kunit for unified reporting.
The implementation reuses parts of usermode drivers and usermode helpers. However these frameworks are not used directly as they make it impossible to retrieve a thread's exit code.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
--- Currently this depends on CONFIG_KUNIT=y as it uses some non-exported symbols around process management. --- Documentation/dev-tools/kunit/api/index.rst | 5 + Documentation/dev-tools/kunit/api/uapi.rst | 12 ++ include/kunit/uapi.h | 24 +++ lib/kunit/Kconfig | 10 + lib/kunit/Makefile | 2 + lib/kunit/uapi.c | 287 ++++++++++++++++++++++++++++ 6 files changed, 340 insertions(+)
diff --git a/Documentation/dev-tools/kunit/api/index.rst b/Documentation/dev-tools/kunit/api/index.rst index 5cdb552a0808f25baeff5e47a9227b7b62c69e40..34d8fee9a97059d6da919a6fb1a7e359b5e0beef 100644 --- a/Documentation/dev-tools/kunit/api/index.rst +++ b/Documentation/dev-tools/kunit/api/index.rst @@ -9,6 +9,7 @@ API Reference test resource functionredirection + uapi clk of platformdevice @@ -32,6 +33,10 @@ Documentation/dev-tools/kunit/api/functionredirection.rst
- Documents the KUnit Function Redirection API
+Documentation/dev-tools/kunit/api/uapi.rst + + - Documents the KUnit Userspace testing API + Driver KUnit API ================
diff --git a/Documentation/dev-tools/kunit/api/uapi.rst b/Documentation/dev-tools/kunit/api/uapi.rst new file mode 100644 index 0000000000000000000000000000000000000000..b4764424c629bf69194cf2786f52aef154b02bf8 --- /dev/null +++ b/Documentation/dev-tools/kunit/api/uapi.rst @@ -0,0 +1,12 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================== +Userspace Test API +================== + +This file documents all of the userspace testing API. +Userspace tests should be built as :ref:`userprogs <kbuild_userprogs>` and included into the test +module or kernel as :ref:`blobs <kbuild_blobs>`. + +.. kernel-doc:: include/kunit/uapi.h + :internal: diff --git a/include/kunit/uapi.h b/include/kunit/uapi.h new file mode 100644 index 0000000000000000000000000000000000000000..a6181790c96a42df05839097991c1fbfd889cdbe --- /dev/null +++ b/include/kunit/uapi.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * KUnit Userspace testing API. + * + * Copyright (C) 2025, Linutronix GmbH. + * Author: Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +#ifndef _KUNIT_UAPI_H +#define _KUNIT_UAPI_H + +struct blob; +struct kunit; + +/** + * kunit_uapi_run_kselftest() - Run a userspace kselftest as part of kunit + * @test: The test context object. + * @executable: kselftest executable to run + * + * Runs the kselftest and forwards its TAP output and exit status to kunit. + */ +void kunit_uapi_run_kselftest(struct kunit *test, const struct blob *executable); + +#endif /* _KUNIT_UAPI_H */ diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index a97897edd9642f3e5df7fdd9dee26ee5cf00d6a4..1f2f5f2213a7d8438cd2683955f22e34f3a036dd 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -93,4 +93,14 @@ config KUNIT_AUTORUN_ENABLED In most cases this should be left as Y. Only if additional opt-in behavior is needed should this be set to N.
+config KUNIT_UAPI + def_bool y + depends on KUNIT=y + depends on CC_CAN_LINK_STATIC || ARCH_HAS_NOLIBC + select HEADERS_INSTALL + help + Enables support for building and running userspace selftests as part of kunit. + These tests should be statically linked and use kselftest.h or kselftest_harness.h + for status reporting. + endif # KUNIT diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 656f1fa35abcc635e67d5b4cb1bc586b48415ac5..dafa09bd4241c24d31c4c19edecb67bf724127d7 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -12,6 +12,8 @@ kunit-objs += test.o \ device.o \ platform.o
+kunit-$(CONFIG_KUNIT_UAPI) += uapi.o + ifeq ($(CONFIG_KUNIT_DEBUGFS),y) kunit-objs += debugfs.o endif diff --git a/lib/kunit/uapi.c b/lib/kunit/uapi.c new file mode 100644 index 0000000000000000000000000000000000000000..121146dda533b3f90aca37c20bd0e7a1d20cb3b5 --- /dev/null +++ b/lib/kunit/uapi.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit Userspace testing API. + * + * Copyright (C) 2025, Linutronix GmbH. + * Author: Thomas Weißschuh thomas.weissschuh@linutronix.de + */ + +#include <linux/binfmts.h> +#include <linux/blob.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/fs_struct.h> +#include <linux/pid.h> +#include <linux/pipe_fs_i.h> +#include <linux/sched/task.h> +#include <linux/types.h> + +#include <kunit/test.h> +#include <kunit/uapi.h> + +#define KSFT_PASS 0 +#define KSFT_FAIL 1 +#define KSFT_XFAIL 2 +#define KSFT_XPASS 3 +#define KSFT_SKIP 4 + +static struct vfsmount *kunit_uapi_mount_ramfs(void) +{ + struct file_system_type *type; + struct vfsmount *mnt; + + type = get_fs_type("ramfs"); + if (!type) + return ERR_PTR(-ENODEV); + + /* FIXME + * The mount setup is supposed to look like this: + * kunit_uapi_mount_ramfs() sets up a private mount, + * with nothing visible except the new tmpfs. + * Then each executable execution gets a new namespace on top of that + * on which it can mount whatever it needs. + * However I didn't manage to set this up, so keep everything simple + * for now and let somebody familiar with the VFS figure this out. + */ + + mnt = kern_mount(type); + put_filesystem(type); + + return mnt; +} + +static int kunit_uapi_write_file(struct vfsmount *mnt, const char *name, mode_t mode, + const u8 *data, size_t size) +{ + struct file *file; + ssize_t written; + + file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, mode); + if (IS_ERR(file)) + return PTR_ERR(file); + + written = kernel_write(file, data, size, NULL); + filp_close(file, NULL); + if (written != size) { + if (written >= 0) + return -ENOMEM; + return written; + } + + /* Flush delayed fput so exec can open the file read-only */ + flush_delayed_fput(); + + return 0; +} + +struct kunit_uapi_user_mode_thread_ctx { + const char *executable; + + /* Signals mnt, out, pwd and tgid */ + struct completion setup_done; + struct vfsmount *mnt; + struct file *out; + struct path pwd; + pid_t tgid; + + /* Valid after wait(tgid) */ + int exec_err; +}; + +static int kunit_uapi_user_mode_thread_init(void *data) +{ + struct kunit_uapi_user_mode_thread_ctx *ctx = data; + const char *const argv[] = { + ctx->executable, + NULL + }; + struct file *out[2]; + int err; + + err = create_pipe_files(out, 0); + if (err) + return err; + + /* stdin, use the *write* end to the pipe to have an unreadable input */ + err = replace_fd(0, out[1], 0); + if (err < 0) { + fput(out[0]); + fput(out[1]); + return err; + } + + /* stdout */ + err = replace_fd(1, out[1], 0); + if (err < 0) { + replace_fd(0, NULL, 0); + fput(out[0]); + fput(out[1]); + return err; + } + + /* stderr */ + err = replace_fd(2, out[1], 0); + if (err < 0) { + replace_fd(0, NULL, 0); + replace_fd(1, NULL, 0); + fput(out[0]); + fput(out[1]); + return err; + } + + fput(out[1]); + + ctx->out = out[0]; + ctx->tgid = current->tgid; + + set_fs_pwd(current->fs, &ctx->pwd); + kernel_sigaction(SIGKILL, SIG_DFL); + kernel_sigaction(SIGABRT, SIG_DFL); + + complete(&ctx->setup_done); + ctx->exec_err = kernel_execve(ctx->executable, argv, NULL); + if (!ctx->exec_err) + return 0; + do_exit(0); +} + +static size_t kunit_uapi_printk_subtest_lines(struct kunit *test, char *buf, size_t s) +{ + const char *ptr = buf, *newline; + size_t n; + + while (s) { + newline = strnchr(ptr, s, '\n'); + if (!newline) + break; + + n = newline - ptr + 1; + + kunit_log(KERN_INFO, test, KUNIT_SUBSUBTEST_INDENT "%.*s", (int)n, ptr); + ptr += n; + s -= n; + } + + memmove(buf, ptr, s); + + return s; +} + +static int kunit_uapi_forward_to_printk(struct kunit *test, struct file *output) +{ + /* + * printk() automatically adds a newline after each message. + * Therefore only fully accumulated lines can be forwarded. + * Each line needs to fit into the buffer below. + */ + char buf[512]; + size_t s = 0; + ssize_t n; + + while (1) { + n = kernel_read(output, buf + s, sizeof(buf) - s, NULL); + if (n <= 0) + return n; + s = kunit_uapi_printk_subtest_lines(test, buf, s + n); + } +} + +static void kunit_uapi_kill_pid(pid_t pid) +{ + struct pid *p; + + p = find_get_pid(pid); + kill_pid(p, SIGKILL, 1); + put_pid(p); +} + +static int kunit_uapi_run_executable_in_mount(struct kunit *test, const char *executable, + struct vfsmount *mnt) +{ + struct kunit_uapi_user_mode_thread_ctx ctx = { + .setup_done = COMPLETION_INITIALIZER_ONSTACK(ctx.setup_done), + .executable = executable, + .pwd = { + .mnt = mnt, + .dentry = mnt->mnt_root, + }, + }; + int forward_err, wait_err, ret; + pid_t pid; + + /* If SIGCHLD is ignored do_wait won't populate the status. */ + kernel_sigaction(SIGCHLD, SIG_DFL); + pid = user_mode_thread(kunit_uapi_user_mode_thread_init, &ctx, SIGCHLD); + if (pid < 0) { + kernel_sigaction(SIGCHLD, SIG_IGN); + return pid; + } + + wait_for_completion(&ctx.setup_done); + + forward_err = kunit_uapi_forward_to_printk(test, ctx.out); + if (forward_err) + kunit_uapi_kill_pid(ctx.tgid); + + wait_err = kernel_wait(ctx.tgid, &ret); + + /* Restore default kernel sig handler */ + kernel_sigaction(SIGCHLD, SIG_IGN); + + if (ctx.exec_err) + return ctx.exec_err; + if (forward_err) + return forward_err; + if (wait_err < 0) + return wait_err; + return ret; +} + +static int kunit_uapi_run_executable(struct kunit *test, const struct blob *executable) +{ + const char *exe_name = kbasename(executable->path); + struct vfsmount *mnt; + int err; + + mnt = kunit_uapi_mount_ramfs(); + if (IS_ERR(mnt)) + return PTR_ERR(mnt); + + err = kunit_uapi_write_file(mnt, exe_name, 0755, executable->data, blob_size(executable)); + + if (!err) + err = kunit_uapi_run_executable_in_mount(test, exe_name, mnt); + + kern_unmount(mnt); + + return err; +} + +void kunit_uapi_run_kselftest(struct kunit *test, const struct blob *executable) +{ + u8 exit_code, exit_signal; + int err; + + err = kunit_uapi_run_executable(test, executable); + if (err < 0) + KUNIT_FAIL(test, "Could not run test executable: %pe\n", ERR_PTR(err)); + + exit_code = err >> 8; + exit_signal = err & 0xff; + + if (exit_signal) + KUNIT_FAIL(test, "kselftest exited with signal: %d\n", exit_signal); + else if (exit_code == KSFT_PASS) + ; /* Noop */ + else if (exit_code == KSFT_FAIL) + KUNIT_FAIL(test, "kselftest exited with code KSFT_FAIL\n"); + else if (exit_code == KSFT_XPASS) + KUNIT_FAIL(test, "kselftest exited with code KSFT_XPASS\n"); + else if (exit_code == KSFT_XFAIL) + ; /* Noop */ + else if (exit_code == KSFT_SKIP) + kunit_mark_skipped(test, "kselftest exited with code KSFT_SKIP\n"); + else + KUNIT_FAIL(test, "kselftest exited with unknown exit code: %d\n", exit_code); +} +EXPORT_SYMBOL_GPL(kunit_uapi_run_kselftest);
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Enable running UAPI tests as part of kunit. The selftests are embedded into the kernel image and their output is forwarded to kunit for unified reporting.
The implementation reuses parts of usermode drivers and usermode helpers. However these frameworks are not used directly as they make it impossible to retrieve a thread's exit code.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
It feels to me like there are three features hidden in here: - KUnit helpers for manipulating vfs files - A way of having KUnit tests run userspace helpers - The full framework for writing/running whole tests in userspace.
It's really the first two which excite me personally most -- as they give us a sort-of inverse to the kselftest "helper module" paradigm -- so we can test things which are impossible to test from within kernelspace. So maybe those APIs should be exposed separately (so a test can be written mostly in kernel-space using the KUnit framework APIs, and just call out to a helper where needed). But I'm happy for them to stay private functions until we have a test which actually needs them.
Currently this depends on CONFIG_KUNIT=y as it uses some non-exported symbols around process management.
That's fine for now, IMHO, but will make it difficult to use this on, e.g., Red Hat setups, where CONFIG_KUNIT=m. Hopefully we can resolve this by exporting some of the symbols?
In general, I'm happy with the implementation here. The fs stuff probably needs a closer look from someone who knows the vfs better than me, though.
Nevertheless, Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
Documentation/dev-tools/kunit/api/index.rst | 5 + Documentation/dev-tools/kunit/api/uapi.rst | 12 ++ include/kunit/uapi.h | 24 +++ lib/kunit/Kconfig | 10 + lib/kunit/Makefile | 2 + lib/kunit/uapi.c | 287 ++++++++++++++++++++++++++++ 6 files changed, 340 insertions(+)
diff --git a/Documentation/dev-tools/kunit/api/index.rst b/Documentation/dev-tools/kunit/api/index.rst index 5cdb552a0808f25baeff5e47a9227b7b62c69e40..34d8fee9a97059d6da919a6fb1a7e359b5e0beef 100644 --- a/Documentation/dev-tools/kunit/api/index.rst +++ b/Documentation/dev-tools/kunit/api/index.rst @@ -9,6 +9,7 @@ API Reference test resource functionredirection
uapi clk of platformdevice
@@ -32,6 +33,10 @@ Documentation/dev-tools/kunit/api/functionredirection.rst
- Documents the KUnit Function Redirection API
+Documentation/dev-tools/kunit/api/uapi.rst
- Documents the KUnit Userspace testing API
Driver KUnit API
diff --git a/Documentation/dev-tools/kunit/api/uapi.rst b/Documentation/dev-tools/kunit/api/uapi.rst new file mode 100644 index 0000000000000000000000000000000000000000..b4764424c629bf69194cf2786f52aef154b02bf8 --- /dev/null +++ b/Documentation/dev-tools/kunit/api/uapi.rst @@ -0,0 +1,12 @@ +.. SPDX-License-Identifier: GPL-2.0
+================== +Userspace Test API +==================
+This file documents all of the userspace testing API. +Userspace tests should be built as :ref:`userprogs <kbuild_userprogs>` and included into the test +module or kernel as :ref:`blobs <kbuild_blobs>`.
+.. kernel-doc:: include/kunit/uapi.h
- :internal:
diff --git a/include/kunit/uapi.h b/include/kunit/uapi.h new file mode 100644 index 0000000000000000000000000000000000000000..a6181790c96a42df05839097991c1fbfd889cdbe --- /dev/null +++ b/include/kunit/uapi.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- KUnit Userspace testing API.
- Copyright (C) 2025, Linutronix GmbH.
- Author: Thomas Weißschuh thomas.weissschuh@linutronix.de
- */
+#ifndef _KUNIT_UAPI_H +#define _KUNIT_UAPI_H
+struct blob; +struct kunit;
+/**
- kunit_uapi_run_kselftest() - Run a userspace kselftest as part of kunit
- @test: The test context object.
- @executable: kselftest executable to run
- Runs the kselftest and forwards its TAP output and exit status to kunit.
- */
+void kunit_uapi_run_kselftest(struct kunit *test, const struct blob *executable);
+#endif /* _KUNIT_UAPI_H */ diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index a97897edd9642f3e5df7fdd9dee26ee5cf00d6a4..1f2f5f2213a7d8438cd2683955f22e34f3a036dd 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -93,4 +93,14 @@ config KUNIT_AUTORUN_ENABLED In most cases this should be left as Y. Only if additional opt-in behavior is needed should this be set to N.
+config KUNIT_UAPI
def_bool y
Maybe it's worth making this configurable separately? I could imagine people wanting an easy way to build a kernel without all of the test blobs built-in.
depends on KUNIT=y
depends on CC_CAN_LINK_STATIC || ARCH_HAS_NOLIBC
select HEADERS_INSTALL
help
Enables support for building and running userspace selftests as part of kunit.
These tests should be statically linked and use kselftest.h or kselftest_harness.h
for status reporting.
endif # KUNIT diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 656f1fa35abcc635e67d5b4cb1bc586b48415ac5..dafa09bd4241c24d31c4c19edecb67bf724127d7 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -12,6 +12,8 @@ kunit-objs += test.o \ device.o \ platform.o
+kunit-$(CONFIG_KUNIT_UAPI) += uapi.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y) kunit-objs += debugfs.o endif diff --git a/lib/kunit/uapi.c b/lib/kunit/uapi.c new file mode 100644 index 0000000000000000000000000000000000000000..121146dda533b3f90aca37c20bd0e7a1d20cb3b5 --- /dev/null +++ b/lib/kunit/uapi.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- KUnit Userspace testing API.
- Copyright (C) 2025, Linutronix GmbH.
- Author: Thomas Weißschuh thomas.weissschuh@linutronix.de
- */
+#include <linux/binfmts.h> +#include <linux/blob.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/fs_struct.h> +#include <linux/pid.h> +#include <linux/pipe_fs_i.h> +#include <linux/sched/task.h> +#include <linux/types.h>
+#include <kunit/test.h> +#include <kunit/uapi.h>
+#define KSFT_PASS 0 +#define KSFT_FAIL 1 +#define KSFT_XFAIL 2 +#define KSFT_XPASS 3 +#define KSFT_SKIP 4
+static struct vfsmount *kunit_uapi_mount_ramfs(void) +{
struct file_system_type *type;
struct vfsmount *mnt;
type = get_fs_type("ramfs");
if (!type)
return ERR_PTR(-ENODEV);
/* FIXME
* The mount setup is supposed to look like this:
* kunit_uapi_mount_ramfs() sets up a private mount,
* with nothing visible except the new tmpfs.
* Then each executable execution gets a new namespace on top of that
* on which it can mount whatever it needs.
* However I didn't manage to set this up, so keep everything simple
* for now and let somebody familiar with the VFS figure this out.
*/
mnt = kern_mount(type);
put_filesystem(type);
return mnt;
+}
+static int kunit_uapi_write_file(struct vfsmount *mnt, const char *name, mode_t mode,
const u8 *data, size_t size)
+{
struct file *file;
ssize_t written;
file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, mode);
if (IS_ERR(file))
return PTR_ERR(file);
written = kernel_write(file, data, size, NULL);
filp_close(file, NULL);
if (written != size) {
if (written >= 0)
return -ENOMEM;
return written;
}
/* Flush delayed fput so exec can open the file read-only */
flush_delayed_fput();
return 0;
+}
+struct kunit_uapi_user_mode_thread_ctx {
const char *executable;
/* Signals mnt, out, pwd and tgid */
struct completion setup_done;
struct vfsmount *mnt;
struct file *out;
struct path pwd;
pid_t tgid;
/* Valid after wait(tgid) */
int exec_err;
+};
+static int kunit_uapi_user_mode_thread_init(void *data) +{
struct kunit_uapi_user_mode_thread_ctx *ctx = data;
const char *const argv[] = {
ctx->executable,
NULL
};
struct file *out[2];
int err;
err = create_pipe_files(out, 0);
if (err)
return err;
/* stdin, use the *write* end to the pipe to have an unreadable input */
err = replace_fd(0, out[1], 0);
if (err < 0) {
fput(out[0]);
fput(out[1]);
return err;
}
/* stdout */
err = replace_fd(1, out[1], 0);
if (err < 0) {
replace_fd(0, NULL, 0);
fput(out[0]);
fput(out[1]);
return err;
}
/* stderr */
err = replace_fd(2, out[1], 0);
if (err < 0) {
replace_fd(0, NULL, 0);
replace_fd(1, NULL, 0);
fput(out[0]);
fput(out[1]);
return err;
}
fput(out[1]);
ctx->out = out[0];
ctx->tgid = current->tgid;
set_fs_pwd(current->fs, &ctx->pwd);
kernel_sigaction(SIGKILL, SIG_DFL);
kernel_sigaction(SIGABRT, SIG_DFL);
complete(&ctx->setup_done);
ctx->exec_err = kernel_execve(ctx->executable, argv, NULL);
if (!ctx->exec_err)
return 0;
do_exit(0);
+}
+static size_t kunit_uapi_printk_subtest_lines(struct kunit *test, char *buf, size_t s) +{
const char *ptr = buf, *newline;
size_t n;
while (s) {
newline = strnchr(ptr, s, '\n');
if (!newline)
break;
n = newline - ptr + 1;
kunit_log(KERN_INFO, test, KUNIT_SUBSUBTEST_INDENT "%.*s", (int)n, ptr);
ptr += n;
s -= n;
}
memmove(buf, ptr, s);
return s;
+}
+static int kunit_uapi_forward_to_printk(struct kunit *test, struct file *output) +{
/*
* printk() automatically adds a newline after each message.
* Therefore only fully accumulated lines can be forwarded.
* Each line needs to fit into the buffer below.
*/
char buf[512];
size_t s = 0;
ssize_t n;
while (1) {
n = kernel_read(output, buf + s, sizeof(buf) - s, NULL);
if (n <= 0)
return n;
s = kunit_uapi_printk_subtest_lines(test, buf, s + n);
}
+}
+static void kunit_uapi_kill_pid(pid_t pid) +{
struct pid *p;
p = find_get_pid(pid);
kill_pid(p, SIGKILL, 1);
put_pid(p);
+}
+static int kunit_uapi_run_executable_in_mount(struct kunit *test, const char *executable,
struct vfsmount *mnt)
+{
struct kunit_uapi_user_mode_thread_ctx ctx = {
.setup_done = COMPLETION_INITIALIZER_ONSTACK(ctx.setup_done),
.executable = executable,
.pwd = {
.mnt = mnt,
.dentry = mnt->mnt_root,
},
};
int forward_err, wait_err, ret;
pid_t pid;
/* If SIGCHLD is ignored do_wait won't populate the status. */
kernel_sigaction(SIGCHLD, SIG_DFL);
pid = user_mode_thread(kunit_uapi_user_mode_thread_init, &ctx, SIGCHLD);
if (pid < 0) {
kernel_sigaction(SIGCHLD, SIG_IGN);
return pid;
}
wait_for_completion(&ctx.setup_done);
forward_err = kunit_uapi_forward_to_printk(test, ctx.out);
if (forward_err)
kunit_uapi_kill_pid(ctx.tgid);
wait_err = kernel_wait(ctx.tgid, &ret);
/* Restore default kernel sig handler */
kernel_sigaction(SIGCHLD, SIG_IGN);
if (ctx.exec_err)
return ctx.exec_err;
if (forward_err)
return forward_err;
if (wait_err < 0)
return wait_err;
return ret;
+}
+static int kunit_uapi_run_executable(struct kunit *test, const struct blob *executable) +{
const char *exe_name = kbasename(executable->path);
struct vfsmount *mnt;
int err;
mnt = kunit_uapi_mount_ramfs();
if (IS_ERR(mnt))
return PTR_ERR(mnt);
err = kunit_uapi_write_file(mnt, exe_name, 0755, executable->data, blob_size(executable));
if (!err)
err = kunit_uapi_run_executable_in_mount(test, exe_name, mnt);
kern_unmount(mnt);
return err;
+}
+void kunit_uapi_run_kselftest(struct kunit *test, const struct blob *executable) +{
u8 exit_code, exit_signal;
int err;
err = kunit_uapi_run_executable(test, executable);
if (err < 0)
KUNIT_FAIL(test, "Could not run test executable: %pe\n", ERR_PTR(err));
exit_code = err >> 8;
exit_signal = err & 0xff;
if (exit_signal)
KUNIT_FAIL(test, "kselftest exited with signal: %d\n", exit_signal);
else if (exit_code == KSFT_PASS)
; /* Noop */
else if (exit_code == KSFT_FAIL)
KUNIT_FAIL(test, "kselftest exited with code KSFT_FAIL\n");
else if (exit_code == KSFT_XPASS)
KUNIT_FAIL(test, "kselftest exited with code KSFT_XPASS\n");
else if (exit_code == KSFT_XFAIL)
; /* Noop */
else if (exit_code == KSFT_SKIP)
kunit_mark_skipped(test, "kselftest exited with code KSFT_SKIP\n");
else
KUNIT_FAIL(test, "kselftest exited with unknown exit code: %d\n", exit_code);
+} +EXPORT_SYMBOL_GPL(kunit_uapi_run_kselftest);
-- 2.49.0
On Fri, Jun 20, 2025 at 05:47:39PM +0800, David Gow wrote:
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Enable running UAPI tests as part of kunit. The selftests are embedded into the kernel image and their output is forwarded to kunit for unified reporting.
The implementation reuses parts of usermode drivers and usermode helpers. However these frameworks are not used directly as they make it impossible to retrieve a thread's exit code.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
It feels to me like there are three features hidden in here:
- KUnit helpers for manipulating vfs files
- A way of having KUnit tests run userspace helpers
- The full framework for writing/running whole tests in userspace.
It's really the first two which excite me personally most -- as they give us a sort-of inverse to the kselftest "helper module" paradigm -- so we can test things which are impossible to test from within kernelspace.
For me it is only the third feature that I really care about right now. But I do expect users for the first two to pop up at some point and these are obviously valid usecases.
So maybe those APIs should be exposed separately (so a test can be written mostly in kernel-space using the KUnit framework APIs, and just call out to a helper where needed). But I'm happy for them to stay private functions until we have a test which actually needs them.
Agreed, let's expose it when there are users.
Currently this depends on CONFIG_KUNIT=y as it uses some non-exported symbols around process management.
That's fine for now, IMHO, but will make it difficult to use this on, e.g., Red Hat setups, where CONFIG_KUNIT=m. Hopefully we can resolve this by exporting some of the symbols?
I'll try to use the new EXPORT_SYMBOL_GPL_FOR_MODULES() on these symbols and see what the maintainers say about it.
In general, I'm happy with the implementation here. The fs stuff probably needs a closer look from someone who knows the vfs better than me, though.
Nevertheless, Reviewed-by: David Gow davidgow@google.com
Thanks
Cheers, -- David
Documentation/dev-tools/kunit/api/index.rst | 5 + Documentation/dev-tools/kunit/api/uapi.rst | 12 ++ include/kunit/uapi.h | 24 +++ lib/kunit/Kconfig | 10 + lib/kunit/Makefile | 2 + lib/kunit/uapi.c | 287 ++++++++++++++++++++++++++++ 6 files changed, 340 insertions(+)
<snip>
Extend the example to show how to run a userspace executable.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- lib/kunit/Makefile | 9 ++++++++- lib/kunit/kunit-example-test.c | 15 +++++++++++++++ lib/kunit/kunit-example-uapi.c | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index dafa09bd4241c24d31c4c19edecb67bf724127d7..e406a31df1df834a87961663de0b7921b59481c2 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -30,4 +30,11 @@ obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o obj-$(CONFIG_KUNIT_TEST) += assert_test.o endif
-obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o +userprogs += kunit-example-uapi +kunit-example-uapi-userccflags := -static +kunit-example-uapi-nolibc := $(CONFIG_ARCH_HAS_NOLIBC) +blobs += kunit-example-uapi.blob.o + +obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-mod.o +kunit-example-mod-y += kunit-example-test.o +kunit-example-mod-$(CONFIG_KUNIT_UAPI) += kunit-example-uapi.blob.o diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index 3056d6bc705d0a8f196f0f4412e679dbb0e03114..b2681a6e047dfd6fea4a7cca60e81651d09c2eae 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -6,8 +6,11 @@ * Author: Brendan Higgins brendanhiggins@google.com */
+#include <linux/blob.h> + #include <kunit/test.h> #include <kunit/static_stub.h> +#include <kunit/uapi.h>
/* * This is the most fundamental element of KUnit, the test case. A test case @@ -277,6 +280,17 @@ static void example_slow_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 1 + 1, 2); }
+/* + * This test shows the usage of UAPI tests. + */ +static void example_uapi_test(struct kunit *test) +{ + if (IS_ENABLED(CONFIG_KUNIT_UAPI)) + kunit_uapi_run_kselftest(test, BLOB(kunit_example_uapi)); + else + kunit_skip(test, "CONFIG_KUNIT_UAPI is not enabled"); +} + /* * Here we make a list of all the test cases we want to add to the test suite * below. @@ -297,6 +311,7 @@ static struct kunit_case example_test_cases[] = { KUNIT_CASE(example_priv_test), KUNIT_CASE_PARAM(example_params_test, example_gen_params), KUNIT_CASE_SLOW(example_slow_test), + KUNIT_CASE(example_uapi_test), {} };
diff --git a/lib/kunit/kunit-example-uapi.c b/lib/kunit/kunit-example-uapi.c new file mode 100644 index 0000000000000000000000000000000000000000..4ce657050dd4a576632a41ca0309c4cb5134ce14 --- /dev/null +++ b/lib/kunit/kunit-example-uapi.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit Userspace example test. + * + * Copyright (C) 2025, Linutronix GmbH. + * Author: Thomas Weißschuh thomas.weissschuh@linutronix.de + * + * This is *userspace* code. + */ + +#include "../../tools/testing/selftests/kselftest.h" + +int main(void) +{ + ksft_print_header(); + ksft_set_plan(4); + ksft_test_result_pass("userspace test 1\n"); + ksft_test_result_pass("userspace test 2\n"); + ksft_test_result_skip("userspace test 3: some reason\n"); + ksft_test_result_pass("userspace test 4\n"); + ksft_finished(); +}
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Extend the example to show how to run a userspace executable.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
lib/kunit/Makefile | 9 ++++++++- lib/kunit/kunit-example-test.c | 15 +++++++++++++++ lib/kunit/kunit-example-uapi.c | 22 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index dafa09bd4241c24d31c4c19edecb67bf724127d7..e406a31df1df834a87961663de0b7921b59481c2 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -30,4 +30,11 @@ obj-$(CONFIG_KUNIT_TEST) += string-stream-test.o obj-$(CONFIG_KUNIT_TEST) += assert_test.o endif
-obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o +userprogs += kunit-example-uapi +kunit-example-uapi-userccflags := -static +kunit-example-uapi-nolibc := $(CONFIG_ARCH_HAS_NOLIBC) +blobs += kunit-example-uapi.blob.o
+obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-mod.o +kunit-example-mod-y += kunit-example-test.o +kunit-example-mod-$(CONFIG_KUNIT_UAPI) += kunit-example-uapi.blob.o diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index 3056d6bc705d0a8f196f0f4412e679dbb0e03114..b2681a6e047dfd6fea4a7cca60e81651d09c2eae 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -6,8 +6,11 @@
- Author: Brendan Higgins brendanhiggins@google.com
*/
+#include <linux/blob.h>
#include <kunit/test.h> #include <kunit/static_stub.h> +#include <kunit/uapi.h>
/*
- This is the most fundamental element of KUnit, the test case. A test case
@@ -277,6 +280,17 @@ static void example_slow_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 1 + 1, 2); }
+/*
- This test shows the usage of UAPI tests.
- */
+static void example_uapi_test(struct kunit *test) +{
if (IS_ENABLED(CONFIG_KUNIT_UAPI))
kunit_uapi_run_kselftest(test, BLOB(kunit_example_uapi));
else
kunit_skip(test, "CONFIG_KUNIT_UAPI is not enabled");
+}
/*
- Here we make a list of all the test cases we want to add to the test suite
- below.
@@ -297,6 +311,7 @@ static struct kunit_case example_test_cases[] = { KUNIT_CASE(example_priv_test), KUNIT_CASE_PARAM(example_params_test, example_gen_params), KUNIT_CASE_SLOW(example_slow_test),
KUNIT_CASE(example_uapi_test), {}
};
diff --git a/lib/kunit/kunit-example-uapi.c b/lib/kunit/kunit-example-uapi.c new file mode 100644 index 0000000000000000000000000000000000000000..4ce657050dd4a576632a41ca0309c4cb5134ce14 --- /dev/null +++ b/lib/kunit/kunit-example-uapi.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- KUnit Userspace example test.
- Copyright (C) 2025, Linutronix GmbH.
- Author: Thomas Weißschuh thomas.weissschuh@linutronix.de
- This is *userspace* code.
- */
+#include "../../tools/testing/selftests/kselftest.h"
+int main(void) +{
ksft_print_header();
ksft_set_plan(4);
ksft_test_result_pass("userspace test 1\n");
ksft_test_result_pass("userspace test 2\n");
ksft_test_result_skip("userspace test 3: some reason\n");
ksft_test_result_pass("userspace test 4\n");
ksft_finished();
+}
-- 2.49.0
UAPI selftests may expect a "normal" userspace environment. For example the normal kernel API pseudo-filesystems should be mounted. This could be done from kernel code but it is non-idiomatic.
Add a preinit userspace executable which performs these setup steps before running the final test executable. This preinit executable is only ever run from the kernel. Give it access to autoconf.h and kconfig.h to adapt itself to the tested kernel.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- lib/kunit/Makefile | 9 ++++++- lib/kunit/uapi-preinit.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/kunit/uapi.c | 11 +++++++-- 3 files changed, 80 insertions(+), 3 deletions(-)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index e406a31df1df834a87961663de0b7921b59481c2..19493ec320c61e2ccbb58e8b2e943e9a4ec447e2 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -12,7 +12,14 @@ kunit-objs += test.o \ device.o \ platform.o
-kunit-$(CONFIG_KUNIT_UAPI) += uapi.o +userprogs += uapi-preinit +uapi-preinit-nolibc := $(CONFIG_ARCH_HAS_NOLIBC) +uapi-preinit-userccflags += -static \ + -include include/generated/autoconf.h \ + -include $(srctree)/tools/include/linux/kconfig.h +blobs += uapi-preinit.blob.o +uapi-preinit.blob-symbol := kunit_uapi_preinit +kunit-$(CONFIG_KUNIT_UAPI) += uapi.o uapi-preinit.blob.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y) kunit-objs += debugfs.o diff --git a/lib/kunit/uapi-preinit.c b/lib/kunit/uapi-preinit.c new file mode 100644 index 0000000000000000000000000000000000000000..81182039965a8c93aebb2d5d76f4113bfef277a6 --- /dev/null +++ b/lib/kunit/uapi-preinit.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit Userspace environment setup. + * + * Copyright (C) 2025, Linutronix GmbH. + * Author: Thomas Weißschuh thomas.weissschuh@linutronix.de + * + * This is *userspace* code. + */ + +#include <sys/mount.h> +#include <sys/stat.h> + +#include "../../tools/testing/selftests/kselftest.h" + +static int setup_api_mount(const char *target, const char *fstype) +{ + int ret; + + ret = mkdir(target, 0755); + if (ret && errno != EEXIST) + return -errno; + + ret = mount("none", target, fstype, 0, NULL); + if (ret && errno != EBUSY) + return -errno; + + return 0; +} + +static void exit_failure(const char *stage, int err) +{ + /* If preinit fails synthesize a failed test report. */ + ksft_print_header(); + ksft_set_plan(1); + ksft_test_result_fail("Failed during test setup: %s: %s\n", stage, strerror(-err)); + ksft_finished(); +} + +int main(int argc, char **argv, char **envp) +{ + int ret; + + ret = setup_api_mount("/proc", "proc"); + if (ret) + exit_failure("mount /proc", ret); + + ret = setup_api_mount("/sys", "sysfs"); + if (ret) + exit_failure("mount /sys", ret); + + if (IS_ENABLED(CONFIG_DEVTMPFS)) { + ret = setup_api_mount("/dev", "devtmpfs"); + if (ret) + exit_failure("mount /dev", ret); + } + + ret = execve(argv[0], argv, envp); + if (ret) + exit_failure("execve", ret); + + return 0; +} diff --git a/lib/kunit/uapi.c b/lib/kunit/uapi.c index 121146dda533b3f90aca37c20bd0e7a1d20cb3b5..bccc081a6538507724c1ef340203cfd147170dc4 100644 --- a/lib/kunit/uapi.c +++ b/lib/kunit/uapi.c @@ -139,7 +139,7 @@ static int kunit_uapi_user_mode_thread_init(void *data) kernel_sigaction(SIGABRT, SIG_DFL);
complete(&ctx->setup_done); - ctx->exec_err = kernel_execve(ctx->executable, argv, NULL); + ctx->exec_err = kernel_execve(kbasename(BLOB(kunit_uapi_preinit)->path), argv, NULL); if (!ctx->exec_err) return 0; do_exit(0); @@ -239,6 +239,7 @@ static int kunit_uapi_run_executable_in_mount(struct kunit *test, const char *ex
static int kunit_uapi_run_executable(struct kunit *test, const struct blob *executable) { + const struct blob *preinit = BLOB(kunit_uapi_preinit); const char *exe_name = kbasename(executable->path); struct vfsmount *mnt; int err; @@ -247,7 +248,13 @@ static int kunit_uapi_run_executable(struct kunit *test, const struct blob *exec if (IS_ERR(mnt)) return PTR_ERR(mnt);
- err = kunit_uapi_write_file(mnt, exe_name, 0755, executable->data, blob_size(executable)); + err = kunit_uapi_write_file(mnt, kbasename(preinit->path), 0755, + preinit->data, + blob_size(preinit)); + + if (!err) + err = kunit_uapi_write_file(mnt, exe_name, 0755, + executable->data, blob_size(executable));
if (!err) err = kunit_uapi_run_executable_in_mount(test, exe_name, mnt);
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
UAPI selftests may expect a "normal" userspace environment. For example the normal kernel API pseudo-filesystems should be mounted. This could be done from kernel code but it is non-idiomatic.
Add a preinit userspace executable which performs these setup steps before running the final test executable. This preinit executable is only ever run from the kernel. Give it access to autoconf.h and kconfig.h to adapt itself to the tested kernel.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Looks good and works here.
Reviewed-by: David Gow davidgow@google.com
(Although, personally, _I wish_ it were more idiomatic to mount things from kernelspace.)
Cheers, -- David
lib/kunit/Makefile | 9 ++++++- lib/kunit/uapi-preinit.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/kunit/uapi.c | 11 +++++++-- 3 files changed, 80 insertions(+), 3 deletions(-)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index e406a31df1df834a87961663de0b7921b59481c2..19493ec320c61e2ccbb58e8b2e943e9a4ec447e2 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -12,7 +12,14 @@ kunit-objs += test.o \ device.o \ platform.o
-kunit-$(CONFIG_KUNIT_UAPI) += uapi.o +userprogs += uapi-preinit +uapi-preinit-nolibc := $(CONFIG_ARCH_HAS_NOLIBC) +uapi-preinit-userccflags += -static \
-include include/generated/autoconf.h \
-include $(srctree)/tools/include/linux/kconfig.h
+blobs += uapi-preinit.blob.o +uapi-preinit.blob-symbol := kunit_uapi_preinit +kunit-$(CONFIG_KUNIT_UAPI) += uapi.o uapi-preinit.blob.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y) kunit-objs += debugfs.o diff --git a/lib/kunit/uapi-preinit.c b/lib/kunit/uapi-preinit.c new file mode 100644 index 0000000000000000000000000000000000000000..81182039965a8c93aebb2d5d76f4113bfef277a6 --- /dev/null +++ b/lib/kunit/uapi-preinit.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- KUnit Userspace environment setup.
- Copyright (C) 2025, Linutronix GmbH.
- Author: Thomas Weißschuh thomas.weissschuh@linutronix.de
- This is *userspace* code.
- */
+#include <sys/mount.h> +#include <sys/stat.h>
+#include "../../tools/testing/selftests/kselftest.h"
+static int setup_api_mount(const char *target, const char *fstype) +{
int ret;
ret = mkdir(target, 0755);
if (ret && errno != EEXIST)
return -errno;
ret = mount("none", target, fstype, 0, NULL);
if (ret && errno != EBUSY)
return -errno;
return 0;
+}
+static void exit_failure(const char *stage, int err) +{
/* If preinit fails synthesize a failed test report. */
ksft_print_header();
ksft_set_plan(1);
ksft_test_result_fail("Failed during test setup: %s: %s\n", stage, strerror(-err));
ksft_finished();
+}
+int main(int argc, char **argv, char **envp) +{
int ret;
ret = setup_api_mount("/proc", "proc");
if (ret)
exit_failure("mount /proc", ret);
ret = setup_api_mount("/sys", "sysfs");
if (ret)
exit_failure("mount /sys", ret);
if (IS_ENABLED(CONFIG_DEVTMPFS)) {
ret = setup_api_mount("/dev", "devtmpfs");
if (ret)
exit_failure("mount /dev", ret);
}
ret = execve(argv[0], argv, envp);
if (ret)
exit_failure("execve", ret);
return 0;
+} diff --git a/lib/kunit/uapi.c b/lib/kunit/uapi.c index 121146dda533b3f90aca37c20bd0e7a1d20cb3b5..bccc081a6538507724c1ef340203cfd147170dc4 100644 --- a/lib/kunit/uapi.c +++ b/lib/kunit/uapi.c @@ -139,7 +139,7 @@ static int kunit_uapi_user_mode_thread_init(void *data) kernel_sigaction(SIGABRT, SIG_DFL);
complete(&ctx->setup_done);
ctx->exec_err = kernel_execve(ctx->executable, argv, NULL);
ctx->exec_err = kernel_execve(kbasename(BLOB(kunit_uapi_preinit)->path), argv, NULL); if (!ctx->exec_err) return 0; do_exit(0);
@@ -239,6 +239,7 @@ static int kunit_uapi_run_executable_in_mount(struct kunit *test, const char *ex
static int kunit_uapi_run_executable(struct kunit *test, const struct blob *executable) {
const struct blob *preinit = BLOB(kunit_uapi_preinit); const char *exe_name = kbasename(executable->path); struct vfsmount *mnt; int err;
@@ -247,7 +248,13 @@ static int kunit_uapi_run_executable(struct kunit *test, const struct blob *exec if (IS_ERR(mnt)) return PTR_ERR(mnt);
err = kunit_uapi_write_file(mnt, exe_name, 0755, executable->data, blob_size(executable));
err = kunit_uapi_write_file(mnt, kbasename(preinit->path), 0755,
preinit->data,
blob_size(preinit));
if (!err)
err = kunit_uapi_write_file(mnt, exe_name, 0755,
executable->data, blob_size(executable)); if (!err) err = kunit_uapi_run_executable_in_mount(test, exe_name, mnt);
-- 2.49.0
Show that the selftests are executed from a fairly "normal" userspace context.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de --- lib/kunit/kunit-example-uapi.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/lib/kunit/kunit-example-uapi.c b/lib/kunit/kunit-example-uapi.c index 4ce657050dd4a576632a41ca0309c4cb5134ce14..5e7a0f3b68f182c42b03e667567e66f02d8c2b86 100644 --- a/lib/kunit/kunit-example-uapi.c +++ b/lib/kunit/kunit-example-uapi.c @@ -8,13 +8,45 @@ * This is *userspace* code. */
+#include <fcntl.h> +#include <unistd.h> +#include <string.h> + #include "../../tools/testing/selftests/kselftest.h"
+static void test_procfs(void) +{ + char buf[256]; + ssize_t r; + int fd; + + fd = open("/proc/self/comm", O_RDONLY); + if (fd == -1) { + ksft_test_result_fail("procfs: open() failed: %s\n", strerror(errno)); + return; + } + + r = read(fd, buf, sizeof(buf)); + close(fd); + + if (r == -1) { + ksft_test_result_fail("procfs: read() failed: %s\n", strerror(errno)); + return; + } + + if (r != 16 || strncmp("kunit-example-u\n", buf, 16) != 0) { + ksft_test_result_fail("procfs: incorrect comm\n"); + return; + } + + ksft_test_result_pass("procfs\n"); +} + int main(void) { ksft_print_header(); ksft_set_plan(4); - ksft_test_result_pass("userspace test 1\n"); + test_procfs(); ksft_test_result_pass("userspace test 2\n"); ksft_test_result_skip("userspace test 3: some reason\n"); ksft_test_result_pass("userspace test 4\n");
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Show that the selftests are executed from a fairly "normal" userspace context.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This is good. I'm not 100% sure the example test is the best place for it, though.
Would it make more sense to either have this: - in the main kunit test (since it's really _verifying_ the KUnit environment, rather than documenting it) - in a separate kunit-uapi test (if we want to keep some separation between the UAPI and entirely in-kernel tests) - in a separate procfs test (since it tests procfs functionality as much as it's testing the KUnit environment)
Personally, my gut feeling is the main kunit-test is the best place for this, even if it means spinning up a separate file is best here.
As for the actual implementation, though, that looks fine to me. A few small comments below, but nothing particularly important.
Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
lib/kunit/kunit-example-uapi.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/lib/kunit/kunit-example-uapi.c b/lib/kunit/kunit-example-uapi.c index 4ce657050dd4a576632a41ca0309c4cb5134ce14..5e7a0f3b68f182c42b03e667567e66f02d8c2b86 100644 --- a/lib/kunit/kunit-example-uapi.c +++ b/lib/kunit/kunit-example-uapi.c @@ -8,13 +8,45 @@
- This is *userspace* code.
*/
+#include <fcntl.h> +#include <unistd.h> +#include <string.h>
#include "../../tools/testing/selftests/kselftest.h"
+static void test_procfs(void) +{
char buf[256];
ssize_t r;
int fd;
fd = open("/proc/self/comm", O_RDONLY);
if (fd == -1) {
ksft_test_result_fail("procfs: open() failed: %s\n", strerror(errno));
return;
}
r = read(fd, buf, sizeof(buf));
close(fd);
if (r == -1) {
ksft_test_result_fail("procfs: read() failed: %s\n", strerror(errno));
return;
}
Do we want to use TASK_COMM_LEN rather than hardcoding 16 below?
(And, if so, do we need something more complicated in case it's not 16?)
if (r != 16 || strncmp("kunit-example-u\n", buf, 16) != 0) {
ksft_test_result_fail("procfs: incorrect comm\n");
return;
}
ksft_test_result_pass("procfs\n");
+}
int main(void) { ksft_print_header(); ksft_set_plan(4);
ksft_test_result_pass("userspace test 1\n");
test_procfs(); ksft_test_result_pass("userspace test 2\n"); ksft_test_result_skip("userspace test 3: some reason\n"); ksft_test_result_pass("userspace test 4\n");
-- 2.49.0
On Fri, Jun 20, 2025 at 05:48:07PM +0800, David Gow wrote:
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Show that the selftests are executed from a fairly "normal" userspace context.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
This is good. I'm not 100% sure the example test is the best place for it, though.
Would it make more sense to either have this:
- in the main kunit test (since it's really _verifying_ the KUnit
environment, rather than documenting it)
- in a separate kunit-uapi test (if we want to keep some separation
between the UAPI and entirely in-kernel tests)
- in a separate procfs test (since it tests procfs functionality as
much as it's testing the KUnit environment)
Originally this change was really meant as an example for users. But moving it into the main kunit test probably makes more sense.
Personally, my gut feeling is the main kunit-test is the best place for this, even if it means spinning up a separate file is best here.
Ack.
As for the actual implementation, though, that looks fine to me. A few small comments below, but nothing particularly important.
Reviewed-by: David Gow davidgow@google.com
Cheers, -- David
lib/kunit/kunit-example-uapi.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/lib/kunit/kunit-example-uapi.c b/lib/kunit/kunit-example-uapi.c index 4ce657050dd4a576632a41ca0309c4cb5134ce14..5e7a0f3b68f182c42b03e667567e66f02d8c2b86 100644 --- a/lib/kunit/kunit-example-uapi.c +++ b/lib/kunit/kunit-example-uapi.c @@ -8,13 +8,45 @@
- This is *userspace* code.
*/
+#include <fcntl.h> +#include <unistd.h> +#include <string.h>
#include "../../tools/testing/selftests/kselftest.h"
+static void test_procfs(void) +{
char buf[256];
ssize_t r;
int fd;
fd = open("/proc/self/comm", O_RDONLY);
if (fd == -1) {
ksft_test_result_fail("procfs: open() failed: %s\n", strerror(errno));
return;
}
r = read(fd, buf, sizeof(buf));
close(fd);
if (r == -1) {
ksft_test_result_fail("procfs: read() failed: %s\n", strerror(errno));
return;
}
Do we want to use TASK_COMM_LEN rather than hardcoding 16 below?
(And, if so, do we need something more complicated in case it's not 16?)
TASK_COMM_LEN is not part of the UAPI headers. But I don't think it can ever change.
if (r != 16 || strncmp("kunit-example-u\n", buf, 16) != 0) {
ksft_test_result_fail("procfs: incorrect comm\n");
return;
}
ksft_test_result_pass("procfs\n");
+}
int main(void) { ksft_print_header(); ksft_set_plan(4);
ksft_test_result_pass("userspace test 1\n");
test_procfs(); ksft_test_result_pass("userspace test 2\n"); ksft_test_result_skip("userspace test 3: some reason\n"); ksft_test_result_pass("userspace test 4\n");
-- 2.49.0
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Currently testing of userspace and in-kernel API use two different frameworks. kselftests for the userspace ones and Kunit for the in-kernel ones. Besides their different scopes, both have different strengths and limitations:
Kunit:
- Tests are normal kernel code.
- They use the regular kernel toolchain.
- They can be packaged and distributed as modules conveniently.
Kselftests:
- Tests are normal userspace code
- They need a userspace toolchain. A kernel cross toolchain is likely not enough.
- A fair amout of userland is required to run the tests, which means a full distro or handcrafted rootfs.
- There is no way to conveniently package and run kselftests with a given kernel image.
- The kselftests makefiles are not as powerful as regular kbuild. For example they are missing proper header dependency tracking or more complex compiler option modifications.
Therefore kunit is much easier to run against different kernel configurations and architectures. This series aims to combine kselftests and kunit, avoiding both their limitations. It works by compiling the userspace kselftests as part of the regular kernel build, embedding them into the kunit kernel or module and executing them from there. If the kernel toolchain is not fit to produce userspace because of a missing libc, the kernel's own nolibc can be used instead. The structured TAP output from the kselftest is integrated into the kunit KTAP output transparently, the kunit parser can parse the combined logs together.
Further room for improvements:
- Call each test in its completely dedicated namespace
- Handle additional test files besides the test executable through archives. CPIO, cramfs, etc.
- Compatibility with kselftest_harness.h (in progress)
- Expose the blobs in debugfs
- Provide some convience wrappers around compat userprogs
- Figure out a migration path/coexistence solution for kunit UAPI and tools/testing/selftests/
Output from the kunit example testcase, note the output of "example_uapi_tests".
$ ./tools/testing/kunit/kunit.py run --kunitconfig lib/kunit example ... Running tests with: $ .kunit/linux kunit.filter_glob=example kunit.enable=1 mem=1G console=tty kunit_shutdown=halt [11:53:53] ================== example (10 subtests) =================== [11:53:53] [PASSED] example_simple_test [11:53:53] [SKIPPED] example_skip_test [11:53:53] [SKIPPED] example_mark_skipped_test [11:53:53] [PASSED] example_all_expect_macros_test [11:53:53] [PASSED] example_static_stub_test [11:53:53] [PASSED] example_static_stub_using_fn_ptr_test [11:53:53] [PASSED] example_priv_test [11:53:53] =================== example_params_test =================== [11:53:53] [SKIPPED] example value 3 [11:53:53] [PASSED] example value 2 [11:53:53] [PASSED] example value 1 [11:53:53] [SKIPPED] example value 0 [11:53:53] =============== [PASSED] example_params_test =============== [11:53:53] [PASSED] example_slow_test [11:53:53] ======================= (4 subtests) ======================= [11:53:53] [PASSED] procfs [11:53:53] [PASSED] userspace test 2 [11:53:53] [SKIPPED] userspace test 3: some reason [11:53:53] [PASSED] userspace test 4 [11:53:53] ================ [PASSED] example_uapi_test ================ [11:53:53] ===================== [PASSED] example ===================== [11:53:53] ============================================================ [11:53:53] Testing complete. Ran 16 tests: passed: 11, skipped: 5 [11:53:53] Elapsed time: 67.543s total, 1.823s configuring, 65.655s building, 0.058s running
Based on v6.15-rc1.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Changes in v3:
- Reintroduce CONFIG_CC_CAN_LINK_STATIC
- Enable CONFIG_ARCH_HAS_NOLIBC for m68k and SPARC
- Properly handle 'clean' target for userprogs
- Use ramfs over tmpfs to reduce dependencies
- Inherit userprogs byte order and ABI from kernel
- Drop now unnecessary "#ifndef NOLIBC"
- Pick up review tags
- Drop usage of __private in blob.h, sparse complains and it is not really necessary
- Fix execution on loongarch when using clang
- Drop userprogs libgcc handling, it was ugly and is not yet necessary
- Link to v2: https://lore.kernel.org/r/20250407-kunit-kselftests-v2-0-454114e287fd@linutr...
Changes in v2:
- Rebase onto v6.15-rc1
- Add documentation and kernel docs
- Resolve invalid kconfig breakages
- Drop already applied patch "kbuild: implement CONFIG_HEADERS_INSTALL for Usermode Linux"
- Drop userprogs CONFIG_WERROR integration, it doesn't need to be part of this series
- Replace patch prefix "kconfig" with "kbuild"
- Rename kunit_uapi_run_executable() to kunit_uapi_run_kselftest()
- Generate private, conflict-free symbols in the blob framework
- Handle kselftest exit codes
- Handle SIGABRT
- Forward output also to kunit debugfs log
- Install a fd=0 stdin filedescriptor
- Link to v1: https://lore.kernel.org/r/20250217-kunit-kselftests-v1-0-42b4524c3b0a@linutr...
Thanks very much for persisting with this, and sorry for the delayed response.
I've taken quite a liking to it: it'd definitely have made my life easier more than once.
As a more general wrapping of selftests in KUnit tests, I do think that there's still some risk of confusion as to when a KUnit UAPI test makes sense versus a simple selftest. The UAPI tests are definitely (IMO) easier to build and run, but won't be easier to debug, or to run on an existing, non-test system as a part of troubleshooting (which has been a complaint when selftests have been ported to KUnit in the past).
Nevertheless, I'm pretty happy to have this be a part of KUnit, though I have three slight reservations: 1. There's no real _user_ of this yet -- save for the small test of /proc/self/comm in the example -- I'd like to see a real-world test using this. 2. There's a fair bit of complexity here, and we're already a bit behind with KUnit reviews. I'd love it if you could commit to helping maintain the KUnit parts of this in MAINTAINERS. 3. We need to make sure that there's a clear understanding of when to use this, versus in-kernel KUnit tests, versus kselftest. This'll probably involve (a) making sure Shuah is on board -- or at least not strongly opposed, and (b) updating Documentation/dev-tools/testing-overview.rst.
But thanks very much -- it's working well in my testing here, and running the tests is very pleasant.
Cheers, -- David
Thomas Weißschuh (16): kbuild: userprogs: avoid duplicating of flags inherited from kernel kbuild: userprogs: also inherit byte order and ABI from kernel init: re-add CONFIG_CC_CAN_LINK_STATIC kbuild: userprogs: add nolibc support kbuild: introduce CONFIG_ARCH_HAS_NOLIBC kbuild: doc: add label for userprogs section kbuild: introduce blob framework kunit: tool: Add test for nested test result reporting kunit: tool: Don't overwrite test status based on subtest counts kunit: tool: Parse skipped tests from kselftest.h kunit: Always descend into kunit directory during build kunit: qemu_configs: loongarch: Enable LSX/LSAX kunit: Introduce UAPI testing framework kunit: uapi: Add example for UAPI tests kunit: uapi: Introduce preinit executable kunit: uapi: Validate usability of /proc
Documentation/dev-tools/kunit/api/index.rst | 5 + Documentation/dev-tools/kunit/api/uapi.rst | 12 + Documentation/kbuild/makefiles.rst | 38 ++- MAINTAINERS | 2 + Makefile | 7 +- include/kunit/uapi.h | 24 ++ include/linux/blob.h | 31 +++ init/Kconfig | 7 + lib/Makefile | 4 - lib/kunit/Kconfig | 10 + lib/kunit/Makefile | 20 +- lib/kunit/kunit-example-test.c | 15 ++ lib/kunit/kunit-example-uapi.c | 54 ++++ lib/kunit/uapi-preinit.c | 63 +++++ lib/kunit/uapi.c | 294 +++++++++++++++++++++ scripts/Makefile.blobs | 19 ++ scripts/Makefile.build | 6 + scripts/Makefile.clean | 2 +- scripts/Makefile.userprogs | 13 +- scripts/blob-wrap.c | 27 ++ tools/include/nolibc/Kconfig.nolibc | 15 ++ tools/testing/kunit/kunit_parser.py | 13 +- tools/testing/kunit/kunit_tool_test.py | 9 + tools/testing/kunit/qemu_configs/loongarch.py | 2 + .../test_is_test_passed-failure-nested.log | 10 + .../test_data/test_is_test_passed-kselftest.log | 3 +- 26 files changed, 686 insertions(+), 19 deletions(-)
base-commit: f07a3558c4a5d76f3fea004075e5151c4516d055 change-id: 20241015-kunit-kselftests-56273bc40442
Best regards,
Thomas Weißschuh thomas.weissschuh@linutronix.de
On Fri, Jun 20, 2025 at 05:37:24PM +0800, David Gow wrote:
On Wed, 11 Jun 2025 at 15:38, Thomas Weißschuh thomas.weissschuh@linutronix.de wrote:
Currently testing of userspace and in-kernel API use two different frameworks. kselftests for the userspace ones and Kunit for the in-kernel ones. Besides their different scopes, both have different strengths and limitations:
Kunit:
- Tests are normal kernel code.
- They use the regular kernel toolchain.
- They can be packaged and distributed as modules conveniently.
Kselftests:
- Tests are normal userspace code
- They need a userspace toolchain. A kernel cross toolchain is likely not enough.
- A fair amout of userland is required to run the tests, which means a full distro or handcrafted rootfs.
- There is no way to conveniently package and run kselftests with a given kernel image.
- The kselftests makefiles are not as powerful as regular kbuild. For example they are missing proper header dependency tracking or more complex compiler option modifications.
Therefore kunit is much easier to run against different kernel configurations and architectures. This series aims to combine kselftests and kunit, avoiding both their limitations. It works by compiling the userspace kselftests as part of the regular kernel build, embedding them into the kunit kernel or module and executing them from there. If the kernel toolchain is not fit to produce userspace because of a missing libc, the kernel's own nolibc can be used instead. The structured TAP output from the kselftest is integrated into the kunit KTAP output transparently, the kunit parser can parse the combined logs together.
Further room for improvements:
- Call each test in its completely dedicated namespace
- Handle additional test files besides the test executable through archives. CPIO, cramfs, etc.
- Compatibility with kselftest_harness.h (in progress)
- Expose the blobs in debugfs
- Provide some convience wrappers around compat userprogs
- Figure out a migration path/coexistence solution for kunit UAPI and tools/testing/selftests/
Output from the kunit example testcase, note the output of "example_uapi_tests".
$ ./tools/testing/kunit/kunit.py run --kunitconfig lib/kunit example ... Running tests with: $ .kunit/linux kunit.filter_glob=example kunit.enable=1 mem=1G console=tty kunit_shutdown=halt [11:53:53] ================== example (10 subtests) =================== [11:53:53] [PASSED] example_simple_test [11:53:53] [SKIPPED] example_skip_test [11:53:53] [SKIPPED] example_mark_skipped_test [11:53:53] [PASSED] example_all_expect_macros_test [11:53:53] [PASSED] example_static_stub_test [11:53:53] [PASSED] example_static_stub_using_fn_ptr_test [11:53:53] [PASSED] example_priv_test [11:53:53] =================== example_params_test =================== [11:53:53] [SKIPPED] example value 3 [11:53:53] [PASSED] example value 2 [11:53:53] [PASSED] example value 1 [11:53:53] [SKIPPED] example value 0 [11:53:53] =============== [PASSED] example_params_test =============== [11:53:53] [PASSED] example_slow_test [11:53:53] ======================= (4 subtests) ======================= [11:53:53] [PASSED] procfs [11:53:53] [PASSED] userspace test 2 [11:53:53] [SKIPPED] userspace test 3: some reason [11:53:53] [PASSED] userspace test 4 [11:53:53] ================ [PASSED] example_uapi_test ================ [11:53:53] ===================== [PASSED] example ===================== [11:53:53] ============================================================ [11:53:53] Testing complete. Ran 16 tests: passed: 11, skipped: 5 [11:53:53] Elapsed time: 67.543s total, 1.823s configuring, 65.655s building, 0.058s running
Based on v6.15-rc1.
Signed-off-by: Thomas Weißschuh thomas.weissschuh@linutronix.de
Changes in v3:
- Reintroduce CONFIG_CC_CAN_LINK_STATIC
- Enable CONFIG_ARCH_HAS_NOLIBC for m68k and SPARC
- Properly handle 'clean' target for userprogs
- Use ramfs over tmpfs to reduce dependencies
- Inherit userprogs byte order and ABI from kernel
- Drop now unnecessary "#ifndef NOLIBC"
- Pick up review tags
- Drop usage of __private in blob.h, sparse complains and it is not really necessary
- Fix execution on loongarch when using clang
- Drop userprogs libgcc handling, it was ugly and is not yet necessary
- Link to v2: https://lore.kernel.org/r/20250407-kunit-kselftests-v2-0-454114e287fd@linutr...
Changes in v2:
- Rebase onto v6.15-rc1
- Add documentation and kernel docs
- Resolve invalid kconfig breakages
- Drop already applied patch "kbuild: implement CONFIG_HEADERS_INSTALL for Usermode Linux"
- Drop userprogs CONFIG_WERROR integration, it doesn't need to be part of this series
- Replace patch prefix "kconfig" with "kbuild"
- Rename kunit_uapi_run_executable() to kunit_uapi_run_kselftest()
- Generate private, conflict-free symbols in the blob framework
- Handle kselftest exit codes
- Handle SIGABRT
- Forward output also to kunit debugfs log
- Install a fd=0 stdin filedescriptor
- Link to v1: https://lore.kernel.org/r/20250217-kunit-kselftests-v1-0-42b4524c3b0a@linutr...
Thanks very much for persisting with this, and sorry for the delayed response.
Thanks for the review.
I've taken quite a liking to it: it'd definitely have made my life easier more than once.
As a more general wrapping of selftests in KUnit tests, I do think that there's still some risk of confusion as to when a KUnit UAPI test makes sense versus a simple selftest. The UAPI tests are definitely (IMO) easier to build and run, but won't be easier to debug, or to run on an existing, non-test system as a part of troubleshooting (which has been a complaint when selftests have been ported to KUnit in the past).
The tests I am currently running with this framework are actually real kselftests. They primarily live in tools/testing/selftests/ but I have a wrapper in the "real" source tree and also build as part of KUnit. This gives the advantages of both systems. FWIW, the KUnit UAPI tests still exist as regular binaries in the output tree and can also be used on their own.
Nevertheless, I'm pretty happy to have this be a part of KUnit, though I have three slight reservations:
- There's no real _user_ of this yet -- save for the small test of
/proc/self/comm in the example -- I'd like to see a real-world test using this.
As mentioned before I am using this with real kselftests. So far I have plugged in:
* all of tools/testing/selftests/vDSO/ * all of tools/testing/selftests/timens/ * a bit of tools/testing/selftests/x86/ * a bit of tools/testing/selftests/timers/
The selftests require a few small changes but these are mostly to resolve warnings introduced by the more struct userprogs CFLAGS. Or make the tests compatible with nolibc by changing syscall constants from the generic SYS_foo to the Linux UAPI variant __NR_foo.
If you want to take a look at the (very WIP) code: https://git.kernel.org/pub/scm/linux/kernel/git/thomas.weissschuh/linux.git/...
In addition, some extensions to nolibc and the vDSO selftest libraries were necessary. But all of that has been upstreamed already.
- There's a fair bit of complexity here, and we're already a bit
behind with KUnit reviews. I'd love it if you could commit to helping maintain the KUnit parts of this in MAINTAINERS.
Ack. I'll add this in the next revision.
- We need to make sure that there's a clear understanding of when to
use this, versus in-kernel KUnit tests, versus kselftest. This'll probably involve (a) making sure Shuah is on board -- or at least not strongly opposed, and (b) updating Documentation/dev-tools/testing-overview.rst.
As mentioned above, I think for most testcases both can be used from the same codebase. So far I don't have any other requirements, although I am fairly sure those will come up at some point. Let's go there when necessary.
I talked to Shuah about the proposal before and she had a positive reaction. But I still would be very happy to get more feedback from her, also about the posted patches.
I'll also update testing-overview.rst.
But thanks very much -- it's working well in my testing here, and running the tests is very pleasant.
Good to hear!
<snip>
linux-kselftest-mirror@lists.linaro.org