|
|
Subscribe / Log in / New account

Toward the unification of kselftests and KUnit

By Jonathan Corbet
July 8, 2025
The kernel project, for many years, lacked a formal testing setup; it was often joked that testing was the project's main reason for keeping users around. While many types of kernel testing can only be done in the presence of specific hardware, there are other parts of the kernel that could be more widely tested. Over time, though, the kernel has gained two separate testing frameworks and a growing body of automated tests to go with them. These two frameworks — kselftests and KUnit — take different approaches to the testing problem; now this patch series from Thomas Weißschuh aims to bring them together.

Kselftests and KUnit

Kselftests was first added by Frederic Weisbecker in 2012, with the first test being focused on the handling of breakpoints on the x86 architecture. These self tests run in user space, exercising the normal kernel system-call interface. Over the years, kselftests has grown a test-harness structure based around the Test Anything Protocol (TAP) and a set of functions, macros, and makefile support for the creation of tests.

In current kernels, the kselftests directory has over 100 subdirectories, each containing tests for a specific subsystem. There are tests focused on system calls, architecture support, sysctl knobs, kernel behavior (such as the sealing of system mappings), /proc files, and a handful of device drivers, among other things. While this set of tests is not (and probably can never be) complete, a successful kselftests run is enough to give confidence that significant parts of the kernel are working as expected.

KUnit is a different beast; its tests are built as kernel modules and run within the kernel itself. That gives KUnit tests the ability to verify the operation of individual kernel functions that are not reachable from user space. KUnit tests can be loaded into a running kernel and run at any time; they can also be built into the kernel and run automatically at every boot. KUnit, too, provides a set of supporting functions to make tests easier; it is built around a version of TAP called KTAP.

KUnit was added in 2019 by Felix Guo. It, too, has accumulated a growing set of tests over the years; the DRM (graphics) subsystem has quite a few tests, and there is a significant set of tests covering the generic support routines found in the kernel's lib/ directory. See, for example, this set of hash-table tests.

The kernel's two test suites are aimed at different aspects of the testing problem, so it is not entirely surprising that they ended up with different designs. Kselftests pokes at the kernel from the outside, ensuring that its user-space ABI behaves as expected, while KUnit works on the inside, testing components that are not reachable from user space. This separation has allowed each test suite to grow within its targeted space, but it has also led to some occasional frustrations.

Perhaps the twain shall meet

Weißschuh's work appears to be driven by some specific needs felt by embedded developers. The KUnit tests are relatively easy to run on a new embedded system; they can be built into the kernel that is run on those systems, and require no extra support there. The kselftests tests, instead, need a fully working user space on the target system to run. Both that user space and the tests themselves must be loaded onto the target separately from the kernel, making the testing task harder in general.

The solution that Weißschuh is pursuing is to integrate the two test suites and, in particular, to make it possible to build the kselftests into the kernel and run them from the KUnit framework. A successful solution would allow a kernel and a full set of tests to be loaded onto a target system as a single binary, easing the process of getting the kernel into a fully working state on a new system. There are, naturally, a few obstacles that need to be overcome to get there.

Kselftests are designed to be run as standalone programs, not as functions within kernel modules like KUnit tests. Weißschuh's series continues to build those tests separately, but the resulting binaries are linked into the new kunit-uapi module, which then runs them in a separate kernel thread. This code is based on the user-mode-helper functionality that was first introduced as part of bpfilter, though a number of changes needed to be made.

A user-space program expects to have a C library available to it; that is part of the user-space setup that Weißschuh is trying to avoid having to do. Fortunately, since the 5.1 release, the kernel happens to have its own minimal C-library implementation in the form of nolibc. It is not a complete implementation, but it contains enough support to run much of the kselftest suite. The building of those tests, though, had to be modified to use nolibc rather than the system's C library.

The kunit-uapi module has to do the rest of the work of creating a sufficient environment for each kselftest, as well as actually running the tests. That requires access to functionality that the kernel does not currently export to modules — kernel functions like kernel_execve(), replace_fd(), create_pipe_files(), and do_exit(). The series includes a patch exporting those symbols, among others. The export is limited to the kunit-uapi module using the new EXPORT_SYMBOL_GPL_FOR_MODULES() macro that was just added for 6.16; it is the current form of the restricted-namespace feature that was first proposed in 2024.

The newly exported functions are used to set up the standard input, output, and error streams for each test (the input is, for all practical purposes, set to /dev/null), mount /proc, run the test itself, and clean up afterward. The TAP output from the tests is passed back into the KUnit framework to be reported with the rest of the test results.

All of this work sets the stage for packaging the existing kselftests, but stops short of that goal. Instead, the only tests enabled at the end of the series are a simple example test and a test verifying the /proc mount. Bringing the existing tests in will require adding a bit of glue for each, causing it to be embedded in the loadable module and run at the right time. A more automated way of incorporating the tests is on the wishlist for the future, but does not exist now.

The incorporation of the actual tests may be waiting for a consensus on the surrounding framework. As of this writing, the series is in its fourth revision, and the most significant concerns would appear to have been addressed; the most recent comments are mostly focused on relatively small issues. Assuming that the biggest problems nearly been overcome, the core framework may find its way into the mainline relatively quickly; the job of integrating all of the actual tests will likely be next.

Index entries for this article
KernelDevelopment tools/Testing


to post comments

What about the opposite?

Posted Jul 9, 2025 7:08 UTC (Wed) by taladar (subscriber, #68407) [Link] (3 responses)

What about the opposite use-case? Is there a way to automatically run the kernel you are working on as a developer in a VM to run the KUnit tests easily without rebooting your development system all the time? Assuming you are working on some feature that doesn't require a specific bit of hardware of course or where the hardware could be exposed to the VM.

What about the opposite?

Posted Jul 9, 2025 9:25 UTC (Wed) by kakashir (subscriber, #165721) [Link]

It's same to just run the code under developing + KUnit in a VM. I'm not sure your key point here, do it automatically ?

What about the opposite?

Posted Jul 9, 2025 10:01 UTC (Wed) by t-8ch (subscriber, #90907) [Link] (1 responses)

That is already supported by KUnit. Take a look at tools/testing/kunit/kunit.py.

What about the opposite?

Posted Jul 10, 2025 17:20 UTC (Thu) by jgg (subscriber, #55211) [Link]

tools/testing/kunit/kunit.py is great, I think it is really under appreciated!

Possibly reduced coverage

Posted Jul 9, 2025 11:42 UTC (Wed) by metan (subscriber, #74107) [Link] (2 responses)

I would say that this partially defeats the purpose of the testing since one of the goals kernel testing has is to make sure that kernel works well together with order of magnitude more complex libc in user space. It's probably good enough for quick CI tests though.

Possibly reduced coverage

Posted Jul 9, 2025 13:33 UTC (Wed) by t-8ch (subscriber, #90907) [Link]

This framework is also capable of running full libc executables.
These will be larger and a full userspace toolchain is needed to build them.

Furthermore the existing kselftest framework won't go away. So test coverage should strictly increase.

Possibly reduced coverage

Posted Jul 10, 2025 14:38 UTC (Thu) by tbird20d (subscriber, #1901) [Link]

I agree with the concern here. To the degree that this adds (in net terms) to the test results pool, by making the use of kselftest easier, then this will be a good thing. But I do worry that this particular test configuration will miss a lot of code paths (in either the kernel or user space), that a more traditional (ie real-world) kselftest configuration would touch. Overall, if it catches bugs, it's most likely a net gain. I suspect that people who have already figured out how to overcome the obstacles to running kselftest that this patch set addresses are unlikely to back off and test in more limited configurations (which could potentially lead to less test coverage). But it's something to watch out for.

A tradeoff in testing attributes

Posted Jul 10, 2025 14:56 UTC (Thu) by tbird20d (subscriber, #1901) [Link]

While it will be nice to make running kselftest easier, I hope this does not end up imposing requirements on test developers that make that aspect of the testing lifecycle harder. That is, right now you can write a kselftest as a simple shell script or a python program. A quick perusal of tools/testing/selftest shows that over 600 of the existing selftests are shell scripts. I would hope that introducing a new test configuration for kselftests does not introduce an incentive
to avoid wirting and using this kind of test.

Shell scripts have the entire set of user-space programs and libraries at their disposal. Often, they are just glue doing sequences of operations where the kernel access and interactions are done by the programs that would be used in real production environments. These will be hard to utilize without a full distribution in place (or some careful analysis to create a limited distribution), as well as needing a shell script interpreter inside the kernel.

It will be interesting to see how many existing kselftest tests will be amenable to being run in the limited environment of a kernel module.


Copyright © 2025, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds