summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-12 12:13:01 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-12 12:13:01 -0800
commit136114e0abf03005e182d75761ab694648e6d388 (patch)
tree05c61b103fc9cb72a7cae99680a4b524347e9616 /lib
parent4cff5c05e076d2ee4e34122aa956b84a2eaac587 (diff)
parent0dddf20b4fd4afd59767acc144ad4da60259f21f (diff)
Merge tag 'mm-nonmm-stable-2026-02-12-10-48' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Pull non-MM updates from Andrew Morton: - "ocfs2: give ocfs2 the ability to reclaim suballocator free bg" saves disk space by teaching ocfs2 to reclaim suballocator block group space (Heming Zhao) - "Add ARRAY_END(), and use it to fix off-by-one bugs" adds the ARRAY_END() macro and uses it in various places (Alejandro Colomar) - "vmcoreinfo: support VMCOREINFO_BYTES larger than PAGE_SIZE" makes the vmcore code future-safe, if VMCOREINFO_BYTES ever exceeds the page size (Pnina Feder) - "kallsyms: Prevent invalid access when showing module buildid" cleans up kallsyms code related to module buildid and fixes an invalid access crash when printing backtraces (Petr Mladek) - "Address page fault in ima_restore_measurement_list()" fixes a kexec-related crash that can occur when booting the second-stage kernel on x86 (Harshit Mogalapalli) - "kho: ABI headers and Documentation updates" updates the kexec handover ABI documentation (Mike Rapoport) - "Align atomic storage" adds the __aligned attribute to atomic_t and atomic64_t definitions to get natural alignment of both types on csky, m68k, microblaze, nios2, openrisc and sh (Finn Thain) - "kho: clean up page initialization logic" simplifies the page initialization logic in kho_restore_page() (Pratyush Yadav) - "Unload linux/kernel.h" moves several things out of kernel.h and into more appropriate places (Yury Norov) - "don't abuse task_struct.group_leader" removes the usage of ->group_leader when it is "obviously unnecessary" (Oleg Nesterov) - "list private v2 & luo flb" adds some infrastructure improvements to the live update orchestrator (Pasha Tatashin) * tag 'mm-nonmm-stable-2026-02-12-10-48' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (107 commits) watchdog/hardlockup: simplify perf event probe and remove per-cpu dependency procfs: fix missing RCU protection when reading real_parent in do_task_stat() watchdog/softlockup: fix sample ring index wrap in need_counting_irqs() kcsan, compiler_types: avoid duplicate type issues in BPF Type Format kho: fix doc for kho_restore_pages() tests/liveupdate: add in-kernel liveupdate test liveupdate: luo_flb: introduce File-Lifecycle-Bound global state liveupdate: luo_file: Use private list list: add kunit test for private list primitives list: add primitives for private list manipulations delayacct: fix uapi timespec64 definition panic: add panic_force_cpu= parameter to redirect panic to a specific CPU netclassid: use thread_group_leader(p) in update_classid_task() RDMA/umem: don't abuse current->group_leader drm/pan*: don't abuse current->group_leader drm/amd: kill the outdated "Only the pthreads threading model is supported" checks drm/amdgpu: don't abuse current->group_leader android/binder: use same_thread_group(proc->tsk, current) in binder_mmap() android/binder: don't abuse current->group_leader kho: skip memoryless NUMA nodes when reserving scratch areas ...
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig13
-rw-r--r--lib/Kconfig.debug118
-rw-r--r--lib/Makefile3
-rwxr-xr-xlib/build_OID_registry26
-rw-r--r--lib/globtest.c167
-rw-r--r--lib/group_cpus.c271
-rw-r--r--lib/hexdump.c1
-rw-r--r--lib/kfifo.c2
-rw-r--r--lib/kstrtox.c4
-rw-r--r--lib/once.c2
-rw-r--r--lib/string_helpers.c1
-rw-r--r--lib/test_kho.c7
-rw-r--r--lib/test_uuid.c134
-rw-r--r--lib/tests/Makefile5
-rw-r--r--lib/tests/glob_kunit.c125
-rw-r--r--lib/tests/list-private-test.c76
-rw-r--r--lib/tests/liveupdate.c158
-rw-r--r--lib/tests/min_heap_kunit.c (renamed from lib/test_min_heap.c)147
-rw-r--r--lib/tests/uuid_kunit.c106
-rw-r--r--lib/uuid.c1
-rw-r--r--lib/vsprintf.c1
21 files changed, 873 insertions, 495 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index 2923924bea78..0f2fb9610647 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -430,19 +430,6 @@ config GLOB
are compiling an out-of tree driver which tells you that it
depends on this.
-config GLOB_SELFTEST
- tristate "glob self-test on init"
- depends on GLOB
- help
- This option enables a simple self-test of the glob_match
- function on startup. It is primarily useful for people
- working on the code to ensure they haven't introduced any
- regressions.
-
- It only adds a little bit of code and slows kernel boot (or
- module load) by a small amount, so you're welcome to play with
- it, but you probably don't need it.
-
#
# Netlink attribute parsing support is select'ed if needed
#
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 64ef7e62eb8a..4e2dfbbd3d78 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1147,13 +1147,14 @@ config SOFTLOCKUP_DETECTOR_INTR_STORM
the CPU stats and the interrupt counts during the "soft lockups".
config BOOTPARAM_SOFTLOCKUP_PANIC
- bool "Panic (Reboot) On Soft Lockups"
+ int "Panic (Reboot) On Soft Lockups"
depends on SOFTLOCKUP_DETECTOR
+ default 0
help
- Say Y here to enable the kernel to panic on "soft lockups",
- which are bugs that cause the kernel to loop in kernel
- mode for more than 20 seconds (configurable using the watchdog_thresh
- sysctl), without giving other tasks a chance to run.
+ Set to a non-zero value N to enable the kernel to panic on "soft
+ lockups", which are bugs that cause the kernel to loop in kernel
+ mode for more than (N * 20 seconds) (configurable using the
+ watchdog_thresh sysctl), without giving other tasks a chance to run.
The panic can be used in combination with panic_timeout,
to cause the system to reboot automatically after a
@@ -1161,7 +1162,7 @@ config BOOTPARAM_SOFTLOCKUP_PANIC
high-availability systems that have uptime guarantees and
where a lockup must be resolved ASAP.
- Say N if unsure.
+ Say 0 if unsure.
config HAVE_HARDLOCKUP_DETECTOR_BUDDY
bool
@@ -1310,7 +1311,7 @@ config BOOTPARAM_HUNG_TASK_PANIC
high-availability systems that have uptime guarantees and
where a hung tasks must be resolved ASAP.
- Say N if unsure.
+ Say 0 if unsure.
config DETECT_HUNG_TASK_BLOCKER
bool "Dump Hung Tasks Blocker"
@@ -1419,6 +1420,24 @@ config DEBUG_PREEMPT
depending on workload as it triggers debugging routines for each
this_cpu operation. It should only be used for debugging purposes.
+config DEBUG_ATOMIC
+ bool "Debug atomic variables"
+ depends on DEBUG_KERNEL
+ help
+ If you say Y here then the kernel will add a runtime alignment check
+ to atomic accesses. Useful for architectures that do not have trap on
+ mis-aligned access.
+
+ This option has potentially significant overhead.
+
+config DEBUG_ATOMIC_LARGEST_ALIGN
+ bool "Check alignment only up to __aligned_largest"
+ depends on DEBUG_ATOMIC
+ help
+ If you say Y here then the check for natural alignment of
+ atomic accesses will be constrained to the compiler's largest
+ alignment for scalar types.
+
menu "Lock Debugging (spinlocks, mutexes, etc...)"
config LOCK_DEBUGGING_SUPPORT
@@ -2337,16 +2356,6 @@ config TEST_LIST_SORT
If unsure, say N.
-config TEST_MIN_HEAP
- tristate "Min heap test"
- depends on DEBUG_KERNEL || m
- help
- Enable this to turn on min heap function tests. This test is
- executed only once during system boot (so affects only boot time),
- or at module load time.
-
- If unsure, say N.
-
config TEST_SORT
tristate "Array-based sort test" if !KUNIT_ALL_TESTS
depends on KUNIT
@@ -2559,9 +2568,6 @@ config TEST_BITMAP
If unsure, say N.
-config TEST_UUID
- tristate "Test functions located in the uuid module at runtime"
-
config TEST_XARRAY
tristate "Test the XArray code at runtime"
@@ -2845,6 +2851,20 @@ config LIST_KUNIT_TEST
If unsure, say N.
+config LIST_PRIVATE_KUNIT_TEST
+ tristate "KUnit Test for Kernel Private Linked-list structures" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the KUnit test for the private linked-list primitives
+ defined in include/linux/list_private.h.
+
+ These primitives allow manipulation of list_head members that are
+ marked as private and require special accessors (ACCESS_PRIVATE)
+ to strip qualifiers or handle encapsulation.
+
+ If unsure, say N.
+
config HASHTABLE_KUNIT_TEST
tristate "KUnit Test for Kernel Hashtable structures" if !KUNIT_ALL_TESTS
depends on KUNIT
@@ -2884,6 +2904,29 @@ config CONTEXT_ANALYSIS_TEST
If unsure, say N.
+config LIVEUPDATE_TEST
+ bool "Live Update Kernel Test"
+ default n
+ depends on LIVEUPDATE
+ help
+ Enable a built-in kernel test module for the Live Update
+ Orchestrator.
+
+ This module validates the File-Lifecycle-Bound subsystem by
+ registering a set of mock FLB objects with any real file handlers
+ that support live update (such as the memfd handler).
+
+ When live update operations are performed, this test module will
+ output messages to the kernel log (dmesg), confirming that its
+ registration and various callback functions (preserve, retrieve,
+ finish, etc.) are being invoked correctly.
+
+ This is a debugging and regression testing tool for developers
+ working on the Live Update subsystem. It should not be enabled in
+ production kernels.
+
+ If unsure, say N
+
config CMDLINE_KUNIT_TEST
tristate "KUnit test for cmdline API" if !KUNIT_ALL_TESTS
depends on KUNIT
@@ -2959,6 +3002,17 @@ config MEMCPY_KUNIT_TEST
If unsure, say N.
+config MIN_HEAP_KUNIT_TEST
+ tristate "Min heap test" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This option enables the KUnit test suite for the min heap library
+ which provides functions for creating and managing min heaps.
+ The test suite checks the functionality of the min heap library.
+
+ If unsure, say N
+
config IS_SIGNED_TYPE_KUNIT_TEST
tristate "Test is_signed_type() macro" if !KUNIT_ALL_TESTS
depends on KUNIT
@@ -3364,6 +3418,17 @@ config RATELIMIT_KUNIT_TEST
If unsure, say N.
+config UUID_KUNIT_TEST
+ tristate "KUnit test for UUID" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This option enables the KUnit test suite for the uuid library,
+ which provides functions for generating and parsing UUID and GUID.
+ The test suite checks parsing of UUID and GUID strings.
+
+ If unsure, say N.
+
config INT_POW_KUNIT_TEST
tristate "Integer exponentiation (int_pow) test" if !KUNIT_ALL_TESTS
depends on KUNIT
@@ -3433,6 +3498,19 @@ config PRIME_NUMBERS_KUNIT_TEST
If unsure, say N
+config GLOB_KUNIT_TEST
+ tristate "Glob matching test" if !KUNIT_ALL_TESTS
+ depends on GLOB
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Enable this option to test the glob functions at runtime.
+
+ This test suite verifies the correctness of glob_match() across various
+ scenarios, including edge cases.
+
+ If unsure, say N
+
endif # RUNTIME_TESTING_MENU
config ARCH_USE_MEMTEST
diff --git a/lib/Makefile b/lib/Makefile
index 22d8742bba57..1b9ee167517f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -77,7 +77,6 @@ obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o
CFLAGS_test_ubsan.o += $(call cc-disable-warning, unused-but-set-variable)
UBSAN_SANITIZE_test_ubsan.o := y
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
-obj-$(CONFIG_TEST_MIN_HEAP) += test_min_heap.o
obj-$(CONFIG_TEST_LKM) += test_module.o
obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
@@ -91,7 +90,6 @@ ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_KASAN),yy)
GCOV_PROFILE_test_bitmap.o := n
endif
-obj-$(CONFIG_TEST_UUID) += test_uuid.o
obj-$(CONFIG_TEST_XARRAY) += test_xarray.o
obj-$(CONFIG_TEST_MAPLE_TREE) += test_maple_tree.o
obj-$(CONFIG_TEST_PARMAN) += test_parman.o
@@ -228,7 +226,6 @@ obj-$(CONFIG_CLOSURES) += closure.o
obj-$(CONFIG_DQL) += dynamic_queue_limits.o
obj-$(CONFIG_GLOB) += glob.o
-obj-$(CONFIG_GLOB_SELFTEST) += globtest.o
obj-$(CONFIG_DIMLIB) += dim/
obj-$(CONFIG_SIGNATURE) += digsig.o
diff --git a/lib/build_OID_registry b/lib/build_OID_registry
index 8267e8d71338..30493ac190c0 100755
--- a/lib/build_OID_registry
+++ b/lib/build_OID_registry
@@ -60,10 +60,12 @@ for (my $i = 0; $i <= $#names; $i++) {
# Determine the encoded length of this OID
my $size = $#components;
for (my $loop = 2; $loop <= $#components; $loop++) {
- my $c = $components[$loop];
+ $ENV{'BC_LINE_LENGTH'} = "0";
+ my $c = `echo "ibase=10; obase=2; $components[$loop]" | bc`;
+ chomp($c);
# We will base128 encode the number
- my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
+ my $tmp = length($c) - 1;
$tmp = int($tmp / 7);
$size += $tmp;
}
@@ -100,16 +102,24 @@ for (my $i = 0; $i <= $#names; $i++) {
push @octets, $components[0] * 40 + $components[1];
for (my $loop = 2; $loop <= $#components; $loop++) {
- my $c = $components[$loop];
+ # get the base 2 representation of the component
+ $ENV{'BC_LINE_LENGTH'} = "0";
+ my $c = `echo "ibase=10; obase=2; $components[$loop]" | bc`;
+ chomp($c);
- # Base128 encode the number
- my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
+ my $tmp = length($c) - 1;
$tmp = int($tmp / 7);
- for (; $tmp > 0; $tmp--) {
- push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80;
+ # zero pad upto length multiple of 7
+ $c = substr("0000000", 0, ($tmp + 1) * 7 - length($c)).$c;
+
+ # Base128 encode the number
+ for (my $j = 0; $j < $tmp; $j++) {
+ my $b = oct("0b".substr($c, $j * 7, 7));
+
+ push @octets, $b | 0x80;
}
- push @octets, $c & 0x7f;
+ push @octets, oct("0b".substr($c, $tmp * 7, 7));
}
push @encoded_oids, \@octets;
diff --git a/lib/globtest.c b/lib/globtest.c
deleted file mode 100644
index d8e97d43b905..000000000000
--- a/lib/globtest.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Extracted fronm glob.c
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/glob.h>
-#include <linux/printk.h>
-
-/* Boot with "glob.verbose=1" to show successful tests, too */
-static bool verbose = false;
-module_param(verbose, bool, 0);
-
-struct glob_test {
- char const *pat, *str;
- bool expected;
-};
-
-static bool __pure __init test(char const *pat, char const *str, bool expected)
-{
- bool match = glob_match(pat, str);
- bool success = match == expected;
-
- /* Can't get string literals into a particular section, so... */
- static char const msg_error[] __initconst =
- KERN_ERR "glob: \"%s\" vs. \"%s\": %s *** ERROR ***\n";
- static char const msg_ok[] __initconst =
- KERN_DEBUG "glob: \"%s\" vs. \"%s\": %s OK\n";
- static char const mismatch[] __initconst = "mismatch";
- char const *message;
-
- if (!success)
- message = msg_error;
- else if (verbose)
- message = msg_ok;
- else
- return success;
-
- printk(message, pat, str, mismatch + 3*match);
- return success;
-}
-
-/*
- * The tests are all jammed together in one array to make it simpler
- * to place that array in the .init.rodata section. The obvious
- * "array of structures containing char *" has no way to force the
- * pointed-to strings to be in a particular section.
- *
- * Anyway, a test consists of:
- * 1. Expected glob_match result: '1' or '0'.
- * 2. Pattern to match: null-terminated string
- * 3. String to match against: null-terminated string
- *
- * The list of tests is terminated with a final '\0' instead of
- * a glob_match result character.
- */
-static char const glob_tests[] __initconst =
- /* Some basic tests */
- "1" "a\0" "a\0"
- "0" "a\0" "b\0"
- "0" "a\0" "aa\0"
- "0" "a\0" "\0"
- "1" "\0" "\0"
- "0" "\0" "a\0"
- /* Simple character class tests */
- "1" "[a]\0" "a\0"
- "0" "[a]\0" "b\0"
- "0" "[!a]\0" "a\0"
- "1" "[!a]\0" "b\0"
- "1" "[ab]\0" "a\0"
- "1" "[ab]\0" "b\0"
- "0" "[ab]\0" "c\0"
- "1" "[!ab]\0" "c\0"
- "1" "[a-c]\0" "b\0"
- "0" "[a-c]\0" "d\0"
- /* Corner cases in character class parsing */
- "1" "[a-c-e-g]\0" "-\0"
- "0" "[a-c-e-g]\0" "d\0"
- "1" "[a-c-e-g]\0" "f\0"
- "1" "[]a-ceg-ik[]\0" "a\0"
- "1" "[]a-ceg-ik[]\0" "]\0"
- "1" "[]a-ceg-ik[]\0" "[\0"
- "1" "[]a-ceg-ik[]\0" "h\0"
- "0" "[]a-ceg-ik[]\0" "f\0"
- "0" "[!]a-ceg-ik[]\0" "h\0"
- "0" "[!]a-ceg-ik[]\0" "]\0"
- "1" "[!]a-ceg-ik[]\0" "f\0"
- /* Simple wild cards */
- "1" "?\0" "a\0"
- "0" "?\0" "aa\0"
- "0" "??\0" "a\0"
- "1" "?x?\0" "axb\0"
- "0" "?x?\0" "abx\0"
- "0" "?x?\0" "xab\0"
- /* Asterisk wild cards (backtracking) */
- "0" "*??\0" "a\0"
- "1" "*??\0" "ab\0"
- "1" "*??\0" "abc\0"
- "1" "*??\0" "abcd\0"
- "0" "??*\0" "a\0"
- "1" "??*\0" "ab\0"
- "1" "??*\0" "abc\0"
- "1" "??*\0" "abcd\0"
- "0" "?*?\0" "a\0"
- "1" "?*?\0" "ab\0"
- "1" "?*?\0" "abc\0"
- "1" "?*?\0" "abcd\0"
- "1" "*b\0" "b\0"
- "1" "*b\0" "ab\0"
- "0" "*b\0" "ba\0"
- "1" "*b\0" "bb\0"
- "1" "*b\0" "abb\0"
- "1" "*b\0" "bab\0"
- "1" "*bc\0" "abbc\0"
- "1" "*bc\0" "bc\0"
- "1" "*bc\0" "bbc\0"
- "1" "*bc\0" "bcbc\0"
- /* Multiple asterisks (complex backtracking) */
- "1" "*ac*\0" "abacadaeafag\0"
- "1" "*ac*ae*ag*\0" "abacadaeafag\0"
- "1" "*a*b*[bc]*[ef]*g*\0" "abacadaeafag\0"
- "0" "*a*b*[ef]*[cd]*g*\0" "abacadaeafag\0"
- "1" "*abcd*\0" "abcabcabcabcdefg\0"
- "1" "*ab*cd*\0" "abcabcabcabcdefg\0"
- "1" "*abcd*abcdef*\0" "abcabcdabcdeabcdefg\0"
- "0" "*abcd*\0" "abcabcabcabcefg\0"
- "0" "*ab*cd*\0" "abcabcabcabcefg\0";
-
-static int __init glob_init(void)
-{
- unsigned successes = 0;
- unsigned n = 0;
- char const *p = glob_tests;
- static char const message[] __initconst =
- KERN_INFO "glob: %u self-tests passed, %u failed\n";
-
- /*
- * Tests are jammed together in a string. The first byte is '1'
- * or '0' to indicate the expected outcome, or '\0' to indicate the
- * end of the tests. Then come two null-terminated strings: the
- * pattern and the string to match it against.
- */
- while (*p) {
- bool expected = *p++ & 1;
- char const *pat = p;
-
- p += strlen(p) + 1;
- successes += test(pat, p, expected);
- p += strlen(p) + 1;
- n++;
- }
-
- n -= successes;
- printk(message, successes, n);
-
- /* What's the errno for "kernel bug detected"? Guess... */
- return n ? -ECANCELED : 0;
-}
-
-/* We need a dummy exit function to allow unload */
-static void __exit glob_fini(void) { }
-
-module_init(glob_init);
-module_exit(glob_fini);
-
-MODULE_DESCRIPTION("glob(7) matching tests");
-MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/group_cpus.c b/lib/group_cpus.c
index 6d08ac05f371..a93df70919df 100644
--- a/lib/group_cpus.c
+++ b/lib/group_cpus.c
@@ -114,48 +114,15 @@ static int ncpus_cmp_func(const void *l, const void *r)
return ln->ncpus - rn->ncpus;
}
-/*
- * Allocate group number for each node, so that for each node:
- *
- * 1) the allocated number is >= 1
- *
- * 2) the allocated number is <= active CPU number of this node
- *
- * The actual allocated total groups may be less than @numgrps when
- * active total CPU number is less than @numgrps.
- *
- * Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]'
- * for each node.
- */
-static void alloc_nodes_groups(unsigned int numgrps,
- cpumask_var_t *node_to_cpumask,
- const struct cpumask *cpu_mask,
- const nodemask_t nodemsk,
- struct cpumask *nmsk,
- struct node_groups *node_groups)
+static void alloc_groups_to_nodes(unsigned int numgrps,
+ unsigned int numcpus,
+ struct node_groups *node_groups,
+ unsigned int num_nodes)
{
- unsigned n, remaining_ncpus = 0;
-
- for (n = 0; n < nr_node_ids; n++) {
- node_groups[n].id = n;
- node_groups[n].ncpus = UINT_MAX;
- }
-
- for_each_node_mask(n, nodemsk) {
- unsigned ncpus;
-
- cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
- ncpus = cpumask_weight(nmsk);
-
- if (!ncpus)
- continue;
- remaining_ncpus += ncpus;
- node_groups[n].ncpus = ncpus;
- }
+ unsigned int n, remaining_ncpus = numcpus;
+ unsigned int ngroups, ncpus;
- numgrps = min_t(unsigned, remaining_ncpus, numgrps);
-
- sort(node_groups, nr_node_ids, sizeof(node_groups[0]),
+ sort(node_groups, num_nodes, sizeof(node_groups[0]),
ncpus_cmp_func, NULL);
/*
@@ -226,9 +193,8 @@ static void alloc_nodes_groups(unsigned int numgrps,
* finally for each node X: grps(X) <= ncpu(X).
*
*/
- for (n = 0; n < nr_node_ids; n++) {
- unsigned ngroups, ncpus;
+ for (n = 0; n < num_nodes; n++) {
if (node_groups[n].ncpus == UINT_MAX)
continue;
@@ -246,12 +212,201 @@ static void alloc_nodes_groups(unsigned int numgrps,
}
}
+/*
+ * Allocate group number for each node, so that for each node:
+ *
+ * 1) the allocated number is >= 1
+ *
+ * 2) the allocated number is <= active CPU number of this node
+ *
+ * The actual allocated total groups may be less than @numgrps when
+ * active total CPU number is less than @numgrps.
+ *
+ * Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]'
+ * for each node.
+ */
+static void alloc_nodes_groups(unsigned int numgrps,
+ cpumask_var_t *node_to_cpumask,
+ const struct cpumask *cpu_mask,
+ const nodemask_t nodemsk,
+ struct cpumask *nmsk,
+ struct node_groups *node_groups)
+{
+ unsigned int n, numcpus = 0;
+
+ for (n = 0; n < nr_node_ids; n++) {
+ node_groups[n].id = n;
+ node_groups[n].ncpus = UINT_MAX;
+ }
+
+ for_each_node_mask(n, nodemsk) {
+ unsigned int ncpus;
+
+ cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
+ ncpus = cpumask_weight(nmsk);
+
+ if (!ncpus)
+ continue;
+ numcpus += ncpus;
+ node_groups[n].ncpus = ncpus;
+ }
+
+ numgrps = min_t(unsigned int, numcpus, numgrps);
+ alloc_groups_to_nodes(numgrps, numcpus, node_groups, nr_node_ids);
+}
+
+static void assign_cpus_to_groups(unsigned int ncpus,
+ struct cpumask *nmsk,
+ struct node_groups *nv,
+ struct cpumask *masks,
+ unsigned int *curgrp,
+ unsigned int last_grp)
+{
+ unsigned int v, cpus_per_grp, extra_grps;
+ /* Account for rounding errors */
+ extra_grps = ncpus - nv->ngroups * (ncpus / nv->ngroups);
+
+ /* Spread allocated groups on CPUs of the current node */
+ for (v = 0; v < nv->ngroups; v++, *curgrp += 1) {
+ cpus_per_grp = ncpus / nv->ngroups;
+
+ /* Account for extra groups to compensate rounding errors */
+ if (extra_grps) {
+ cpus_per_grp++;
+ --extra_grps;
+ }
+
+ /*
+ * wrapping has to be considered given 'startgrp'
+ * may start anywhere
+ */
+ if (*curgrp >= last_grp)
+ *curgrp = 0;
+ grp_spread_init_one(&masks[*curgrp], nmsk, cpus_per_grp);
+ }
+}
+
+static int alloc_cluster_groups(unsigned int ncpus,
+ unsigned int ngroups,
+ struct cpumask *node_cpumask,
+ cpumask_var_t msk,
+ const struct cpumask ***clusters_ptr,
+ struct node_groups **cluster_groups_ptr)
+{
+ unsigned int ncluster = 0;
+ unsigned int cpu, nc, n;
+ const struct cpumask *cluster_mask;
+ const struct cpumask **clusters;
+ struct node_groups *cluster_groups;
+
+ cpumask_copy(msk, node_cpumask);
+
+ /* Probe how many clusters in this node. */
+ while (1) {
+ cpu = cpumask_first(msk);
+ if (cpu >= nr_cpu_ids)
+ break;
+
+ cluster_mask = topology_cluster_cpumask(cpu);
+ if (!cpumask_weight(cluster_mask))
+ goto no_cluster;
+ /* Clean out CPUs on the same cluster. */
+ cpumask_andnot(msk, msk, cluster_mask);
+ ncluster++;
+ }
+
+ /* If ngroups < ncluster, cross cluster is inevitable, skip. */
+ if (ncluster == 0 || ncluster > ngroups)
+ goto no_cluster;
+
+ /* Allocate memory based on cluster number. */
+ clusters = kcalloc(ncluster, sizeof(struct cpumask *), GFP_KERNEL);
+ if (!clusters)
+ goto no_cluster;
+ cluster_groups = kcalloc(ncluster, sizeof(struct node_groups), GFP_KERNEL);
+ if (!cluster_groups)
+ goto fail_cluster_groups;
+
+ /* Filling cluster info for later process. */
+ cpumask_copy(msk, node_cpumask);
+ for (n = 0; n < ncluster; n++) {
+ cpu = cpumask_first(msk);
+ cluster_mask = topology_cluster_cpumask(cpu);
+ nc = cpumask_weight_and(cluster_mask, node_cpumask);
+ clusters[n] = cluster_mask;
+ cluster_groups[n].id = n;
+ cluster_groups[n].ncpus = nc;
+ cpumask_andnot(msk, msk, cluster_mask);
+ }
+
+ alloc_groups_to_nodes(ngroups, ncpus, cluster_groups, ncluster);
+
+ *clusters_ptr = clusters;
+ *cluster_groups_ptr = cluster_groups;
+ return ncluster;
+
+ fail_cluster_groups:
+ kfree(clusters);
+ no_cluster:
+ return 0;
+}
+
+/*
+ * Try group CPUs evenly for cluster locality within a NUMA node.
+ *
+ * Return: true if success, false otherwise.
+ */
+static bool __try_group_cluster_cpus(unsigned int ncpus,
+ unsigned int ngroups,
+ struct cpumask *node_cpumask,
+ struct cpumask *masks,
+ unsigned int *curgrp,
+ unsigned int last_grp)
+{
+ struct node_groups *cluster_groups;
+ const struct cpumask **clusters;
+ unsigned int ncluster;
+ bool ret = false;
+ cpumask_var_t nmsk;
+ unsigned int i, nc;
+
+ if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL))
+ goto fail_nmsk_alloc;
+
+ ncluster = alloc_cluster_groups(ncpus, ngroups, node_cpumask, nmsk,
+ &clusters, &cluster_groups);
+
+ if (ncluster == 0)
+ goto fail_no_clusters;
+
+ for (i = 0; i < ncluster; i++) {
+ struct node_groups *nv = &cluster_groups[i];
+
+ /* Get the cpus on this cluster. */
+ cpumask_and(nmsk, node_cpumask, clusters[nv->id]);
+ nc = cpumask_weight(nmsk);
+ if (!nc)
+ continue;
+ WARN_ON_ONCE(nv->ngroups > nc);
+
+ assign_cpus_to_groups(nc, nmsk, nv, masks, curgrp, last_grp);
+ }
+
+ ret = true;
+ kfree(cluster_groups);
+ kfree(clusters);
+ fail_no_clusters:
+ free_cpumask_var(nmsk);
+ fail_nmsk_alloc:
+ return ret;
+}
+
static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps,
cpumask_var_t *node_to_cpumask,
const struct cpumask *cpu_mask,
struct cpumask *nmsk, struct cpumask *masks)
{
- unsigned int i, n, nodes, cpus_per_grp, extra_grps, done = 0;
+ unsigned int i, n, nodes, done = 0;
unsigned int last_grp = numgrps;
unsigned int curgrp = startgrp;
nodemask_t nodemsk = NODE_MASK_NONE;
@@ -287,7 +442,7 @@ static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps,
alloc_nodes_groups(numgrps, node_to_cpumask, cpu_mask,
nodemsk, nmsk, node_groups);
for (i = 0; i < nr_node_ids; i++) {
- unsigned int ncpus, v;
+ unsigned int ncpus;
struct node_groups *nv = &node_groups[i];
if (nv->ngroups == UINT_MAX)
@@ -301,28 +456,14 @@ static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps,
WARN_ON_ONCE(nv->ngroups > ncpus);
- /* Account for rounding errors */
- extra_grps = ncpus - nv->ngroups * (ncpus / nv->ngroups);
-
- /* Spread allocated groups on CPUs of the current node */
- for (v = 0; v < nv->ngroups; v++, curgrp++) {
- cpus_per_grp = ncpus / nv->ngroups;
-
- /* Account for extra groups to compensate rounding errors */
- if (extra_grps) {
- cpus_per_grp++;
- --extra_grps;
- }
-
- /*
- * wrapping has to be considered given 'startgrp'
- * may start anywhere
- */
- if (curgrp >= last_grp)
- curgrp = 0;
- grp_spread_init_one(&masks[curgrp], nmsk,
- cpus_per_grp);
+ if (__try_group_cluster_cpus(ncpus, nv->ngroups, nmsk,
+ masks, &curgrp, last_grp)) {
+ done += nv->ngroups;
+ continue;
}
+
+ assign_cpus_to_groups(ncpus, nmsk, nv, masks, &curgrp,
+ last_grp);
done += nv->ngroups;
}
kfree(node_groups);
diff --git a/lib/hexdump.c b/lib/hexdump.c
index c3db7c3a7643..2e5cd8c24769 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -6,6 +6,7 @@
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/errno.h>
+#include <linux/hex.h>
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/export.h>
diff --git a/lib/kfifo.c b/lib/kfifo.c
index 525e66f8294c..2633f9cc336c 100644
--- a/lib/kfifo.c
+++ b/lib/kfifo.c
@@ -41,7 +41,7 @@ int __kfifo_alloc_node(struct __kfifo *fifo, unsigned int size,
return -EINVAL;
}
- fifo->data = kmalloc_array_node(esize, size, gfp_mask, node);
+ fifo->data = kmalloc_array_node(size, esize, gfp_mask, node);
if (!fifo->data) {
fifo->mask = 0;
diff --git a/lib/kstrtox.c b/lib/kstrtox.c
index bdde40cd69d7..97be2a39f537 100644
--- a/lib/kstrtox.c
+++ b/lib/kstrtox.c
@@ -340,8 +340,8 @@ EXPORT_SYMBOL(kstrtos8);
* @s: input string
* @res: result
*
- * This routine returns 0 iff the first character is one of 'YyTt1NnFf0', or
- * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value
+ * This routine returns 0 iff the first character is one of 'EeYyTt1DdNnFf0',
+ * or [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value
* pointed to by res is updated upon finding a match.
*/
noinline
diff --git a/lib/once.c b/lib/once.c
index 2c306f0e891e..8557eb489f34 100644
--- a/lib/once.c
+++ b/lib/once.c
@@ -93,6 +93,6 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key,
{
*done = true;
mutex_unlock(&once_mutex);
- once_disable_jump(once_key, mod);
+ static_branch_disable(once_key);
}
EXPORT_SYMBOL(__do_once_sleepable_done);
diff --git a/lib/string_helpers.c b/lib/string_helpers.c
index ffb8ead6d4cd..8cb6f66c9c2b 100644
--- a/lib/string_helpers.c
+++ b/lib/string_helpers.c
@@ -13,6 +13,7 @@
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/hex.h>
#include <linux/limits.h>
#include <linux/mm.h>
#include <linux/slab.h>
diff --git a/lib/test_kho.c b/lib/test_kho.c
index 47de56280795..a20fafaf9846 100644
--- a/lib/test_kho.c
+++ b/lib/test_kho.c
@@ -19,6 +19,7 @@
#include <linux/printk.h>
#include <linux/vmalloc.h>
#include <linux/kexec_handover.h>
+#include <linux/kho/abi/kexec_handover.h>
#include <net/checksum.h>
@@ -339,11 +340,15 @@ module_init(kho_test_init);
static void kho_test_cleanup(void)
{
+ /* unpreserve and free the data stored in folios */
+ kho_test_unpreserve_data(&kho_test_state);
for (int i = 0; i < kho_test_state.nr_folios; i++)
folio_put(kho_test_state.folios[i]);
kvfree(kho_test_state.folios);
- vfree(kho_test_state.folios_info);
+
+ /* Unpreserve and release the FDT folio */
+ kho_unpreserve_folio(kho_test_state.fdt);
folio_put(kho_test_state.fdt);
}
diff --git a/lib/test_uuid.c b/lib/test_uuid.c
deleted file mode 100644
index 0124fad5d72c..000000000000
--- a/lib/test_uuid.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Test cases for lib/uuid.c module.
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/uuid.h>
-
-struct test_uuid_data {
- const char *uuid;
- guid_t le;
- uuid_t be;
-};
-
-static const struct test_uuid_data test_uuid_test_data[] = {
- {
- .uuid = "c33f4995-3701-450e-9fbf-206a2e98e576",
- .le = GUID_INIT(0xc33f4995, 0x3701, 0x450e, 0x9f, 0xbf, 0x20, 0x6a, 0x2e, 0x98, 0xe5, 0x76),
- .be = UUID_INIT(0xc33f4995, 0x3701, 0x450e, 0x9f, 0xbf, 0x20, 0x6a, 0x2e, 0x98, 0xe5, 0x76),
- },
- {
- .uuid = "64b4371c-77c1-48f9-8221-29f054fc023b",
- .le = GUID_INIT(0x64b4371c, 0x77c1, 0x48f9, 0x82, 0x21, 0x29, 0xf0, 0x54, 0xfc, 0x02, 0x3b),
- .be = UUID_INIT(0x64b4371c, 0x77c1, 0x48f9, 0x82, 0x21, 0x29, 0xf0, 0x54, 0xfc, 0x02, 0x3b),
- },
- {
- .uuid = "0cb4ddff-a545-4401-9d06-688af53e7f84",
- .le = GUID_INIT(0x0cb4ddff, 0xa545, 0x4401, 0x9d, 0x06, 0x68, 0x8a, 0xf5, 0x3e, 0x7f, 0x84),
- .be = UUID_INIT(0x0cb4ddff, 0xa545, 0x4401, 0x9d, 0x06, 0x68, 0x8a, 0xf5, 0x3e, 0x7f, 0x84),
- },
-};
-
-static const char * const test_uuid_wrong_data[] = {
- "c33f4995-3701-450e-9fbf206a2e98e576 ", /* no hyphen(s) */
- "64b4371c-77c1-48f9-8221-29f054XX023b", /* invalid character(s) */
- "0cb4ddff-a545-4401-9d06-688af53e", /* not enough data */
-};
-
-static unsigned total_tests __initdata;
-static unsigned failed_tests __initdata;
-
-static void __init test_uuid_failed(const char *prefix, bool wrong, bool be,
- const char *data, const char *actual)
-{
- pr_err("%s test #%u %s %s data: '%s'\n",
- prefix,
- total_tests,
- wrong ? "passed on wrong" : "failed on",
- be ? "BE" : "LE",
- data);
- if (actual && *actual)
- pr_err("%s test #%u actual data: '%s'\n",
- prefix,
- total_tests,
- actual);
- failed_tests++;
-}
-
-static void __init test_uuid_test(const struct test_uuid_data *data)
-{
- guid_t le;
- uuid_t be;
- char buf[48];
-
- /* LE */
- total_tests++;
- if (guid_parse(data->uuid, &le))
- test_uuid_failed("conversion", false, false, data->uuid, NULL);
-
- total_tests++;
- if (!guid_equal(&data->le, &le)) {
- sprintf(buf, "%pUl", &le);
- test_uuid_failed("cmp", false, false, data->uuid, buf);
- }
-
- /* BE */
- total_tests++;
- if (uuid_parse(data->uuid, &be))
- test_uuid_failed("conversion", false, true, data->uuid, NULL);
-
- total_tests++;
- if (!uuid_equal(&data->be, &be)) {
- sprintf(buf, "%pUb", &be);
- test_uuid_failed("cmp", false, true, data->uuid, buf);
- }
-}
-
-static void __init test_uuid_wrong(const char *data)
-{
- guid_t le;
- uuid_t be;
-
- /* LE */
- total_tests++;
- if (!guid_parse(data, &le))
- test_uuid_failed("negative", true, false, data, NULL);
-
- /* BE */
- total_tests++;
- if (!uuid_parse(data, &be))
- test_uuid_failed("negative", true, true, data, NULL);
-}
-
-static int __init test_uuid_init(void)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(test_uuid_test_data); i++)
- test_uuid_test(&test_uuid_test_data[i]);
-
- for (i = 0; i < ARRAY_SIZE(test_uuid_wrong_data); i++)
- test_uuid_wrong(test_uuid_wrong_data[i]);
-
- if (failed_tests == 0)
- pr_info("all %u tests passed\n", total_tests);
- else
- pr_err("failed %u out of %u tests\n", failed_tests, total_tests);
-
- return failed_tests ? -EINVAL : 0;
-}
-module_init(test_uuid_init);
-
-static void __exit test_uuid_exit(void)
-{
- /* do nothing */
-}
-module_exit(test_uuid_exit);
-
-MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
-MODULE_DESCRIPTION("Test cases for lib/uuid.c module");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 0f24048f3684..05f74edbc62b 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -20,20 +20,24 @@ CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
CFLAGS_test_fprobe.o += $(CC_FLAGS_FTRACE)
obj-$(CONFIG_FPROBE_SANITY_TEST) += test_fprobe.o
+obj-$(CONFIG_GLOB_KUNIT_TEST) += glob_kunit.o
obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o
obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o
obj-$(CONFIG_IS_SIGNED_TYPE_KUNIT_TEST) += is_signed_type_kunit.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
+obj-$(CONFIG_LIST_PRIVATE_KUNIT_TEST) += list-private-test.o
obj-$(CONFIG_KFIFO_KUNIT_TEST) += kfifo_kunit.o
obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
+obj-$(CONFIG_LIVEUPDATE_TEST) += liveupdate.o
CFLAGS_longest_symbol_kunit.o += $(call cc-disable-warning, missing-prototypes)
obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o
obj-$(CONFIG_MEMCPY_KUNIT_TEST) += memcpy_kunit.o
+obj-$(CONFIG_MIN_HEAP_KUNIT_TEST) += min_heap_kunit.o
CFLAGS_overflow_kunit.o = $(call cc-disable-warning, tautological-constant-out-of-range-compare)
obj-$(CONFIG_OVERFLOW_KUNIT_TEST) += overflow_kunit.o
obj-$(CONFIG_PRINTF_KUNIT_TEST) += printf_kunit.o
@@ -50,5 +54,6 @@ obj-$(CONFIG_STRING_HELPERS_KUNIT_TEST) += string_helpers_kunit.o
obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o
obj-$(CONFIG_RATELIMIT_KUNIT_TEST) += test_ratelimit.o
+obj-$(CONFIG_UUID_KUNIT_TEST) += uuid_kunit.o
obj-$(CONFIG_TEST_RUNTIME_MODULE) += module/
diff --git a/lib/tests/glob_kunit.c b/lib/tests/glob_kunit.c
new file mode 100644
index 000000000000..362b1eda8e5b
--- /dev/null
+++ b/lib/tests/glob_kunit.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: MIT OR GPL-2.0
+/*
+ * Test cases for glob functions.
+ */
+
+#include <kunit/test.h>
+#include <linux/glob.h>
+#include <linux/module.h>
+
+/**
+ * struct glob_test_case - Test case for glob matching.
+ * @pat: Pattern to match.
+ * @str: String to match against.
+ * @expected: Expected glob_match result, true if matched.
+ */
+struct glob_test_case {
+ const char *pat;
+ const char *str;
+ bool expected;
+};
+
+static const struct glob_test_case glob_test_cases[] = {
+ /* Some basic tests */
+ { .pat = "a", .str = "a", .expected = true },
+ { .pat = "a", .str = "b", .expected = false },
+ { .pat = "a", .str = "aa", .expected = false },
+ { .pat = "a", .str = "", .expected = false },
+ { .pat = "", .str = "", .expected = true },
+ { .pat = "", .str = "a", .expected = false },
+ /* Simple character class tests */
+ { .pat = "[a]", .str = "a", .expected = true },
+ { .pat = "[a]", .str = "b", .expected = false },
+ { .pat = "[!a]", .str = "a", .expected = false },
+ { .pat = "[!a]", .str = "b", .expected = true },
+ { .pat = "[ab]", .str = "a", .expected = true },
+ { .pat = "[ab]", .str = "b", .expected = true },
+ { .pat = "[ab]", .str = "c", .expected = false },
+ { .pat = "[!ab]", .str = "c", .expected = true },
+ { .pat = "[a-c]", .str = "b", .expected = true },
+ { .pat = "[a-c]", .str = "d", .expected = false },
+ /* Corner cases in character class parsing */
+ { .pat = "[a-c-e-g]", .str = "-", .expected = true },
+ { .pat = "[a-c-e-g]", .str = "d", .expected = false },
+ { .pat = "[a-c-e-g]", .str = "f", .expected = true },
+ { .pat = "[]a-ceg-ik[]", .str = "a", .expected = true },
+ { .pat = "[]a-ceg-ik[]", .str = "]", .expected = true },
+ { .pat = "[]a-ceg-ik[]", .str = "[", .expected = true },
+ { .pat = "[]a-ceg-ik[]", .str = "h", .expected = true },
+ { .pat = "[]a-ceg-ik[]", .str = "f", .expected = false },
+ { .pat = "[!]a-ceg-ik[]", .str = "h", .expected = false },
+ { .pat = "[!]a-ceg-ik[]", .str = "]", .expected = false },
+ { .pat = "[!]a-ceg-ik[]", .str = "f", .expected = true },
+ /* Simple wild cards */
+ { .pat = "?", .str = "a", .expected = true },
+ { .pat = "?", .str = "aa", .expected = false },
+ { .pat = "??", .str = "a", .expected = false },
+ { .pat = "?x?", .str = "axb", .expected = true },
+ { .pat = "?x?", .str = "abx", .expected = false },
+ { .pat = "?x?", .str = "xab", .expected = false },
+ /* Asterisk wild cards (backtracking) */
+ { .pat = "*??", .str = "a", .expected = false },
+ { .pat = "*??", .str = "ab", .expected = true },
+ { .pat = "*??", .str = "abc", .expected = true },
+ { .pat = "*??", .str = "abcd", .expected = true },
+ { .pat = "??*", .str = "a", .expected = false },
+ { .pat = "??*", .str = "ab", .expected = true },
+ { .pat = "??*", .str = "abc", .expected = true },
+ { .pat = "??*", .str = "abcd", .expected = true },
+ { .pat = "?*?", .str = "a", .expected = false },
+ { .pat = "?*?", .str = "ab", .expected = true },
+ { .pat = "?*?", .str = "abc", .expected = true },
+ { .pat = "?*?", .str = "abcd", .expected = true },
+ { .pat = "*b", .str = "b", .expected = true },
+ { .pat = "*b", .str = "ab", .expected = true },
+ { .pat = "*b", .str = "ba", .expected = false },
+ { .pat = "*b", .str = "bb", .expected = true },
+ { .pat = "*b", .str = "abb", .expected = true },
+ { .pat = "*b", .str = "bab", .expected = true },
+ { .pat = "*bc", .str = "abbc", .expected = true },
+ { .pat = "*bc", .str = "bc", .expected = true },
+ { .pat = "*bc", .str = "bbc", .expected = true },
+ { .pat = "*bc", .str = "bcbc", .expected = true },
+ /* Multiple asterisks (complex backtracking) */
+ { .pat = "*ac*", .str = "abacadaeafag", .expected = true },
+ { .pat = "*ac*ae*ag*", .str = "abacadaeafag", .expected = true },
+ { .pat = "*a*b*[bc]*[ef]*g*", .str = "abacadaeafag", .expected = true },
+ { .pat = "*a*b*[ef]*[cd]*g*", .str = "abacadaeafag", .expected = false },
+ { .pat = "*abcd*", .str = "abcabcabcabcdefg", .expected = true },
+ { .pat = "*ab*cd*", .str = "abcabcabcabcdefg", .expected = true },
+ { .pat = "*abcd*abcdef*", .str = "abcabcdabcdeabcdefg", .expected = true },
+ { .pat = "*abcd*", .str = "abcabcabcabcefg", .expected = false },
+ { .pat = "*ab*cd*", .str = "abcabcabcabcefg", .expected = false },
+};
+
+static void glob_case_to_desc(const struct glob_test_case *t, char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "pat:\"%s\" str:\"%s\"", t->pat, t->str);
+}
+
+KUNIT_ARRAY_PARAM(glob, glob_test_cases, glob_case_to_desc);
+
+static void glob_test_match(struct kunit *test)
+{
+ const struct glob_test_case *params = test->param_value;
+
+ KUNIT_EXPECT_EQ_MSG(test,
+ glob_match(params->pat, params->str),
+ params->expected,
+ "Pattern: \"%s\", String: \"%s\", Expected: %d",
+ params->pat, params->str, params->expected);
+}
+
+static struct kunit_case glob_kunit_test_cases[] = {
+ KUNIT_CASE_PARAM(glob_test_match, glob_gen_params),
+ {}
+};
+
+static struct kunit_suite glob_test_suite = {
+ .name = "glob",
+ .test_cases = glob_kunit_test_cases,
+};
+
+kunit_test_suite(glob_test_suite);
+MODULE_DESCRIPTION("Test cases for glob functions");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/tests/list-private-test.c b/lib/tests/list-private-test.c
new file mode 100644
index 000000000000..3bd62939ae67
--- /dev/null
+++ b/lib/tests/list-private-test.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit compilation/smoke test for Private list primitives.
+ *
+ * Copyright (c) 2025, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+#include <linux/list_private.h>
+#include <kunit/test.h>
+
+/*
+ * This forces compiler to warn if you access it directly, because list
+ * primitives expect (struct list_head *), not (volatile struct list_head *).
+ */
+#undef __private
+#define __private volatile
+
+/* Redefine ACCESS_PRIVATE for this test. */
+#undef ACCESS_PRIVATE
+#define ACCESS_PRIVATE(p, member) \
+ (*((struct list_head *)((unsigned long)&((p)->member))))
+
+struct list_test_struct {
+ int data;
+ struct list_head __private list;
+};
+
+static void list_private_compile_test(struct kunit *test)
+{
+ struct list_test_struct entry;
+ struct list_test_struct *pos, *n;
+ LIST_HEAD(head);
+
+ INIT_LIST_HEAD(&ACCESS_PRIVATE(&entry, list));
+ list_add(&ACCESS_PRIVATE(&entry, list), &head);
+ pos = &entry;
+
+ pos = list_private_entry(&ACCESS_PRIVATE(&entry, list), struct list_test_struct, list);
+ pos = list_private_first_entry(&head, struct list_test_struct, list);
+ pos = list_private_last_entry(&head, struct list_test_struct, list);
+ pos = list_private_next_entry(pos, list);
+ pos = list_private_prev_entry(pos, list);
+ pos = list_private_next_entry_circular(pos, &head, list);
+ pos = list_private_prev_entry_circular(pos, &head, list);
+
+ if (list_private_entry_is_head(pos, &head, list))
+ return;
+
+ list_private_for_each_entry(pos, &head, list) { }
+ list_private_for_each_entry_reverse(pos, &head, list) { }
+ list_private_for_each_entry_continue(pos, &head, list) { }
+ list_private_for_each_entry_continue_reverse(pos, &head, list) { }
+ list_private_for_each_entry_from(pos, &head, list) { }
+ list_private_for_each_entry_from_reverse(pos, &head, list) { }
+
+ list_private_for_each_entry_safe(pos, n, &head, list)
+ list_private_safe_reset_next(pos, n, list);
+ list_private_for_each_entry_safe_continue(pos, n, &head, list) { }
+ list_private_for_each_entry_safe_from(pos, n, &head, list) { }
+ list_private_for_each_entry_safe_reverse(pos, n, &head, list) { }
+}
+
+static struct kunit_case list_private_test_cases[] = {
+ KUNIT_CASE(list_private_compile_test),
+ {},
+};
+
+static struct kunit_suite list_private_test_module = {
+ .name = "list-private-kunit-test",
+ .test_cases = list_private_test_cases,
+};
+
+kunit_test_suite(list_private_test_module);
+
+MODULE_DESCRIPTION("KUnit compilation test for private list primitives");
+MODULE_LICENSE("GPL");
diff --git a/lib/tests/liveupdate.c b/lib/tests/liveupdate.c
new file mode 100644
index 000000000000..496d6ef91a30
--- /dev/null
+++ b/lib/tests/liveupdate.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (c) 2025, Google LLC.
+ * Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME " test: " fmt
+
+#include <linux/cleanup.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/liveupdate.h>
+#include <linux/module.h>
+#include "../../kernel/liveupdate/luo_internal.h"
+
+static const struct liveupdate_flb_ops test_flb_ops;
+#define DEFINE_TEST_FLB(i) { \
+ .ops = &test_flb_ops, \
+ .compatible = LIVEUPDATE_TEST_FLB_COMPATIBLE(i), \
+}
+
+/* Number of Test FLBs to register with every file handler */
+#define TEST_NFLBS 3
+static struct liveupdate_flb test_flbs[TEST_NFLBS] = {
+ DEFINE_TEST_FLB(0),
+ DEFINE_TEST_FLB(1),
+ DEFINE_TEST_FLB(2),
+};
+
+#define TEST_FLB_MAGIC_BASE 0xFEEDF00DCAFEBEE0ULL
+
+static int test_flb_preserve(struct liveupdate_flb_op_args *argp)
+{
+ ptrdiff_t index = argp->flb - test_flbs;
+
+ pr_info("%s: preserve was triggered\n", argp->flb->compatible);
+ argp->data = TEST_FLB_MAGIC_BASE + index;
+
+ return 0;
+}
+
+static void test_flb_unpreserve(struct liveupdate_flb_op_args *argp)
+{
+ pr_info("%s: unpreserve was triggered\n", argp->flb->compatible);
+}
+
+static int test_flb_retrieve(struct liveupdate_flb_op_args *argp)
+{
+ ptrdiff_t index = argp->flb - test_flbs;
+ u64 expected_data = TEST_FLB_MAGIC_BASE + index;
+
+ if (argp->data == expected_data) {
+ pr_info("%s: found flb data from the previous boot\n",
+ argp->flb->compatible);
+ argp->obj = (void *)argp->data;
+ } else {
+ pr_err("%s: ERROR - incorrect data handle: %llx, expected %llx\n",
+ argp->flb->compatible, argp->data, expected_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void test_flb_finish(struct liveupdate_flb_op_args *argp)
+{
+ ptrdiff_t index = argp->flb - test_flbs;
+ void *expected_obj = (void *)(TEST_FLB_MAGIC_BASE + index);
+
+ if (argp->obj == expected_obj) {
+ pr_info("%s: finish was triggered\n", argp->flb->compatible);
+ } else {
+ pr_err("%s: ERROR - finish called with invalid object\n",
+ argp->flb->compatible);
+ }
+}
+
+static const struct liveupdate_flb_ops test_flb_ops = {
+ .preserve = test_flb_preserve,
+ .unpreserve = test_flb_unpreserve,
+ .retrieve = test_flb_retrieve,
+ .finish = test_flb_finish,
+ .owner = THIS_MODULE,
+};
+
+static void liveupdate_test_init(void)
+{
+ static DEFINE_MUTEX(init_lock);
+ static bool initialized;
+ int i;
+
+ guard(mutex)(&init_lock);
+
+ if (initialized)
+ return;
+
+ for (i = 0; i < TEST_NFLBS; i++) {
+ struct liveupdate_flb *flb = &test_flbs[i];
+ void *obj;
+ int err;
+
+ err = liveupdate_flb_get_incoming(flb, &obj);
+ if (err && err != -ENODATA && err != -ENOENT) {
+ pr_err("liveupdate_flb_get_incoming for %s failed: %pe\n",
+ flb->compatible, ERR_PTR(err));
+ }
+ }
+ initialized = true;
+}
+
+void liveupdate_test_register(struct liveupdate_file_handler *fh)
+{
+ int err, i;
+
+ liveupdate_test_init();
+
+ for (i = 0; i < TEST_NFLBS; i++) {
+ struct liveupdate_flb *flb = &test_flbs[i];
+
+ err = liveupdate_register_flb(fh, flb);
+ if (err) {
+ pr_err("Failed to register %s %pe\n",
+ flb->compatible, ERR_PTR(err));
+ }
+ }
+
+ err = liveupdate_register_flb(fh, &test_flbs[0]);
+ if (!err || err != -EEXIST) {
+ pr_err("Failed: %s should be already registered, but got err: %pe\n",
+ test_flbs[0].compatible, ERR_PTR(err));
+ }
+
+ pr_info("Registered %d FLBs with file handler: [%s]\n",
+ TEST_NFLBS, fh->compatible);
+}
+
+void liveupdate_test_unregister(struct liveupdate_file_handler *fh)
+{
+ int err, i;
+
+ for (i = 0; i < TEST_NFLBS; i++) {
+ struct liveupdate_flb *flb = &test_flbs[i];
+
+ err = liveupdate_unregister_flb(fh, flb);
+ if (err) {
+ pr_err("Failed to unregister %s %pe\n",
+ flb->compatible, ERR_PTR(err));
+ }
+ }
+
+ pr_info("Unregistered %d FLBs from file handler: [%s]\n",
+ TEST_NFLBS, fh->compatible);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pasha Tatashin <pasha.tatashin@soleen.com>");
+MODULE_DESCRIPTION("In-kernel test for LUO mechanism");
diff --git a/lib/test_min_heap.c b/lib/tests/min_heap_kunit.c
index a9c4a74d3898..9c1122661698 100644
--- a/lib/test_min_heap.c
+++ b/lib/tests/min_heap_kunit.c
@@ -1,60 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
-#define pr_fmt(fmt) "min_heap_test: " fmt
-
/*
* Test cases for the min max heap.
*/
-#include <linux/log2.h>
+#include <kunit/test.h>
#include <linux/min_heap.h>
#include <linux/module.h>
-#include <linux/printk.h>
#include <linux/random.h>
+struct min_heap_test_case {
+ const char *str;
+ bool min_heap;
+};
+
+static struct min_heap_test_case min_heap_cases[] = {
+ {
+ .str = "min",
+ .min_heap = true,
+ },
+ {
+ .str = "max",
+ .min_heap = false,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(min_heap, min_heap_cases, str);
+
DEFINE_MIN_HEAP(int, min_heap_test);
-static __init bool less_than(const void *lhs, const void *rhs, void __always_unused *args)
+static bool less_than(const void *lhs, const void *rhs, void __always_unused *args)
{
return *(int *)lhs < *(int *)rhs;
}
-static __init bool greater_than(const void *lhs, const void *rhs, void __always_unused *args)
+static bool greater_than(const void *lhs, const void *rhs, void __always_unused *args)
{
return *(int *)lhs > *(int *)rhs;
}
-static __init int pop_verify_heap(bool min_heap,
- struct min_heap_test *heap,
- const struct min_heap_callbacks *funcs)
+static void pop_verify_heap(struct kunit *test,
+ bool min_heap,
+ struct min_heap_test *heap,
+ const struct min_heap_callbacks *funcs)
{
int *values = heap->data;
- int err = 0;
int last;
last = values[0];
min_heap_pop_inline(heap, funcs, NULL);
while (heap->nr > 0) {
- if (min_heap) {
- if (last > values[0]) {
- pr_err("error: expected %d <= %d\n", last,
- values[0]);
- err++;
- }
- } else {
- if (last < values[0]) {
- pr_err("error: expected %d >= %d\n", last,
- values[0]);
- err++;
- }
- }
+ if (min_heap)
+ KUNIT_EXPECT_LE(test, last, values[0]);
+ else
+ KUNIT_EXPECT_GE(test, last, values[0]);
last = values[0];
min_heap_pop_inline(heap, funcs, NULL);
}
- return err;
}
-static __init int test_heapify_all(bool min_heap)
+static void test_heapify_all(struct kunit *test)
{
+ const struct min_heap_test_case *params = test->param_value;
int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0,
-3, -1, -2, -4, 0x8000000, 0x7FFFFFF };
struct min_heap_test heap = {
@@ -63,15 +69,14 @@ static __init int test_heapify_all(bool min_heap)
.size = ARRAY_SIZE(values),
};
struct min_heap_callbacks funcs = {
- .less = min_heap ? less_than : greater_than,
+ .less = params->min_heap ? less_than : greater_than,
.swp = NULL,
};
- int i, err;
+ int i;
/* Test with known set of values. */
min_heapify_all_inline(&heap, &funcs, NULL);
- err = pop_verify_heap(min_heap, &heap, &funcs);
-
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
/* Test with randomly generated values. */
heap.nr = ARRAY_SIZE(values);
@@ -79,13 +84,12 @@ static __init int test_heapify_all(bool min_heap)
values[i] = get_random_u32();
min_heapify_all_inline(&heap, &funcs, NULL);
- err += pop_verify_heap(min_heap, &heap, &funcs);
-
- return err;
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
}
-static __init int test_heap_push(bool min_heap)
+static void test_heap_push(struct kunit *test)
{
+ const struct min_heap_test_case *params = test->param_value;
const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0,
-3, -1, -2, -4, 0x80000000, 0x7FFFFFFF };
int values[ARRAY_SIZE(data)];
@@ -95,29 +99,28 @@ static __init int test_heap_push(bool min_heap)
.size = ARRAY_SIZE(values),
};
struct min_heap_callbacks funcs = {
- .less = min_heap ? less_than : greater_than,
+ .less = params->min_heap ? less_than : greater_than,
.swp = NULL,
};
- int i, temp, err;
+ int i, temp;
/* Test with known set of values copied from data. */
for (i = 0; i < ARRAY_SIZE(data); i++)
min_heap_push_inline(&heap, &data[i], &funcs, NULL);
- err = pop_verify_heap(min_heap, &heap, &funcs);
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
/* Test with randomly generated values. */
while (heap.nr < heap.size) {
temp = get_random_u32();
min_heap_push_inline(&heap, &temp, &funcs, NULL);
}
- err += pop_verify_heap(min_heap, &heap, &funcs);
-
- return err;
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
}
-static __init int test_heap_pop_push(bool min_heap)
+static void test_heap_pop_push(struct kunit *test)
{
+ const struct min_heap_test_case *params = test->param_value;
const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0,
-3, -1, -2, -4, 0x80000000, 0x7FFFFFFF };
int values[ARRAY_SIZE(data)];
@@ -127,13 +130,13 @@ static __init int test_heap_pop_push(bool min_heap)
.size = ARRAY_SIZE(values),
};
struct min_heap_callbacks funcs = {
- .less = min_heap ? less_than : greater_than,
+ .less = params->min_heap ? less_than : greater_than,
.swp = NULL,
};
- int i, temp, err;
+ int i, temp;
/* Fill values with data to pop and replace. */
- temp = min_heap ? 0x80000000 : 0x7FFFFFFF;
+ temp = params->min_heap ? 0x80000000 : 0x7FFFFFFF;
for (i = 0; i < ARRAY_SIZE(data); i++)
min_heap_push_inline(&heap, &temp, &funcs, NULL);
@@ -141,7 +144,7 @@ static __init int test_heap_pop_push(bool min_heap)
for (i = 0; i < ARRAY_SIZE(data); i++)
min_heap_pop_push_inline(&heap, &data[i], &funcs, NULL);
- err = pop_verify_heap(min_heap, &heap, &funcs);
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
heap.nr = 0;
for (i = 0; i < ARRAY_SIZE(data); i++)
@@ -152,13 +155,12 @@ static __init int test_heap_pop_push(bool min_heap)
temp = get_random_u32();
min_heap_pop_push_inline(&heap, &temp, &funcs, NULL);
}
- err += pop_verify_heap(min_heap, &heap, &funcs);
-
- return err;
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
}
-static __init int test_heap_del(bool min_heap)
+static void test_heap_del(struct kunit *test)
{
+ const struct min_heap_test_case *params = test->param_value;
int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0,
-3, -1, -2, -4, 0x8000000, 0x7FFFFFF };
struct min_heap_test heap;
@@ -166,17 +168,16 @@ static __init int test_heap_del(bool min_heap)
min_heap_init_inline(&heap, values, ARRAY_SIZE(values));
heap.nr = ARRAY_SIZE(values);
struct min_heap_callbacks funcs = {
- .less = min_heap ? less_than : greater_than,
+ .less = params->min_heap ? less_than : greater_than,
.swp = NULL,
};
- int i, err;
+ int i;
/* Test with known set of values. */
min_heapify_all_inline(&heap, &funcs, NULL);
for (i = 0; i < ARRAY_SIZE(values) / 2; i++)
min_heap_del_inline(&heap, get_random_u32() % heap.nr, &funcs, NULL);
- err = pop_verify_heap(min_heap, &heap, &funcs);
-
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
/* Test with randomly generated values. */
heap.nr = ARRAY_SIZE(values);
@@ -186,37 +187,23 @@ static __init int test_heap_del(bool min_heap)
for (i = 0; i < ARRAY_SIZE(values) / 2; i++)
min_heap_del_inline(&heap, get_random_u32() % heap.nr, &funcs, NULL);
- err += pop_verify_heap(min_heap, &heap, &funcs);
-
- return err;
+ pop_verify_heap(test, params->min_heap, &heap, &funcs);
}
-static int __init test_min_heap_init(void)
-{
- int err = 0;
-
- err += test_heapify_all(true);
- err += test_heapify_all(false);
- err += test_heap_push(true);
- err += test_heap_push(false);
- err += test_heap_pop_push(true);
- err += test_heap_pop_push(false);
- err += test_heap_del(true);
- err += test_heap_del(false);
- if (err) {
- pr_err("test failed with %d errors\n", err);
- return -EINVAL;
- }
- pr_info("test passed\n");
- return 0;
-}
-module_init(test_min_heap_init);
+static struct kunit_case min_heap_test_cases[] = {
+ KUNIT_CASE_PARAM(test_heapify_all, min_heap_gen_params),
+ KUNIT_CASE_PARAM(test_heap_push, min_heap_gen_params),
+ KUNIT_CASE_PARAM(test_heap_pop_push, min_heap_gen_params),
+ KUNIT_CASE_PARAM(test_heap_del, min_heap_gen_params),
+ {},
+};
-static void __exit test_min_heap_exit(void)
-{
- /* do nothing */
-}
-module_exit(test_min_heap_exit);
+static struct kunit_suite min_heap_test_suite = {
+ .name = "min_heap",
+ .test_cases = min_heap_test_cases,
+};
+
+kunit_test_suite(min_heap_test_suite);
MODULE_DESCRIPTION("Test cases for the min max heap");
MODULE_LICENSE("GPL");
diff --git a/lib/tests/uuid_kunit.c b/lib/tests/uuid_kunit.c
new file mode 100644
index 000000000000..de71b2649dac
--- /dev/null
+++ b/lib/tests/uuid_kunit.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Test cases for lib/uuid.c module.
+ */
+
+#include <kunit/test.h>
+#include <linux/uuid.h>
+
+struct test_uuid_data {
+ const char *uuid;
+ guid_t le;
+ uuid_t be;
+};
+
+static const struct test_uuid_data test_uuid_test_data[] = {
+ {
+ .uuid = "c33f4995-3701-450e-9fbf-206a2e98e576",
+ .le = GUID_INIT(0xc33f4995, 0x3701, 0x450e, 0x9f, 0xbf, 0x20, 0x6a, 0x2e, 0x98, 0xe5, 0x76),
+ .be = UUID_INIT(0xc33f4995, 0x3701, 0x450e, 0x9f, 0xbf, 0x20, 0x6a, 0x2e, 0x98, 0xe5, 0x76),
+ },
+ {
+ .uuid = "64b4371c-77c1-48f9-8221-29f054fc023b",
+ .le = GUID_INIT(0x64b4371c, 0x77c1, 0x48f9, 0x82, 0x21, 0x29, 0xf0, 0x54, 0xfc, 0x02, 0x3b),
+ .be = UUID_INIT(0x64b4371c, 0x77c1, 0x48f9, 0x82, 0x21, 0x29, 0xf0, 0x54, 0xfc, 0x02, 0x3b),
+ },
+ {
+ .uuid = "0cb4ddff-a545-4401-9d06-688af53e7f84",
+ .le = GUID_INIT(0x0cb4ddff, 0xa545, 0x4401, 0x9d, 0x06, 0x68, 0x8a, 0xf5, 0x3e, 0x7f, 0x84),
+ .be = UUID_INIT(0x0cb4ddff, 0xa545, 0x4401, 0x9d, 0x06, 0x68, 0x8a, 0xf5, 0x3e, 0x7f, 0x84),
+ },
+};
+
+static const char * const test_uuid_wrong_data[] = {
+ "c33f4995-3701-450e-9fbf206a2e98e576 ", /* no hyphen(s) */
+ "64b4371c-77c1-48f9-8221-29f054XX023b", /* invalid character(s) */
+ "0cb4ddff-a545-4401-9d06-688af53e", /* not enough data */
+};
+
+static void uuid_test_guid_valid(struct kunit *test)
+{
+ unsigned int i;
+ const struct test_uuid_data *data;
+ guid_t le;
+
+ for (i = 0; i < ARRAY_SIZE(test_uuid_test_data); i++) {
+ data = &test_uuid_test_data[i];
+ KUNIT_EXPECT_EQ(test, guid_parse(data->uuid, &le), 0);
+ KUNIT_EXPECT_TRUE(test, guid_equal(&data->le, &le));
+ }
+}
+
+static void uuid_test_uuid_valid(struct kunit *test)
+{
+ unsigned int i;
+ const struct test_uuid_data *data;
+ uuid_t be;
+
+ for (i = 0; i < ARRAY_SIZE(test_uuid_test_data); i++) {
+ data = &test_uuid_test_data[i];
+ KUNIT_EXPECT_EQ(test, uuid_parse(data->uuid, &be), 0);
+ KUNIT_EXPECT_TRUE(test, uuid_equal(&data->be, &be));
+ }
+}
+
+static void uuid_test_guid_invalid(struct kunit *test)
+{
+ unsigned int i;
+ const char *uuid;
+ guid_t le;
+
+ for (i = 0; i < ARRAY_SIZE(test_uuid_wrong_data); i++) {
+ uuid = test_uuid_wrong_data[i];
+ KUNIT_EXPECT_EQ(test, guid_parse(uuid, &le), -EINVAL);
+ }
+}
+
+static void uuid_test_uuid_invalid(struct kunit *test)
+{
+ unsigned int i;
+ const char *uuid;
+ uuid_t be;
+
+ for (i = 0; i < ARRAY_SIZE(test_uuid_wrong_data); i++) {
+ uuid = test_uuid_wrong_data[i];
+ KUNIT_EXPECT_EQ(test, uuid_parse(uuid, &be), -EINVAL);
+ }
+}
+
+static struct kunit_case uuid_test_cases[] = {
+ KUNIT_CASE(uuid_test_guid_valid),
+ KUNIT_CASE(uuid_test_uuid_valid),
+ KUNIT_CASE(uuid_test_guid_invalid),
+ KUNIT_CASE(uuid_test_uuid_invalid),
+ {},
+};
+
+static struct kunit_suite uuid_test_suite = {
+ .name = "uuid",
+ .test_cases = uuid_test_cases,
+};
+
+kunit_test_suite(uuid_test_suite);
+
+MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>");
+MODULE_DESCRIPTION("Test cases for lib/uuid.c module");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/lib/uuid.c b/lib/uuid.c
index e309b4c5be3d..e8543c668dc7 100644
--- a/lib/uuid.c
+++ b/lib/uuid.c
@@ -10,6 +10,7 @@
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/export.h>
+#include <linux/hex.h>
#include <linux/uuid.h>
#include <linux/random.h>
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index a3790c43a0ab..800b8ac49f53 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -26,6 +26,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
+#include <linux/hex.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/math64.h>