aboutsummaryrefslogtreecommitdiff
path: root/util/bitmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/bitmap.c')
-rw-r--r--util/bitmap.c45
1 files changed, 45 insertions, 0 deletions
diff --git a/util/bitmap.c b/util/bitmap.c
index 39994af675..300a68e38c 100644
--- a/util/bitmap.c
+++ b/util/bitmap.c
@@ -235,6 +235,51 @@ void bitmap_clear(unsigned long *map, long start, long nr)
}
}
+bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr)
+{
+ unsigned long *p = map + BIT_WORD(start);
+ const long size = start + nr;
+ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
+ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
+ unsigned long dirty = 0;
+ unsigned long old_bits;
+
+ /* First word */
+ if (nr - bits_to_clear > 0) {
+ old_bits = atomic_fetch_and(p, ~mask_to_clear);
+ dirty |= old_bits & mask_to_clear;
+ nr -= bits_to_clear;
+ bits_to_clear = BITS_PER_LONG;
+ mask_to_clear = ~0UL;
+ p++;
+ }
+
+ /* Full words */
+ if (bits_to_clear == BITS_PER_LONG) {
+ while (nr >= BITS_PER_LONG) {
+ if (*p) {
+ old_bits = atomic_xchg(p, 0);
+ dirty |= old_bits;
+ }
+ nr -= BITS_PER_LONG;
+ p++;
+ }
+ }
+
+ /* Last word */
+ if (nr) {
+ mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
+ old_bits = atomic_fetch_and(p, ~mask_to_clear);
+ dirty |= old_bits & mask_to_clear;
+ } else {
+ if (!dirty) {
+ smp_mb();
+ }
+ }
+
+ return dirty != 0;
+}
+
#define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
/**