Skip to content

Commit

Permalink
bitfield: add tests
Browse files Browse the repository at this point in the history
Add tests for the bitfield helpers. The constant ones will all
be folded to nothing by the compiler (if everything is correct
in the header file), and the variable ones do some tests against
open-coding the necessary shifts.

A few test cases that should fail/warn compilation are provided
under ifdef.

Suggested-by: Andy Shevchenko <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
Signed-off-by: Kalle Valo <[email protected]>
  • Loading branch information
jmberg authored and Kalle Valo committed Jun 27, 2018
1 parent 37a3862 commit 0e2dc70
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 0 deletions.
7 changes: 7 additions & 0 deletions lib/Kconfig.debug
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,13 @@ config TEST_BITMAP

If unsure, say N.

config TEST_BITFIELD
tristate "Test bitfield functions at runtime"
help
Enable this option to test the bitfield functions at boot.

If unsure, say N.

config TEST_UUID
tristate "Test functions located in the uuid module at runtime"

Expand Down
1 change: 1 addition & 0 deletions lib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_keys.o
obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_PRINTF) += test_printf.o
obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
obj-$(CONFIG_TEST_BITFIELD) += test_bitfield.o
obj-$(CONFIG_TEST_UUID) += test_uuid.o
obj-$(CONFIG_TEST_PARMAN) += test_parman.o
obj-$(CONFIG_TEST_KMOD) += test_kmod.o
Expand Down
168 changes: 168 additions & 0 deletions lib/test_bitfield.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Test cases for bitfield helpers.
*/

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/bitfield.h>

#define CHECK_ENC_GET_U(tp, v, field, res) do { \
{ \
u##tp _res; \
\
_res = u##tp##_encode_bits(v, field); \
if (_res != res) { \
pr_warn("u" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != " #res "\n",\
(u64)_res); \
return -EINVAL; \
} \
if (u##tp##_get_bits(_res, field) != v) \
return -EINVAL; \
} \
} while (0)

#define CHECK_ENC_GET_LE(tp, v, field, res) do { \
{ \
__le##tp _res; \
\
_res = le##tp##_encode_bits(v, field); \
if (_res != cpu_to_le##tp(res)) { \
pr_warn("le" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx\n",\
(u64)le##tp##_to_cpu(_res), \
(u64)(res)); \
return -EINVAL; \
} \
if (le##tp##_get_bits(_res, field) != v) \
return -EINVAL; \
} \
} while (0)

#define CHECK_ENC_GET_BE(tp, v, field, res) do { \
{ \
__be##tp _res; \
\
_res = be##tp##_encode_bits(v, field); \
if (_res != cpu_to_be##tp(res)) { \
pr_warn("be" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx\n",\
(u64)be##tp##_to_cpu(_res), \
(u64)(res)); \
return -EINVAL; \
} \
if (be##tp##_get_bits(_res, field) != v) \
return -EINVAL; \
} \
} while (0)

#define CHECK_ENC_GET(tp, v, field, res) do { \
CHECK_ENC_GET_U(tp, v, field, res); \
CHECK_ENC_GET_LE(tp, v, field, res); \
CHECK_ENC_GET_BE(tp, v, field, res); \
} while (0)

static int test_constants(void)
{
/*
* NOTE
* This whole function compiles (or at least should, if everything
* is going according to plan) to nothing after optimisation.
*/

CHECK_ENC_GET(16, 1, 0x000f, 0x0001);
CHECK_ENC_GET(16, 3, 0x00f0, 0x0030);
CHECK_ENC_GET(16, 5, 0x0f00, 0x0500);
CHECK_ENC_GET(16, 7, 0xf000, 0x7000);
CHECK_ENC_GET(16, 14, 0x000f, 0x000e);
CHECK_ENC_GET(16, 15, 0x00f0, 0x00f0);

CHECK_ENC_GET_U(8, 1, 0x0f, 0x01);
CHECK_ENC_GET_U(8, 3, 0xf0, 0x30);
CHECK_ENC_GET_U(8, 14, 0x0f, 0x0e);
CHECK_ENC_GET_U(8, 15, 0xf0, 0xf0);

CHECK_ENC_GET(32, 1, 0x00000f00, 0x00000100);
CHECK_ENC_GET(32, 3, 0x0000f000, 0x00003000);
CHECK_ENC_GET(32, 5, 0x000f0000, 0x00050000);
CHECK_ENC_GET(32, 7, 0x00f00000, 0x00700000);
CHECK_ENC_GET(32, 14, 0x0f000000, 0x0e000000);
CHECK_ENC_GET(32, 15, 0xf0000000, 0xf0000000);

CHECK_ENC_GET(64, 1, 0x00000f0000000000ull, 0x0000010000000000ull);
CHECK_ENC_GET(64, 3, 0x0000f00000000000ull, 0x0000300000000000ull);
CHECK_ENC_GET(64, 5, 0x000f000000000000ull, 0x0005000000000000ull);
CHECK_ENC_GET(64, 7, 0x00f0000000000000ull, 0x0070000000000000ull);
CHECK_ENC_GET(64, 14, 0x0f00000000000000ull, 0x0e00000000000000ull);
CHECK_ENC_GET(64, 15, 0xf000000000000000ull, 0xf000000000000000ull);

return 0;
}

#define CHECK(tp, mask) do { \
u64 v; \
\
for (v = 0; v < 1 << hweight32(mask); v++) \
if (tp##_encode_bits(v, mask) != v << __ffs64(mask)) \
return -EINVAL; \
} while (0)

static int test_variables(void)
{
CHECK(u8, 0x0f);
CHECK(u8, 0xf0);
CHECK(u8, 0x38);

CHECK(u16, 0x0038);
CHECK(u16, 0x0380);
CHECK(u16, 0x3800);
CHECK(u16, 0x8000);

CHECK(u32, 0x80000000);
CHECK(u32, 0x7f000000);
CHECK(u32, 0x07e00000);
CHECK(u32, 0x00018000);

CHECK(u64, 0x8000000000000000ull);
CHECK(u64, 0x7f00000000000000ull);
CHECK(u64, 0x0001800000000000ull);
CHECK(u64, 0x0000000080000000ull);
CHECK(u64, 0x000000007f000000ull);
CHECK(u64, 0x0000000018000000ull);
CHECK(u64, 0x0000001f8000000ull);

return 0;
}

static int __init test_bitfields(void)
{
int ret = test_constants();

if (ret) {
pr_warn("constant tests failed!\n");
return ret;
}

ret = test_variables();
if (ret) {
pr_warn("variable tests failed!\n");
return ret;
}

#ifdef TEST_BITFIELD_COMPILE
/* these should fail compilation */
CHECK_ENC_GET(16, 16, 0x0f00, 0x1000);
u32_encode_bits(7, 0x06000000);

/* this should at least give a warning */
u16_encode_bits(0, 0x60000);
#endif

pr_info("tests passed\n");

return 0;
}
module_init(test_bitfields)

MODULE_AUTHOR("Johannes Berg <[email protected]>");
MODULE_LICENSE("GPL");

0 comments on commit 0e2dc70

Please sign in to comment.