From 4101b3b571701cdf1081b2f96124b1daaeaebbf1 Mon Sep 17 00:00:00 2001 From: Ryota Sakamoto Date: Sun, 11 Jan 2026 02:38:07 +0900 Subject: lib/tests: add KUnit test for bitops Add a KUnit test suite for the bitops API. The existing 'lib/test_bitops.c' is preserved as-is because it contains ad-hoc micro-benchmarks 'test_fns' and is intended to ensure no compiler warnings from C=1 sparse checker or -Wextra compilations. Introduce 'lib/tests/bitops_kunit.c' for functional regression testing. It ports the test logic and data patterns from 'lib/test_bitops.c' to KUnit, verifying correct behavior across various input patterns and architecture-specific edge cases using isolated stack-allocated bitmaps. The following test logic has been ported from test_bitops_startup() in lib/test_bitops.c: - set_bit() / clear_bit() / find_first_bit() validation -> test_set_bit_clear_bit() - get_count_order() validation -> test_get_count_order() - get_count_order_long() validation -> test_get_count_order_long() Also improve the find_first_bit() test to check the full bitmap length (BITOPS_LENGTH) instead of omitting the last bit, ensuring the bitmap is completely empty after cleanup. Verified on x86_64, i386, and arm64 architectures. Sample KUnit output: KTAP version 1 # Subtest: bitops # module: bitops_kunit 1..3 KTAP version 1 # Subtest: test_set_bit_clear_bit ok 1 BITOPS_4 ok 2 BITOPS_7 ok 3 BITOPS_11 ok 4 BITOPS_31 ok 5 BITOPS_88 # test_set_bit_clear_bit: pass:5 fail:0 skip:0 total:5 ok 1 test_set_bit_clear_bit KTAP version 1 # Subtest: test_get_count_order ok 1 0x00000003 ok 2 0x00000004 ok 3 0x00001fff ok 4 0x00002000 ok 5 0x50000000 ok 6 0x80000000 ok 7 0x80003000 # test_get_count_order: pass:7 fail:0 skip:0 total:7 ok 2 test_get_count_order KTAP version 1 # Subtest: test_get_count_order_long ok 1 0x0000000300000000 ok 2 0x0000000400000000 ok 3 0x00001fff00000000 ok 4 0x0000200000000000 ok 5 0x5000000000000000 ok 6 0x8000000000000000 ok 7 0x8000300000000000 # test_get_count_order_long: pass:7 fail:0 skip:0 total:7 ok 3 test_get_count_order_long [Yury: trim Kconfig help message] CC: Jesse Brandeburg CC: Wei Yang Signed-off-by: Ryota Sakamoto Signed-off-by: Yury Norov --- lib/Kconfig.debug | 13 +++++ lib/tests/Makefile | 1 + lib/tests/bitops_kunit.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 lib/tests/bitops_kunit.c (limited to 'lib') diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ba36939fda79..e4f8ba2f9f32 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2652,6 +2652,19 @@ config TEST_SYSCTL If unsure, say N. +config BITOPS_KUNIT + tristate "KUnit test for bitops" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This option enables the KUnit test for the bitops library + which provides functions for bit operations. + + Note that this is a partial copy of the original test_bitops module. + For the full coverage, enable TEST_BITOPS. + + If unsure, say N. + config BITFIELD_KUNIT tristate "KUnit test bitfield functions at runtime" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 601dba4b7d96..0f24048f3684 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -5,6 +5,7 @@ # KUnit tests CFLAGS_bitfield_kunit.o := $(DISABLE_STRUCTLEAK_PLUGIN) obj-$(CONFIG_BASE64_KUNIT) += base64_kunit.o +obj-$(CONFIG_BITOPS_KUNIT) += bitops_kunit.o obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o obj-$(CONFIG_BITS_TEST) += test_bits.o obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o diff --git a/lib/tests/bitops_kunit.c b/lib/tests/bitops_kunit.c new file mode 100644 index 000000000000..5c47a1276061 --- /dev/null +++ b/lib/tests/bitops_kunit.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Intel Corporation + * Copyright (C) 2026 Ryota Sakamoto + */ + +#include +#include +#include + +/* use an enum because that's the most common BITMAP usage */ +enum bitops_fun { + BITOPS_4 = 4, + BITOPS_7 = 7, + BITOPS_11 = 11, + BITOPS_31 = 31, + BITOPS_88 = 88, + BITOPS_LENGTH = 256 +}; + +struct bitops_test_case { + const char *str; + const long nr; +}; + +static struct bitops_test_case bitops_cases[] = { + { + .str = "BITOPS_4", + .nr = BITOPS_4, + }, + { + .str = "BITOPS_7", + .nr = BITOPS_7, + }, + { + .str = "BITOPS_11", + .nr = BITOPS_11, + }, + { + .str = "BITOPS_31", + .nr = BITOPS_31, + }, + { + .str = "BITOPS_88", + .nr = BITOPS_88, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(bitops, bitops_cases, str); + +static void test_set_bit_clear_bit(struct kunit *test) +{ + const struct bitops_test_case *params = test->param_value; + DECLARE_BITMAP(bitmap, BITOPS_LENGTH); + int bit_set; + + bitmap_zero(bitmap, BITOPS_LENGTH); + + set_bit(params->nr, bitmap); + KUNIT_EXPECT_TRUE(test, test_bit(params->nr, bitmap)); + + clear_bit(params->nr, bitmap); + KUNIT_EXPECT_FALSE(test, test_bit(params->nr, bitmap)); + + bit_set = find_first_bit(bitmap, BITOPS_LENGTH); + KUNIT_EXPECT_EQ(test, bit_set, BITOPS_LENGTH); +} + +struct order_test_case { + const char *str; + const unsigned int count; + const int expected; +}; + +static struct order_test_case order_test_cases[] = { + {"0x00000003", 0x00000003, 2}, + {"0x00000004", 0x00000004, 2}, + {"0x00001fff", 0x00001fff, 13}, + {"0x00002000", 0x00002000, 13}, + {"0x50000000", 0x50000000, 31}, + {"0x80000000", 0x80000000, 31}, + {"0x80003000", 0x80003000, 32}, +}; + +KUNIT_ARRAY_PARAM_DESC(order, order_test_cases, str); + +static void test_get_count_order(struct kunit *test) +{ + const struct order_test_case *params = test->param_value; + + KUNIT_EXPECT_EQ(test, get_count_order(params->count), params->expected); + KUNIT_EXPECT_EQ(test, get_count_order_long(params->count), params->expected); +} + +#ifdef CONFIG_64BIT +struct order_long_test_case { + const char *str; + const unsigned long count; + const int expected; +}; + +static struct order_long_test_case order_long_test_cases[] = { + {"0x0000000300000000", 0x0000000300000000, 34}, + {"0x0000000400000000", 0x0000000400000000, 34}, + {"0x00001fff00000000", 0x00001fff00000000, 45}, + {"0x0000200000000000", 0x0000200000000000, 45}, + {"0x5000000000000000", 0x5000000000000000, 63}, + {"0x8000000000000000", 0x8000000000000000, 63}, + {"0x8000300000000000", 0x8000300000000000, 64}, +}; + +KUNIT_ARRAY_PARAM_DESC(order_long, order_long_test_cases, str); + +static void test_get_count_order_long(struct kunit *test) +{ + const struct order_long_test_case *params = test->param_value; + + KUNIT_EXPECT_EQ(test, get_count_order_long(params->count), params->expected); +} +#endif + +static struct kunit_case bitops_test_cases[] = { + KUNIT_CASE_PARAM(test_set_bit_clear_bit, bitops_gen_params), + KUNIT_CASE_PARAM(test_get_count_order, order_gen_params), +#ifdef CONFIG_64BIT + KUNIT_CASE_PARAM(test_get_count_order_long, order_long_gen_params), +#endif + {}, +}; + +static struct kunit_suite bitops_test_suite = { + .name = "bitops", + .test_cases = bitops_test_cases, +}; + +kunit_test_suite(bitops_test_suite); + +MODULE_AUTHOR("Jesse Brandeburg "); +MODULE_AUTHOR("Wei Yang "); +MODULE_AUTHOR("Ryota Sakamoto "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Bit testing module"); -- cgit From 92010ab6dbacc0e0f3566d92a84ff00a939e7fd4 Mon Sep 17 00:00:00 2001 From: Lee Yongjun Date: Tue, 20 Jan 2026 22:18:27 +0900 Subject: lib/find_bit: fix uninitialized variable use in FIND_NTH_BIT In the FIND_NTH_BIT macro, if the 'size' parameter is 0, both the loop conditions and the modulo condition are not met. Consequently, the 'tmp' variable remains uninitialized before being used in the 'found' label. This results in the following smatch errors: lib/find_bit.c:164 __find_nth_bit() error: uninitialized symbol 'tmp'. lib/find_bit.c:171 __find_nth_and_bit() error: uninitialized symbol 'tmp'. lib/find_bit.c:178 __find_nth_andnot_bit() error: uninitialized symbol 'tmp'. lib/find_bit.c:187 __find_nth_and_andnot_bit() error: uninitialized symbol 'tmp'. Initialize 'tmp' to 0 to ensure that fns() operates on a zeroed value (no bits set) when size is 0, preventing the use of garbage values. [Yury: size == 0 is generally a sign of error on client side, and in this case, any returned value is OK because the returned value would be greater than 'size'. Applying the patch to reduce the checker noise.] Signed-off-by: Lee Yongjun Signed-off-by: Yury Norov --- lib/find_bit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/find_bit.c b/lib/find_bit.c index d4b5a29e3e72..5a0066c26d9a 100644 --- a/lib/find_bit.c +++ b/lib/find_bit.c @@ -71,7 +71,7 @@ out: \ #define FIND_NTH_BIT(FETCH, size, num) \ ({ \ - unsigned long sz = (size), nr = (num), idx, w, tmp; \ + unsigned long sz = (size), nr = (num), idx, w, tmp = 0; \ \ for (idx = 0; (idx + 1) * BITS_PER_LONG <= sz; idx++) { \ if (idx * BITS_PER_LONG + nr >= sz) \ -- cgit From 6711069dd72fcbafe010fb16be504364e5ced190 Mon Sep 17 00:00:00 2001 From: Ryota Sakamoto Date: Fri, 23 Jan 2026 00:40:41 +0900 Subject: lib/tests: extend KUnit test for bitops with more cases Extend a KUnit test suite for the bitops API to cover more APIs from include/asm-generic/bitops/instrumented-atomic.h. - change_bit() - test_and_set_bit() - test_and_clear_bit() - test_and_change_bit() Verified on x86_64, i386, and arm64 architectures. Sample KUnit output: KTAP version 1 # Subtest: test_change_bit ok 1 BITOPS_4 ok 2 BITOPS_7 ok 3 BITOPS_11 ok 4 BITOPS_31 ok 5 BITOPS_88 # test_change_bit: pass:5 fail:0 skip:0 total:5 ok 2 test_change_bit KTAP version 1 # Subtest: test_test_and_set_bit_test_and_clear_bit ok 1 BITOPS_4 ok 2 BITOPS_7 ok 3 BITOPS_11 ok 4 BITOPS_31 ok 5 BITOPS_88 # test_test_and_set_bit_test_and_clear_bit: pass:5 fail:0 skip:0 total:5 ok 3 test_test_and_set_bit_test_and_clear_bit KTAP version 1 # Subtest: test_test_and_change_bit ok 1 BITOPS_4 ok 2 BITOPS_7 ok 3 BITOPS_11 ok 4 BITOPS_31 ok 5 BITOPS_88 # test_test_and_change_bit: pass:5 fail:0 skip:0 total:5 ok 4 test_test_and_change_bit Signed-off-by: Ryota Sakamoto Signed-off-by: Yury Norov --- lib/Kconfig.debug | 4 +-- lib/tests/bitops_kunit.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e4f8ba2f9f32..c59b12cf633a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2660,8 +2660,8 @@ config BITOPS_KUNIT This option enables the KUnit test for the bitops library which provides functions for bit operations. - Note that this is a partial copy of the original test_bitops module. - For the full coverage, enable TEST_BITOPS. + Note that this is derived from the original test_bitops module. + For micro-benchmarks and compiler warning checks, enable TEST_BITOPS. If unsure, say N. diff --git a/lib/tests/bitops_kunit.c b/lib/tests/bitops_kunit.c index 5c47a1276061..7fd9d697f131 100644 --- a/lib/tests/bitops_kunit.c +++ b/lib/tests/bitops_kunit.c @@ -66,6 +66,66 @@ static void test_set_bit_clear_bit(struct kunit *test) KUNIT_EXPECT_EQ(test, bit_set, BITOPS_LENGTH); } +static void test_change_bit(struct kunit *test) +{ + const struct bitops_test_case *params = test->param_value; + DECLARE_BITMAP(bitmap, BITOPS_LENGTH); + int bit_set; + + bitmap_zero(bitmap, BITOPS_LENGTH); + + change_bit(params->nr, bitmap); + KUNIT_EXPECT_TRUE(test, test_bit(params->nr, bitmap)); + + change_bit(params->nr, bitmap); + KUNIT_EXPECT_FALSE(test, test_bit(params->nr, bitmap)); + + bit_set = find_first_bit(bitmap, BITOPS_LENGTH); + KUNIT_EXPECT_EQ(test, bit_set, BITOPS_LENGTH); +} + +static void test_test_and_set_bit_test_and_clear_bit(struct kunit *test) +{ + const struct bitops_test_case *params = test->param_value; + DECLARE_BITMAP(bitmap, BITOPS_LENGTH); + int bit_set; + + bitmap_zero(bitmap, BITOPS_LENGTH); + + KUNIT_EXPECT_FALSE(test, test_and_set_bit(params->nr, bitmap)); + KUNIT_EXPECT_TRUE(test, test_bit(params->nr, bitmap)); + + KUNIT_EXPECT_TRUE(test, test_and_set_bit(params->nr, bitmap)); + KUNIT_EXPECT_TRUE(test, test_bit(params->nr, bitmap)); + + KUNIT_EXPECT_TRUE(test, test_and_clear_bit(params->nr, bitmap)); + KUNIT_EXPECT_FALSE(test, test_bit(params->nr, bitmap)); + + KUNIT_EXPECT_FALSE(test, test_and_clear_bit(params->nr, bitmap)); + KUNIT_EXPECT_FALSE(test, test_bit(params->nr, bitmap)); + + bit_set = find_first_bit(bitmap, BITOPS_LENGTH); + KUNIT_EXPECT_EQ(test, bit_set, BITOPS_LENGTH); +} + +static void test_test_and_change_bit(struct kunit *test) +{ + const struct bitops_test_case *params = test->param_value; + DECLARE_BITMAP(bitmap, BITOPS_LENGTH); + int bit_set; + + bitmap_zero(bitmap, BITOPS_LENGTH); + + KUNIT_EXPECT_FALSE(test, test_and_change_bit(params->nr, bitmap)); + KUNIT_EXPECT_TRUE(test, test_bit(params->nr, bitmap)); + + KUNIT_EXPECT_TRUE(test, test_and_change_bit(params->nr, bitmap)); + KUNIT_EXPECT_FALSE(test, test_bit(params->nr, bitmap)); + + bit_set = find_first_bit(bitmap, BITOPS_LENGTH); + KUNIT_EXPECT_EQ(test, bit_set, BITOPS_LENGTH); +} + struct order_test_case { const char *str; const unsigned int count; @@ -121,6 +181,9 @@ static void test_get_count_order_long(struct kunit *test) static struct kunit_case bitops_test_cases[] = { KUNIT_CASE_PARAM(test_set_bit_clear_bit, bitops_gen_params), + KUNIT_CASE_PARAM(test_change_bit, bitops_gen_params), + KUNIT_CASE_PARAM(test_test_and_set_bit_test_and_clear_bit, bitops_gen_params), + KUNIT_CASE_PARAM(test_test_and_change_bit, bitops_gen_params), KUNIT_CASE_PARAM(test_get_count_order, order_gen_params), #ifdef CONFIG_64BIT KUNIT_CASE_PARAM(test_get_count_order_long, order_long_gen_params), -- cgit