Skip to content

Commit

Permalink
lib/test_bitmap: add tests for bitmap_{read,write}()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ramosian-glider authored and davem330 committed Apr 1, 2024
1 parent 63c1582 commit 991e558
Showing 1 changed file with 172 additions and 7 deletions.
179 changes: 172 additions & 7 deletions lib/test_bitmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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__)
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down

0 comments on commit 991e558

Please sign in to comment.