lib/test_bitmap: add tests for bitmap_{read,write}()

Add basic tests ensuring that values can be added at arbitrary positions
of the bitmap, including those spanning into the adjacent unsigned
longs.

Two new performance tests, test_bitmap_read_perf() and
test_bitmap_write_perf(), can be used to assess future performance
improvements of bitmap_read() and bitmap_write():

[    0.431119][    T1] test_bitmap: Time spent in test_bitmap_read_perf:	615253
[    0.433197][    T1] test_bitmap: Time spent in test_bitmap_write_perf:	916313

(numbers from a Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz machine running
QEMU).

Signed-off-by: Alexander Potapenko <glider@google.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Acked-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Yury Norov <yury.norov@gmail.com>
Signed-off-by: Alexander Lobakin <aleksander.lobakin@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Alexander Potapenko 2024-03-27 16:23:39 +01:00 committed by David S. Miller
parent 63c15822b8
commit 991e558364
1 changed files with 172 additions and 7 deletions

View File

@ -60,18 +60,17 @@ static const unsigned long exp3_1_0[] __initconst = {
};
static bool __init
__check_eq_uint(const char *srcfile, unsigned int line,
const unsigned int exp_uint, unsigned int x)
__check_eq_ulong(const char *srcfile, unsigned int line,
const unsigned long exp_ulong, unsigned long x)
{
if (exp_uint != x) {
pr_err("[%s:%u] expected %u, got %u\n",
srcfile, line, exp_uint, x);
if (exp_ulong != x) {
pr_err("[%s:%u] expected %lu, got %lu\n",
srcfile, line, exp_ulong, x);
return false;
}
return true;
}
static bool __init
__check_eq_bitmap(const char *srcfile, unsigned int line,
const unsigned long *exp_bmap, const unsigned long *bmap,
@ -185,7 +184,8 @@ __check_eq_str(const char *srcfile, unsigned int line,
result; \
})
#define expect_eq_uint(...) __expect_eq(uint, ##__VA_ARGS__)
#define expect_eq_ulong(...) __expect_eq(ulong, ##__VA_ARGS__)
#define expect_eq_uint(x, y) expect_eq_ulong((unsigned int)(x), (unsigned int)(y))
#define expect_eq_bitmap(...) __expect_eq(bitmap, ##__VA_ARGS__)
#define expect_eq_pbl(...) __expect_eq(pbl, ##__VA_ARGS__)
#define expect_eq_u32_array(...) __expect_eq(u32_array, ##__VA_ARGS__)
@ -1286,6 +1286,168 @@ static void __init test_bitmap_const_eval(void)
BUILD_BUG_ON(~var != ~BIT(25));
}
/*
* Test bitmap should be big enough to include the cases when start is not in
* the first word, and start+nbits lands in the following word.
*/
#define TEST_BIT_LEN (1000)
/*
* Helper function to test bitmap_write() overwriting the chosen byte pattern.
*/
static void __init test_bitmap_write_helper(const char *pattern)
{
DECLARE_BITMAP(bitmap, TEST_BIT_LEN);
DECLARE_BITMAP(exp_bitmap, TEST_BIT_LEN);
DECLARE_BITMAP(pat_bitmap, TEST_BIT_LEN);
unsigned long w, r, bit;
int i, n, nbits;
/*
* Only parse the pattern once and store the result in the intermediate
* bitmap.
*/
bitmap_parselist(pattern, pat_bitmap, TEST_BIT_LEN);
/*
* Check that writing a single bit does not accidentally touch the
* adjacent bits.
*/
for (i = 0; i < TEST_BIT_LEN; i++) {
bitmap_copy(bitmap, pat_bitmap, TEST_BIT_LEN);
bitmap_copy(exp_bitmap, pat_bitmap, TEST_BIT_LEN);
for (bit = 0; bit <= 1; bit++) {
bitmap_write(bitmap, bit, i, 1);
__assign_bit(i, exp_bitmap, bit);
expect_eq_bitmap(exp_bitmap, bitmap,
TEST_BIT_LEN);
}
}
/* Ensure writing 0 bits does not change anything. */
bitmap_copy(bitmap, pat_bitmap, TEST_BIT_LEN);
bitmap_copy(exp_bitmap, pat_bitmap, TEST_BIT_LEN);
for (i = 0; i < TEST_BIT_LEN; i++) {
bitmap_write(bitmap, ~0UL, i, 0);
expect_eq_bitmap(exp_bitmap, bitmap, TEST_BIT_LEN);
}
for (nbits = BITS_PER_LONG; nbits >= 1; nbits--) {
w = IS_ENABLED(CONFIG_64BIT) ? 0xdeadbeefdeadbeefUL
: 0xdeadbeefUL;
w >>= (BITS_PER_LONG - nbits);
for (i = 0; i <= TEST_BIT_LEN - nbits; i++) {
bitmap_copy(bitmap, pat_bitmap, TEST_BIT_LEN);
bitmap_copy(exp_bitmap, pat_bitmap, TEST_BIT_LEN);
for (n = 0; n < nbits; n++)
__assign_bit(i + n, exp_bitmap, w & BIT(n));
bitmap_write(bitmap, w, i, nbits);
expect_eq_bitmap(exp_bitmap, bitmap, TEST_BIT_LEN);
r = bitmap_read(bitmap, i, nbits);
expect_eq_ulong(r, w);
}
}
}
static void __init test_bitmap_read_write(void)
{
unsigned char *pattern[3] = {"", "all:1/2", "all"};
DECLARE_BITMAP(bitmap, TEST_BIT_LEN);
unsigned long zero_bits = 0, bits_per_long = BITS_PER_LONG;
unsigned long val;
int i, pi;
/*
* Reading/writing zero bits should not crash the kernel.
* READ_ONCE() prevents constant folding.
*/
bitmap_write(NULL, 0, 0, READ_ONCE(zero_bits));
/* Return value of bitmap_read() is undefined here. */
bitmap_read(NULL, 0, READ_ONCE(zero_bits));
/*
* Reading/writing more than BITS_PER_LONG bits should not crash the
* kernel. READ_ONCE() prevents constant folding.
*/
bitmap_write(NULL, 0, 0, READ_ONCE(bits_per_long) + 1);
/* Return value of bitmap_read() is undefined here. */
bitmap_read(NULL, 0, READ_ONCE(bits_per_long) + 1);
/*
* Ensure that bitmap_read() reads the same value that was previously
* written, and two consequent values are correctly merged.
* The resulting bit pattern is asymmetric to rule out possible issues
* with bit numeration order.
*/
for (i = 0; i < TEST_BIT_LEN - 7; i++) {
bitmap_zero(bitmap, TEST_BIT_LEN);
bitmap_write(bitmap, 0b10101UL, i, 5);
val = bitmap_read(bitmap, i, 5);
expect_eq_ulong(0b10101UL, val);
bitmap_write(bitmap, 0b101UL, i + 5, 3);
val = bitmap_read(bitmap, i + 5, 3);
expect_eq_ulong(0b101UL, val);
val = bitmap_read(bitmap, i, 8);
expect_eq_ulong(0b10110101UL, val);
}
for (pi = 0; pi < ARRAY_SIZE(pattern); pi++)
test_bitmap_write_helper(pattern[pi]);
}
static void __init test_bitmap_read_perf(void)
{
DECLARE_BITMAP(bitmap, TEST_BIT_LEN);
unsigned int cnt, nbits, i;
unsigned long val;
ktime_t time;
bitmap_fill(bitmap, TEST_BIT_LEN);
time = ktime_get();
for (cnt = 0; cnt < 5; cnt++) {
for (nbits = 1; nbits <= BITS_PER_LONG; nbits++) {
for (i = 0; i < TEST_BIT_LEN; i++) {
if (i + nbits > TEST_BIT_LEN)
break;
/*
* Prevent the compiler from optimizing away the
* bitmap_read() by using its value.
*/
WRITE_ONCE(val, bitmap_read(bitmap, i, nbits));
}
}
}
time = ktime_get() - time;
pr_err("Time spent in %s:\t%llu\n", __func__, time);
}
static void __init test_bitmap_write_perf(void)
{
DECLARE_BITMAP(bitmap, TEST_BIT_LEN);
unsigned int cnt, nbits, i;
unsigned long val = 0xfeedface;
ktime_t time;
bitmap_zero(bitmap, TEST_BIT_LEN);
time = ktime_get();
for (cnt = 0; cnt < 5; cnt++) {
for (nbits = 1; nbits <= BITS_PER_LONG; nbits++) {
for (i = 0; i < TEST_BIT_LEN; i++) {
if (i + nbits > TEST_BIT_LEN)
break;
bitmap_write(bitmap, val, i, nbits);
}
}
}
time = ktime_get() - time;
pr_err("Time spent in %s:\t%llu\n", __func__, time);
}
#undef TEST_BIT_LEN
static void __init selftest(void)
{
test_zero_clear();
@ -1303,6 +1465,9 @@ static void __init selftest(void)
test_bitmap_cut();
test_bitmap_print_buf();
test_bitmap_const_eval();
test_bitmap_read_write();
test_bitmap_read_perf();
test_bitmap_write_perf();
test_find_nth_bit();
test_for_each_set_bit();