From: Yury Norov Date: Mon, 22 Dec 2025 19:11:37 +0000 (-0500) Subject: bitmap: add bitmap_weight_from() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf31ddc14f8c6bcd4987c31fe2bc9e93e433b41a;p=thirdparty%2Fkernel%2Flinux.git bitmap: add bitmap_weight_from() The function calculates a Hamming weight of a bitmap starting from an arbitrary bit. Signed-off-by: Yury Norov --- diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index b0395e4ccf903..9c0d1de443500 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -57,6 +57,7 @@ struct device; * bitmap_weight(src, nbits) Hamming Weight: number set bits * bitmap_weight_and(src1, src2, nbits) Hamming Weight of and'ed bitmap * bitmap_weight_andnot(src1, src2, nbits) Hamming Weight of andnot'ed bitmap + * bitmap_weight_from(src, start, end) Hamming Weight starting from @start * bitmap_set(dst, pos, nbits) Set specified bit area * bitmap_clear(dst, pos, nbits) Clear specified bit area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area @@ -479,6 +480,38 @@ unsigned long bitmap_weight_andnot(const unsigned long *src1, return __bitmap_weight_andnot(src1, src2, nbits); } +/** + * bitmap_weight_from - Hamming weight for a memory region + * @bitmap: The base address + * @start: The bitnumber to starts weighting + * @end: the bitmap size in bits + * + * Returns the number of set bits in the region. If @start >= @end, + * return >= end. + */ +static __always_inline +unsigned long bitmap_weight_from(const unsigned long *bitmap, + unsigned int start, unsigned int end) +{ + unsigned long w; + + if (unlikely(start >= end)) + return end; + + if (small_const_nbits(end)) + return hweight_long(*bitmap & GENMASK(end - 1, start)); + + bitmap += start / BITS_PER_LONG; + /* Opencode round_down() to not include math.h */ + end -= start & ~(BITS_PER_LONG - 1); + start %= BITS_PER_LONG; + w = bitmap_weight(bitmap, end); + if (start) + w -= hweight_long(*bitmap & BITMAP_LAST_WORD_MASK(start)); + + return w; +} + static __always_inline void bitmap_set(unsigned long *map, unsigned int start, unsigned int nbits) { diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c index 1c352c1edfa5f..cd4cb36e42a51 100644 --- a/lib/test_bitmap.c +++ b/lib/test_bitmap.c @@ -854,6 +854,31 @@ static void __init test_for_each_set_bit_from(void) } } +static void __init test_bitmap_weight(void) +{ + unsigned int bit, w1, w2, w; + DECLARE_BITMAP(b, 30); + + bitmap_parselist("all:1/2", b, 30); + + /* Test inline implementation */ + w = bitmap_weight(b, 30); + w1 = bitmap_weight(b, 15); + w2 = bitmap_weight_from(b, 15, 30); + + expect_eq_uint(15, w); + expect_eq_uint(8, w1); + expect_eq_uint(7, w2); + + /* Test outline implementation */ + w = bitmap_weight(exp1, EXP1_IN_BITS); + for (bit = 0; bit < EXP1_IN_BITS; bit++) { + w1 = bitmap_weight(exp1, bit); + w2 = bitmap_weight_from(exp1, bit, EXP1_IN_BITS); + expect_eq_uint(w1 + w2, w); + } +} + static void __init test_for_each_clear_bit(void) { DECLARE_BITMAP(orig, 500); @@ -1444,6 +1469,7 @@ static void __init selftest(void) test_bitmap_const_eval(); test_bitmap_read_write(); test_bitmap_read_perf(); + test_bitmap_weight(); test_bitmap_write_perf(); test_find_nth_bit();