]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bitmap: add bitmap_weight_from()
authorYury Norov <ynorov@nvidia.com>
Mon, 22 Dec 2025 19:11:37 +0000 (14:11 -0500)
committerYury Norov <ynorov@nvidia.com>
Mon, 23 Mar 2026 17:33:51 +0000 (13:33 -0400)
The function calculates a Hamming weight of a bitmap starting from an
arbitrary bit.

Signed-off-by: Yury Norov <ynorov@nvidia.com>
include/linux/bitmap.h
lib/test_bitmap.c

index b0395e4ccf9036d593a7b818297f9658f3c402ab..9c0d1de4435003d4167e9fabaacca41a43bc9b65 100644 (file)
@@ -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)
 {
index 1c352c1edfa5fcfcc41055f82d40e2882f3520b0..cd4cb36e42a51c7a1e0b78413b5c8c9e48e5e9ea 100644 (file)
@@ -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();