* [PATCH v3 0/5] lib/find: add find_nth_bit()
@ 2022-09-18 3:07 Yury Norov
2022-09-18 3:07 ` [PATCH v3 1/6] lib/bitmap: don't call __bitmap_weight() in kernel code Yury Norov
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
Kernel lacks for a function that searches for Nth bit in a bitmap.
Usually people do it like this:
for_each_set_bit(bit, mask, size)
if (n-- == 0)
return bit;
Which is not so elegant and very slow.
This series adds fast routines for this task, and applies them where
appropriate.
v1: https://lore.kernel.org/lkml/20220706182300.70862-4-yury.norov@gmail.com/T/
v2: https://lore.kernel.org/lkml/CAAH8bW_RYG_Tbpip=BkSFAymDm2y3jJBqTi0PJWA=H-a-L_3tg@mail.gmail.com/T/
v3:
- add bitmap_weight_and() and use it in cpumask_local_spread();
- rework find_nth_bit() family: similarly to [1], introduce
FIND_NTH_BIT(), and optimize the function to return earlier when
it's known that the rest of bitmap can't meet the requirement.
- patch "lib/nodemask: inline next_node_in() and node_random()" has
been merged, so drop it from this series.
On top of:
[1] https://lore.kernel.org/lkml/20220915020730.852234-1-yury.norov@gmail.com/T/
Yury Norov (6):
lib/bitmap: don't call __bitmap_weight() in kernel code
lib/bitmap: add bitmap_weight_and()
lib: add find_nth(,and,andnot)_bit()
lib/bitmap: add tests for find_nth_bit()
lib/bitmap: remove bitmap_ord_to_pos
cpumask: add cpumask_nth_{,and,andnot}
fs/ntfs3/bitmap.c | 4 +-
include/linux/bitmap.h | 13 +++++-
include/linux/bitops.h | 19 +++++++++
include/linux/cpumask.h | 55 +++++++++++++++++++++++++
include/linux/find.h | 86 ++++++++++++++++++++++++++++++++++++++++
include/linux/nodemask.h | 3 +-
lib/bitmap.c | 68 ++++++++++++-------------------
lib/cpumask.c | 28 ++++++-------
lib/find_bit.c | 44 ++++++++++++++++++++
lib/find_bit_benchmark.c | 18 +++++++++
lib/test_bitmap.c | 47 +++++++++++++++++++++-
11 files changed, 320 insertions(+), 65 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 1/6] lib/bitmap: don't call __bitmap_weight() in kernel code
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
@ 2022-09-18 3:07 ` Yury Norov
2022-09-18 3:07 ` [PATCH v3 2/6] lib/bitmap: add bitmap_weight_and() Yury Norov
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
__bitmap_weight() is not to be used directly in the kernel code because
it's a helper for bitmap_weight(). Switch everything to bitmap_weight().
Signed-off-by: Yury Norov <yury.norov@gmail.com>
---
fs/ntfs3/bitmap.c | 4 ++--
lib/bitmap.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/fs/ntfs3/bitmap.c b/fs/ntfs3/bitmap.c
index 5d44ceac855b..e92bbd754365 100644
--- a/fs/ntfs3/bitmap.c
+++ b/fs/ntfs3/bitmap.c
@@ -560,7 +560,7 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
buf = (ulong *)bh->b_data;
- used = __bitmap_weight(buf, wbits);
+ used = bitmap_weight(buf, wbits);
if (used < wbits) {
frb = wbits - used;
wnd->free_bits[iw] = frb;
@@ -1364,7 +1364,7 @@ int wnd_extend(struct wnd_bitmap *wnd, size_t new_bits)
buf = (ulong *)bh->b_data;
__bitmap_clear(buf, b0, blocksize * 8 - b0);
- frb = wbits - __bitmap_weight(buf, wbits);
+ frb = wbits - bitmap_weight(buf, wbits);
wnd->total_zeroes += frb - wnd->free_bits[iw];
wnd->free_bits[iw] = frb;
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 488e6c3e5acc..d56e275db73e 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -953,7 +953,7 @@ static int bitmap_pos_to_ord(const unsigned long *buf, unsigned int pos, unsigne
if (pos >= nbits || !test_bit(pos, buf))
return -1;
- return __bitmap_weight(buf, pos);
+ return bitmap_weight(buf, pos);
}
/**
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 2/6] lib/bitmap: add bitmap_weight_and()
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
2022-09-18 3:07 ` [PATCH v3 1/6] lib/bitmap: don't call __bitmap_weight() in kernel code Yury Norov
@ 2022-09-18 3:07 ` Yury Norov
2022-09-18 3:07 ` [PATCH v3 3/6] lib: add find_nth{,_and,_andnot}_bit() Yury Norov
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
The function calculates Hamming weight of (bitmap1 & bitmap2). Now we
have to do like this:
tmp = bitmap_alloc(nbits);
bitmap_and(tmp, map1, map2, nbits);
weight = bitmap_weight(tmp, nbits);
bitmap_free(tmp);
This requires additional memory, adds pressure on alloc subsystem, and
way less cache-friendly than just:
weight = bitmap_weight_and(map1, map2, nbits);
The following patches apply it for cpumask functions.
Signed-off-by: Yury Norov <yury.norov@gmail.com>
---
include/linux/bitmap.h | 12 ++++++++++++
include/linux/cpumask.h | 11 +++++++++++
lib/bitmap.c | 30 +++++++++++++++++++++---------
3 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index f65410a49fda..b2aef45af0db 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -51,6 +51,7 @@ struct device;
* bitmap_empty(src, nbits) Are all bits zero in *src?
* bitmap_full(src, nbits) Are all bits set in *src?
* bitmap_weight(src, nbits) Hamming Weight: number set bits
+ * bitmap_weight_and(src1, src2, nbits) Hamming Weight of and'ed bitmap
* 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
@@ -164,6 +165,8 @@ bool __bitmap_intersects(const unsigned long *bitmap1,
bool __bitmap_subset(const unsigned long *bitmap1,
const unsigned long *bitmap2, unsigned int nbits);
unsigned int __bitmap_weight(const unsigned long *bitmap, unsigned int nbits);
+unsigned int __bitmap_weight_and(const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int nbits);
void __bitmap_set(unsigned long *map, unsigned int start, int len);
void __bitmap_clear(unsigned long *map, unsigned int start, int len);
@@ -439,6 +442,15 @@ unsigned int bitmap_weight(const unsigned long *src, unsigned int nbits)
return __bitmap_weight(src, nbits);
}
+static __always_inline
+unsigned long bitmap_weight_and(const unsigned long *src1,
+ const unsigned long *src2, unsigned int nbits)
+{
+ if (small_const_nbits(nbits))
+ return hweight_long(*src1 & *src2 & BITMAP_LAST_WORD_MASK(nbits));
+ return __bitmap_weight_and(src1, src2, nbits);
+}
+
static __always_inline void bitmap_set(unsigned long *map, unsigned int start,
unsigned int nbits)
{
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index e8ad12b5b9d2..31bf08c786e3 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -586,6 +586,17 @@ static inline unsigned int cpumask_weight(const struct cpumask *srcp)
return bitmap_weight(cpumask_bits(srcp), nr_cpumask_bits);
}
+/**
+ * cpumask_weight_and - Count of bits in (*srcp1 & *srcp2)
+ * @srcp1: the cpumask to count bits (< nr_cpu_ids) in.
+ * @srcp2: the cpumask to count bits (< nr_cpu_ids) in.
+ */
+static inline unsigned int cpumask_weight_and(const struct cpumask *srcp1,
+ const struct cpumask *srcp2)
+{
+ return bitmap_weight_and(cpumask_bits(srcp1), cpumask_bits(srcp2), nr_cpumask_bits);
+}
+
/**
* cpumask_shift_right - *dstp = *srcp >> n
* @dstp: the cpumask result
diff --git a/lib/bitmap.c b/lib/bitmap.c
index d56e275db73e..3fc2e338ec30 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -333,20 +333,32 @@ bool __bitmap_subset(const unsigned long *bitmap1,
}
EXPORT_SYMBOL(__bitmap_subset);
+#define BITMAP_WEIGHT(FETCH, bits) \
+({ \
+ unsigned int __bits = (bits), idx, w = 0; \
+ \
+ for (idx = 0; idx < __bits / BITS_PER_LONG; idx++) \
+ w += hweight_long(FETCH); \
+ \
+ if (__bits % BITS_PER_LONG) \
+ w += hweight_long((FETCH) & BITMAP_LAST_WORD_MASK(__bits)); \
+ \
+ w; \
+})
+
unsigned int __bitmap_weight(const unsigned long *bitmap, unsigned int bits)
{
- unsigned int k, lim = bits/BITS_PER_LONG, w = 0;
-
- for (k = 0; k < lim; k++)
- w += hweight_long(bitmap[k]);
-
- if (bits % BITS_PER_LONG)
- w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits));
-
- return w;
+ return BITMAP_WEIGHT(bitmap[idx], bits);
}
EXPORT_SYMBOL(__bitmap_weight);
+unsigned int __bitmap_weight_and(const unsigned long *bitmap1,
+ const unsigned long *bitmap2, unsigned int bits)
+{
+ return BITMAP_WEIGHT(bitmap1[idx] & bitmap2[idx], bits);
+}
+EXPORT_SYMBOL(__bitmap_weight_and);
+
void __bitmap_set(unsigned long *map, unsigned int start, int len)
{
unsigned long *p = map + BIT_WORD(start);
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 3/6] lib: add find_nth{,_and,_andnot}_bit()
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
2022-09-18 3:07 ` [PATCH v3 1/6] lib/bitmap: don't call __bitmap_weight() in kernel code Yury Norov
2022-09-18 3:07 ` [PATCH v3 2/6] lib/bitmap: add bitmap_weight_and() Yury Norov
@ 2022-09-18 3:07 ` Yury Norov
2022-09-18 3:07 ` [PATCH v3 4/6] lib/bitmap: add tests for find_nth_bit() Yury Norov
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
Kernel lacks for a function that searches for Nth bit in a bitmap.
Usually people do it like this:
for_each_set_bit(bit, mask, size)
if (n-- == 0)
return bit;
We can do it more efficiently, if we:
1. find a word containing Nth bit, using hweight(); and
2. find the bit, using a helper fns(), that works similarly to
__ffs() and ffz().
fns() is implemented as a simple loop. For x86_64, there's PDEP instruction
to do that: ret = clz(pdep(1 << idx, num)). However, for large bitmaps the
most of improvement comes from using hweight(), so I kept fns() simple.
New find_nth_bit() is ~70 times faster on x86_64/kvm in find_bit benchmark:
find_nth_bit: 7154190 ns, 16411 iterations
for_each_bit: 505493126 ns, 16315 iterations
With all that, a family of 3 new functions is added, and used where
appropriate in the following patches.
Signed-off-by: Yury Norov <yury.norov@gmail.com>
---
Comparing to v2, it adds a path that allows quit earlier if the
condition can't be met:
for (idx = 0; (idx + 1) * BITS_PER_LONG <= sz; idx++) {
+ if (idx * BITS_PER_LONG + nr >= sz)
+ goto out;
...
include/linux/bitops.h | 19 ++++++++++
include/linux/find.h | 86 ++++++++++++++++++++++++++++++++++++++++++
lib/find_bit.c | 44 +++++++++++++++++++++
3 files changed, 149 insertions(+)
diff --git a/include/linux/bitops.h b/include/linux/bitops.h
index 3b89c64bcfd8..d7dd83fafeba 100644
--- a/include/linux/bitops.h
+++ b/include/linux/bitops.h
@@ -247,6 +247,25 @@ static inline unsigned long __ffs64(u64 word)
return __ffs((unsigned long)word);
}
+/**
+ * fns - find N'th set bit in a word
+ * @word: The word to search
+ * @n: Bit to find
+ */
+static inline unsigned long fns(unsigned long word, unsigned int n)
+{
+ unsigned int bit;
+
+ while (word) {
+ bit = __ffs(word);
+ if (n-- == 0)
+ return bit;
+ __clear_bit(bit, &word);
+ }
+
+ return BITS_PER_LONG;
+}
+
/**
* assign_bit - Assign value to a bit in memory
* @nr: the bit to set
diff --git a/include/linux/find.h b/include/linux/find.h
index dead6f53a97b..b100944daba0 100644
--- a/include/linux/find.h
+++ b/include/linux/find.h
@@ -15,6 +15,11 @@ unsigned long _find_next_and_bit(const unsigned long *addr1, const unsigned long
unsigned long _find_next_zero_bit(const unsigned long *addr, unsigned long nbits,
unsigned long start);
extern unsigned long _find_first_bit(const unsigned long *addr, unsigned long size);
+unsigned long __find_nth_bit(const unsigned long *addr, unsigned long size, unsigned long n);
+unsigned long __find_nth_and_bit(const unsigned long *addr1, const unsigned long *addr2,
+ unsigned long size, unsigned long n);
+unsigned long __find_nth_andnot_bit(const unsigned long *addr1, const unsigned long *addr2,
+ unsigned long size, unsigned long n);
extern unsigned long _find_first_and_bit(const unsigned long *addr1,
const unsigned long *addr2, unsigned long size);
extern unsigned long _find_first_zero_bit(const unsigned long *addr, unsigned long size);
@@ -136,6 +141,87 @@ unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
}
#endif
+/**
+ * find_nth_bit - find N'th set bit in a memory region
+ * @addr: The address to start the search at
+ * @size: The maximum number of bits to search
+ * @n: The number of set bit, which position is needed, counting from 0
+ *
+ * The following is semantically equivalent:
+ * idx = find_nth_bit(addr, size, 0);
+ * idx = find_first_bit(addr, size);
+ *
+ * Returns the bit number of the N'th set bit.
+ * If no such, returns @size.
+ */
+static inline
+unsigned long find_nth_bit(const unsigned long *addr, unsigned long size, unsigned long n)
+{
+ if (n >= size)
+ return size;
+
+ if (small_const_nbits(size)) {
+ unsigned long val = *addr & GENMASK(size - 1, 0);
+
+ return val ? fns(val, n) : size;
+ }
+
+ return __find_nth_bit(addr, size, n);
+}
+
+/**
+ * find_nth_and_bit - find N'th set bit in 2 memory regions
+ * @addr1: The 1st address to start the search at
+ * @addr2: The 2nd address to start the search at
+ * @size: The maximum number of bits to search
+ * @n: The number of set bit, which position is needed, counting from 0
+ *
+ * Returns the bit number of the N'th set bit.
+ * If no such, returns @size.
+ */
+static inline
+unsigned long find_nth_and_bit(const unsigned long *addr1, const unsigned long *addr2,
+ unsigned long size, unsigned long n)
+{
+ if (n >= size)
+ return size;
+
+ if (small_const_nbits(size)) {
+ unsigned long val = *addr1 & *addr2 & GENMASK(size - 1, 0);
+
+ return val ? fns(val, n) : size;
+ }
+
+ return __find_nth_and_bit(addr1, addr2, size, n);
+}
+
+/**
+ * find_nth_andnot_bit - find N'th set bit in 2 memory regions,
+ * flipping bits in 2nd region
+ * @addr1: The 1st address to start the search at
+ * @addr2: The 2nd address to start the search at
+ * @size: The maximum number of bits to search
+ * @n: The number of set bit, which position is needed, counting from 0
+ *
+ * Returns the bit number of the N'th set bit.
+ * If no such, returns @size.
+ */
+static inline
+unsigned long find_nth_andnot_bit(const unsigned long *addr1, const unsigned long *addr2,
+ unsigned long size, unsigned long n)
+{
+ if (n >= size)
+ return size;
+
+ if (small_const_nbits(size)) {
+ unsigned long val = *addr1 & (~*addr2) & GENMASK(size - 1, 0);
+
+ return val ? fns(val, n) : size;
+ }
+
+ return __find_nth_andnot_bit(addr1, addr2, size, n);
+}
+
#ifndef find_first_and_bit
/**
* find_first_and_bit - find the first set bit in both memory regions
diff --git a/lib/find_bit.c b/lib/find_bit.c
index 8707b4ef3e5e..ae2a59b59bfe 100644
--- a/lib/find_bit.c
+++ b/lib/find_bit.c
@@ -68,6 +68,30 @@ out: \
sz; \
})
+#define FIND_NTH_BIT(FETCH, size, num) \
+({ \
+ unsigned long sz = (size), nr = (num), idx, w, tmp; \
+ \
+ for (idx = 0; (idx + 1) * BITS_PER_LONG <= sz; idx++) { \
+ if (idx * BITS_PER_LONG + nr >= sz) \
+ goto out; \
+ \
+ tmp = (FETCH); \
+ w = hweight_long(tmp); \
+ if (w > nr) \
+ goto found; \
+ \
+ nr -= w; \
+ } \
+ \
+ if (sz % BITS_PER_LONG) \
+ tmp = (FETCH) & BITMAP_LAST_WORD_MASK(sz); \
+found: \
+ sz = min(idx * BITS_PER_LONG + fns(tmp, nr), sz); \
+out: \
+ sz; \
+})
+
#ifndef find_first_bit
/*
* Find the first set bit in a memory region.
@@ -111,6 +135,26 @@ unsigned long _find_next_bit(const unsigned long *addr, unsigned long nbits, uns
EXPORT_SYMBOL(_find_next_bit);
#endif
+unsigned long __find_nth_bit(const unsigned long *addr, unsigned long size, unsigned long n)
+{
+ return FIND_NTH_BIT(addr[idx], size, n);
+}
+EXPORT_SYMBOL(__find_nth_bit);
+
+unsigned long __find_nth_and_bit(const unsigned long *addr1, const unsigned long *addr2,
+ unsigned long size, unsigned long n)
+{
+ return FIND_NTH_BIT(addr1[idx] & addr2[idx], size, n);
+}
+EXPORT_SYMBOL(__find_nth_and_bit);
+
+unsigned long __find_nth_andnot_bit(const unsigned long *addr1, const unsigned long *addr2,
+ unsigned long size, unsigned long n)
+{
+ return FIND_NTH_BIT(addr1[idx] & ~addr2[idx], size, n);
+}
+EXPORT_SYMBOL(__find_nth_andnot_bit);
+
#ifndef find_next_and_bit
unsigned long _find_next_and_bit(const unsigned long *addr1, const unsigned long *addr2,
unsigned long nbits, unsigned long start)
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 4/6] lib/bitmap: add tests for find_nth_bit()
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
` (2 preceding siblings ...)
2022-09-18 3:07 ` [PATCH v3 3/6] lib: add find_nth{,_and,_andnot}_bit() Yury Norov
@ 2022-09-18 3:07 ` Yury Norov
2022-09-18 3:07 ` [PATCH v3 5/6] lib/bitmap: remove bitmap_ord_to_pos Yury Norov
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
Add functional and performance tests for find_nth_bit().
Signed-off-by: Yury Norov <yury.norov@gmail.com>
---
lib/find_bit_benchmark.c | 18 +++++++++++++++
lib/test_bitmap.c | 47 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/lib/find_bit_benchmark.c b/lib/find_bit_benchmark.c
index db904b57d4b8..10754586403b 100644
--- a/lib/find_bit_benchmark.c
+++ b/lib/find_bit_benchmark.c
@@ -115,6 +115,22 @@ static int __init test_find_last_bit(const void *bitmap, unsigned long len)
return 0;
}
+static int __init test_find_nth_bit(const unsigned long *bitmap, unsigned long len)
+{
+ unsigned long l, n, w = bitmap_weight(bitmap, len);
+ ktime_t time;
+
+ time = ktime_get();
+ for (n = 0; n < w; n++) {
+ l = find_nth_bit(bitmap, len, n);
+ WARN_ON(l >= len);
+ }
+ time = ktime_get() - time;
+ pr_err("find_nth_bit: %18llu ns, %6ld iterations\n", time, w);
+
+ return 0;
+}
+
static int __init test_find_next_and_bit(const void *bitmap,
const void *bitmap2, unsigned long len)
{
@@ -142,6 +158,7 @@ static int __init find_bit_test(void)
test_find_next_bit(bitmap, BITMAP_LEN);
test_find_next_zero_bit(bitmap, BITMAP_LEN);
test_find_last_bit(bitmap, BITMAP_LEN);
+ test_find_nth_bit(bitmap, BITMAP_LEN / 10);
/*
* test_find_first_bit() may take some time, so
@@ -164,6 +181,7 @@ static int __init find_bit_test(void)
test_find_next_bit(bitmap, BITMAP_LEN);
test_find_next_zero_bit(bitmap, BITMAP_LEN);
test_find_last_bit(bitmap, BITMAP_LEN);
+ test_find_nth_bit(bitmap, BITMAP_LEN);
test_find_first_bit(bitmap, BITMAP_LEN);
test_find_first_and_bit(bitmap, bitmap2, BITMAP_LEN);
test_find_next_and_bit(bitmap, bitmap2, BITMAP_LEN);
diff --git a/lib/test_bitmap.c b/lib/test_bitmap.c
index 98754ff9fe68..da52dc759c95 100644
--- a/lib/test_bitmap.c
+++ b/lib/test_bitmap.c
@@ -16,6 +16,8 @@
#include "../tools/testing/selftests/kselftest_module.h"
+#define EXP1_IN_BITS (sizeof(exp1) * 8)
+
KSTM_MODULE_GLOBALS();
static char pbl_buffer[PAGE_SIZE] __initdata;
@@ -219,6 +221,47 @@ static void __init test_zero_clear(void)
expect_eq_pbl("", bmap, 1024);
}
+static void __init test_find_nth_bit(void)
+{
+ unsigned long b, bit, cnt = 0;
+ DECLARE_BITMAP(bmap, 64 * 3);
+
+ bitmap_zero(bmap, 64 * 3);
+ __set_bit(10, bmap);
+ __set_bit(20, bmap);
+ __set_bit(30, bmap);
+ __set_bit(40, bmap);
+ __set_bit(50, bmap);
+ __set_bit(60, bmap);
+ __set_bit(80, bmap);
+ __set_bit(123, bmap);
+
+ expect_eq_uint(10, find_nth_bit(bmap, 64 * 3, 0));
+ expect_eq_uint(20, find_nth_bit(bmap, 64 * 3, 1));
+ expect_eq_uint(30, find_nth_bit(bmap, 64 * 3, 2));
+ expect_eq_uint(40, find_nth_bit(bmap, 64 * 3, 3));
+ expect_eq_uint(50, find_nth_bit(bmap, 64 * 3, 4));
+ expect_eq_uint(60, find_nth_bit(bmap, 64 * 3, 5));
+ expect_eq_uint(80, find_nth_bit(bmap, 64 * 3, 6));
+ expect_eq_uint(123, find_nth_bit(bmap, 64 * 3, 7));
+ expect_eq_uint(64 * 3, find_nth_bit(bmap, 64 * 3, 8));
+
+ expect_eq_uint(10, find_nth_bit(bmap, 64 * 3 - 1, 0));
+ expect_eq_uint(20, find_nth_bit(bmap, 64 * 3 - 1, 1));
+ expect_eq_uint(30, find_nth_bit(bmap, 64 * 3 - 1, 2));
+ expect_eq_uint(40, find_nth_bit(bmap, 64 * 3 - 1, 3));
+ expect_eq_uint(50, find_nth_bit(bmap, 64 * 3 - 1, 4));
+ expect_eq_uint(60, find_nth_bit(bmap, 64 * 3 - 1, 5));
+ expect_eq_uint(80, find_nth_bit(bmap, 64 * 3 - 1, 6));
+ expect_eq_uint(123, find_nth_bit(bmap, 64 * 3 - 1, 7));
+ expect_eq_uint(64 * 3 - 1, find_nth_bit(bmap, 64 * 3 - 1, 8));
+
+ for_each_set_bit(bit, exp1, EXP1_IN_BITS) {
+ b = find_nth_bit(exp1, EXP1_IN_BITS, cnt++);
+ expect_eq_uint(b, bit);
+ }
+}
+
static void __init test_fill_set(void)
{
DECLARE_BITMAP(bmap, 1024);
@@ -557,8 +600,6 @@ static void __init test_bitmap_parse(void)
}
}
-#define EXP1_IN_BITS (sizeof(exp1) * 8)
-
static void __init test_bitmap_arr32(void)
{
unsigned int nbits, next_bit;
@@ -952,6 +993,8 @@ static void __init selftest(void)
test_bitmap_cut();
test_bitmap_print_buf();
test_bitmap_const_eval();
+
+ test_find_nth_bit();
}
KSTM_MODULE_LOADERS(test_bitmap);
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 5/6] lib/bitmap: remove bitmap_ord_to_pos
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
` (3 preceding siblings ...)
2022-09-18 3:07 ` [PATCH v3 4/6] lib/bitmap: add tests for find_nth_bit() Yury Norov
@ 2022-09-18 3:07 ` Yury Norov
2022-09-18 3:07 ` [PATCH v3 6/6] cpumask: add cpumask_nth_{,and,andnot} Yury Norov
2022-09-23 16:18 ` [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
6 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
Now that we have find_nth_bit(), we can drop bitmap_ord_to_pos().
Signed-off-by: Yury Norov <yury.norov@gmail.com>
---
include/linux/bitmap.h | 1 -
include/linux/nodemask.h | 3 +--
lib/bitmap.c | 36 +++---------------------------------
3 files changed, 4 insertions(+), 36 deletions(-)
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index b2aef45af0db..7d6d73b78147 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -225,7 +225,6 @@ void bitmap_copy_le(unsigned long *dst, const unsigned long *src, unsigned int n
#else
#define bitmap_copy_le bitmap_copy
#endif
-unsigned int bitmap_ord_to_pos(const unsigned long *bitmap, unsigned int ord, unsigned int nbits);
int bitmap_print_to_pagebuf(bool list, char *buf,
const unsigned long *maskp, int nmaskbits);
diff --git a/include/linux/nodemask.h b/include/linux/nodemask.h
index 4b71a96190a8..0c45fb066caa 100644
--- a/include/linux/nodemask.h
+++ b/include/linux/nodemask.h
@@ -508,8 +508,7 @@ static inline int node_random(const nodemask_t *maskp)
w = nodes_weight(*maskp);
if (w)
- bit = bitmap_ord_to_pos(maskp->bits,
- get_random_int() % w, MAX_NUMNODES);
+ bit = find_nth_bit(maskp->bits, MAX_NUMNODES, get_random_int() % w);
return bit;
#else
return 0;
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 3fc2e338ec30..1c81413c51f8 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -968,36 +968,6 @@ static int bitmap_pos_to_ord(const unsigned long *buf, unsigned int pos, unsigne
return bitmap_weight(buf, pos);
}
-/**
- * bitmap_ord_to_pos - find position of n-th set bit in bitmap
- * @buf: pointer to bitmap
- * @ord: ordinal bit position (n-th set bit, n >= 0)
- * @nbits: number of valid bit positions in @buf
- *
- * Map the ordinal offset of bit @ord in @buf to its position in @buf.
- * Value of @ord should be in range 0 <= @ord < weight(buf). If @ord
- * >= weight(buf), returns @nbits.
- *
- * If for example, just bits 4 through 7 are set in @buf, then @ord
- * values 0 through 3 will get mapped to 4 through 7, respectively,
- * and all other @ord values returns @nbits. When @ord value 3
- * gets mapped to (returns) @pos value 7 in this example, that means
- * that the 3rd set bit (starting with 0th) is at position 7 in @buf.
- *
- * The bit positions 0 through @nbits-1 are valid positions in @buf.
- */
-unsigned int bitmap_ord_to_pos(const unsigned long *buf, unsigned int ord, unsigned int nbits)
-{
- unsigned int pos;
-
- for (pos = find_first_bit(buf, nbits);
- pos < nbits && ord;
- pos = find_next_bit(buf, nbits, pos + 1))
- ord--;
-
- return pos;
-}
-
/**
* bitmap_remap - Apply map defined by a pair of bitmaps to another bitmap
* @dst: remapped result
@@ -1047,7 +1017,7 @@ void bitmap_remap(unsigned long *dst, const unsigned long *src,
if (n < 0 || w == 0)
set_bit(oldbit, dst); /* identity map */
else
- set_bit(bitmap_ord_to_pos(new, n % w, nbits), dst);
+ set_bit(find_nth_bit(new, nbits, n % w), dst);
}
}
EXPORT_SYMBOL(bitmap_remap);
@@ -1086,7 +1056,7 @@ int bitmap_bitremap(int oldbit, const unsigned long *old,
if (n < 0 || w == 0)
return oldbit;
else
- return bitmap_ord_to_pos(new, n % w, bits);
+ return find_nth_bit(new, bits, n % w);
}
EXPORT_SYMBOL(bitmap_bitremap);
@@ -1210,7 +1180,7 @@ void bitmap_onto(unsigned long *dst, const unsigned long *orig,
* The following code is a more efficient, but less
* obvious, equivalent to the loop:
* for (m = 0; m < bitmap_weight(relmap, bits); m++) {
- * n = bitmap_ord_to_pos(orig, m, bits);
+ * n = find_nth_bit(orig, bits, m);
* if (test_bit(m, orig))
* set_bit(n, dst);
* }
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3 6/6] cpumask: add cpumask_nth_{,and,andnot}
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
` (4 preceding siblings ...)
2022-09-18 3:07 ` [PATCH v3 5/6] lib/bitmap: remove bitmap_ord_to_pos Yury Norov
@ 2022-09-18 3:07 ` Yury Norov
2022-09-23 16:18 ` [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
6 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-18 3:07 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Cc: Yury Norov
Add cpumask_nth_{,and,andnot} as wrappers around corresponding
find functions, and use it in cpumask_local_spread().
Signed-off-by: Yury Norov <yury.norov@gmail.com>
---
include/linux/cpumask.h | 44 +++++++++++++++++++++++++++++++++++++++++
lib/cpumask.c | 28 ++++++++++++--------------
2 files changed, 57 insertions(+), 15 deletions(-)
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 31bf08c786e3..621b59820561 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -337,6 +337,50 @@ unsigned int cpumask_any_but(const struct cpumask *mask, unsigned int cpu)
return i;
}
+/**
+ * cpumask_nth - get the first cpu in a cpumask
+ * @srcp: the cpumask pointer
+ * @cpu: the N'th cpu to find, starting from 0
+ *
+ * Returns >= nr_cpu_ids if such cpu doesn't exist.
+ */
+static inline unsigned int cpumask_nth(unsigned int cpu, const struct cpumask *srcp)
+{
+ return find_nth_bit(cpumask_bits(srcp), nr_cpumask_bits, cpumask_check(cpu));
+}
+
+/**
+ * cpumask_nth_and - get the first cpu in 2 cpumasks
+ * @srcp1: the cpumask pointer
+ * @srcp2: the cpumask pointer
+ * @cpu: the N'th cpu to find, starting from 0
+ *
+ * Returns >= nr_cpu_ids if such cpu doesn't exist.
+ */
+static inline
+unsigned int cpumask_nth_and(unsigned int cpu, const struct cpumask *srcp1,
+ const struct cpumask *srcp2)
+{
+ return find_nth_and_bit(cpumask_bits(srcp1), cpumask_bits(srcp2),
+ nr_cpumask_bits, cpumask_check(cpu));
+}
+
+/**
+ * cpumask_nth_andnot - get the first cpu set in 1st cpumask, and clear in 2nd.
+ * @srcp1: the cpumask pointer
+ * @srcp2: the cpumask pointer
+ * @cpu: the N'th cpu to find, starting from 0
+ *
+ * Returns >= nr_cpu_ids if such cpu doesn't exist.
+ */
+static inline
+unsigned int cpumask_nth_andnot(unsigned int cpu, const struct cpumask *srcp1,
+ const struct cpumask *srcp2)
+{
+ return find_nth_andnot_bit(cpumask_bits(srcp1), cpumask_bits(srcp2),
+ nr_cpumask_bits, cpumask_check(cpu));
+}
+
#define CPU_BITS_NONE \
{ \
[0 ... BITS_TO_LONGS(NR_CPUS)-1] = 0UL \
diff --git a/lib/cpumask.c b/lib/cpumask.c
index f0ae119be8c4..2c4a63b6f03f 100644
--- a/lib/cpumask.c
+++ b/lib/cpumask.c
@@ -128,23 +128,21 @@ unsigned int cpumask_local_spread(unsigned int i, int node)
i %= num_online_cpus();
if (node == NUMA_NO_NODE) {
- for_each_cpu(cpu, cpu_online_mask)
- if (i-- == 0)
- return cpu;
+ cpu = cpumask_nth(i, cpu_online_mask);
+ if (cpu < nr_cpu_ids)
+ return cpu;
} else {
/* NUMA first. */
- for_each_cpu_and(cpu, cpumask_of_node(node), cpu_online_mask)
- if (i-- == 0)
- return cpu;
-
- for_each_cpu(cpu, cpu_online_mask) {
- /* Skip NUMA nodes, done above. */
- if (cpumask_test_cpu(cpu, cpumask_of_node(node)))
- continue;
-
- if (i-- == 0)
- return cpu;
- }
+ cpu = cpumask_nth_and(i, cpu_online_mask, cpumask_of_node(node));
+ if (cpu < nr_cpu_ids)
+ return cpu;
+
+ i -= cpumask_weight_and(cpu_online_mask, cpumask_of_node(node));
+
+ /* Skip NUMA nodes, done above. */
+ cpu = cpumask_nth_andnot(i, cpu_online_mask, cpumask_of_node(node));
+ if (cpu < nr_cpu_ids)
+ return cpu;
}
BUG();
}
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 0/5] lib/find: add find_nth_bit()
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
` (5 preceding siblings ...)
2022-09-18 3:07 ` [PATCH v3 6/6] cpumask: add cpumask_nth_{,and,andnot} Yury Norov
@ 2022-09-23 16:18 ` Yury Norov
2022-09-26 19:17 ` Yury Norov
6 siblings, 1 reply; 9+ messages in thread
From: Yury Norov @ 2022-09-23 16:18 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
Ping?
On Sat, Sep 17, 2022 at 08:07:10PM -0700, Yury Norov wrote:
> Kernel lacks for a function that searches for Nth bit in a bitmap.
> Usually people do it like this:
> for_each_set_bit(bit, mask, size)
> if (n-- == 0)
> return bit;
>
> Which is not so elegant and very slow.
>
> This series adds fast routines for this task, and applies them where
> appropriate.
>
> v1: https://lore.kernel.org/lkml/20220706182300.70862-4-yury.norov@gmail.com/T/
> v2: https://lore.kernel.org/lkml/CAAH8bW_RYG_Tbpip=BkSFAymDm2y3jJBqTi0PJWA=H-a-L_3tg@mail.gmail.com/T/
> v3:
> - add bitmap_weight_and() and use it in cpumask_local_spread();
> - rework find_nth_bit() family: similarly to [1], introduce
> FIND_NTH_BIT(), and optimize the function to return earlier when
> it's known that the rest of bitmap can't meet the requirement.
> - patch "lib/nodemask: inline next_node_in() and node_random()" has
> been merged, so drop it from this series.
>
> On top of:
> [1] https://lore.kernel.org/lkml/20220915020730.852234-1-yury.norov@gmail.com/T/
>
> Yury Norov (6):
> lib/bitmap: don't call __bitmap_weight() in kernel code
> lib/bitmap: add bitmap_weight_and()
> lib: add find_nth(,and,andnot)_bit()
> lib/bitmap: add tests for find_nth_bit()
> lib/bitmap: remove bitmap_ord_to_pos
> cpumask: add cpumask_nth_{,and,andnot}
>
> fs/ntfs3/bitmap.c | 4 +-
> include/linux/bitmap.h | 13 +++++-
> include/linux/bitops.h | 19 +++++++++
> include/linux/cpumask.h | 55 +++++++++++++++++++++++++
> include/linux/find.h | 86 ++++++++++++++++++++++++++++++++++++++++
> include/linux/nodemask.h | 3 +-
> lib/bitmap.c | 68 ++++++++++++-------------------
> lib/cpumask.c | 28 ++++++-------
> lib/find_bit.c | 44 ++++++++++++++++++++
> lib/find_bit_benchmark.c | 18 +++++++++
> lib/test_bitmap.c | 47 +++++++++++++++++++++-
> 11 files changed, 320 insertions(+), 65 deletions(-)
>
> --
> 2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3 0/5] lib/find: add find_nth_bit()
2022-09-23 16:18 ` [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
@ 2022-09-26 19:17 ` Yury Norov
0 siblings, 0 replies; 9+ messages in thread
From: Yury Norov @ 2022-09-26 19:17 UTC (permalink / raw)
To: linux-kernel, Alexander Lobakin, Andy Shevchenko, Arnd Bergmann,
David Gow, Eric Dumazet, Isabella Basso, Kees Cook, Keith Busch,
Kumar Kartikeya Dwivedi, Marco Elver, Mark Rutland,
Rasmus Villemoes, Steven Rostedt,
Toke Høiland-Jørgensen, Valentin Schneider
OK, take in bitmap-for-next.
On Fri, Sep 23, 2022 at 09:18:44AM -0700, Yury Norov wrote:
> Ping?
>
> On Sat, Sep 17, 2022 at 08:07:10PM -0700, Yury Norov wrote:
> > Kernel lacks for a function that searches for Nth bit in a bitmap.
> > Usually people do it like this:
> > for_each_set_bit(bit, mask, size)
> > if (n-- == 0)
> > return bit;
> >
> > Which is not so elegant and very slow.
> >
> > This series adds fast routines for this task, and applies them where
> > appropriate.
> >
> > v1: https://lore.kernel.org/lkml/20220706182300.70862-4-yury.norov@gmail.com/T/
> > v2: https://lore.kernel.org/lkml/CAAH8bW_RYG_Tbpip=BkSFAymDm2y3jJBqTi0PJWA=H-a-L_3tg@mail.gmail.com/T/
> > v3:
> > - add bitmap_weight_and() and use it in cpumask_local_spread();
> > - rework find_nth_bit() family: similarly to [1], introduce
> > FIND_NTH_BIT(), and optimize the function to return earlier when
> > it's known that the rest of bitmap can't meet the requirement.
> > - patch "lib/nodemask: inline next_node_in() and node_random()" has
> > been merged, so drop it from this series.
> >
> > On top of:
> > [1] https://lore.kernel.org/lkml/20220915020730.852234-1-yury.norov@gmail.com/T/
> >
> > Yury Norov (6):
> > lib/bitmap: don't call __bitmap_weight() in kernel code
> > lib/bitmap: add bitmap_weight_and()
> > lib: add find_nth(,and,andnot)_bit()
> > lib/bitmap: add tests for find_nth_bit()
> > lib/bitmap: remove bitmap_ord_to_pos
> > cpumask: add cpumask_nth_{,and,andnot}
> >
> > fs/ntfs3/bitmap.c | 4 +-
> > include/linux/bitmap.h | 13 +++++-
> > include/linux/bitops.h | 19 +++++++++
> > include/linux/cpumask.h | 55 +++++++++++++++++++++++++
> > include/linux/find.h | 86 ++++++++++++++++++++++++++++++++++++++++
> > include/linux/nodemask.h | 3 +-
> > lib/bitmap.c | 68 ++++++++++++-------------------
> > lib/cpumask.c | 28 ++++++-------
> > lib/find_bit.c | 44 ++++++++++++++++++++
> > lib/find_bit_benchmark.c | 18 +++++++++
> > lib/test_bitmap.c | 47 +++++++++++++++++++++-
> > 11 files changed, 320 insertions(+), 65 deletions(-)
> >
> > --
> > 2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2022-09-26 19:17 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-18 3:07 [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
2022-09-18 3:07 ` [PATCH v3 1/6] lib/bitmap: don't call __bitmap_weight() in kernel code Yury Norov
2022-09-18 3:07 ` [PATCH v3 2/6] lib/bitmap: add bitmap_weight_and() Yury Norov
2022-09-18 3:07 ` [PATCH v3 3/6] lib: add find_nth{,_and,_andnot}_bit() Yury Norov
2022-09-18 3:07 ` [PATCH v3 4/6] lib/bitmap: add tests for find_nth_bit() Yury Norov
2022-09-18 3:07 ` [PATCH v3 5/6] lib/bitmap: remove bitmap_ord_to_pos Yury Norov
2022-09-18 3:07 ` [PATCH v3 6/6] cpumask: add cpumask_nth_{,and,andnot} Yury Norov
2022-09-23 16:18 ` [PATCH v3 0/5] lib/find: add find_nth_bit() Yury Norov
2022-09-26 19:17 ` Yury Norov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®