diff --git a/qemu/include/qemu/bitmap.h b/qemu/include/qemu/bitmap.h index 89b6b34d..a089a95f 100644 --- a/qemu/include/qemu/bitmap.h +++ b/qemu/include/qemu/bitmap.h @@ -27,6 +27,7 @@ * Otherwise many inlines will generate horrible code. * * bitmap_set(dst, pos, nbits) Set specified bit area + * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops * bitmap_clear(dst, pos, nbits) Clear specified bit area */ @@ -47,6 +48,7 @@ unsigned long name[BITS_TO_LONGS(bits)] void bitmap_set(unsigned long *map, long i, long len); +void bitmap_set_atomic(unsigned long *map, long i, long len); void bitmap_clear(unsigned long *map, long start, long nr); static inline unsigned long *bitmap_zero_extend(unsigned long *old, diff --git a/qemu/include/qemu/bitops.h b/qemu/include/qemu/bitops.h index 5a616529..9dfcba57 100644 --- a/qemu/include/qemu/bitops.h +++ b/qemu/include/qemu/bitops.h @@ -16,6 +16,7 @@ #include #include "host-utils.h" +#include "atomic.h" #define BITS_PER_BYTE CHAR_BIT #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) @@ -38,6 +39,19 @@ static inline void set_bit(long nr, unsigned long *addr) *p |= mask; } +/** + * set_bit_atomic - Set a bit in memory atomically + * @nr: the bit to set + * @addr: the address to start counting from + */ +static inline void set_bit_atomic(long nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = addr + BIT_WORD(nr); + + atomic_or(p, mask); +} + /** * clear_bit - Clears a bit in memory * @nr: Bit to clear diff --git a/qemu/util/bitmap.c b/qemu/util/bitmap.c index 96bbabc8..788f07e0 100644 --- a/qemu/util/bitmap.c +++ b/qemu/util/bitmap.c @@ -11,6 +11,7 @@ #include "qemu/bitops.h" #include "qemu/bitmap.h" +#include "qemu/atomic.h" #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) @@ -34,6 +35,43 @@ void bitmap_set(unsigned long *map, long start, long nr) } } +void bitmap_set_atomic(unsigned long *map, long start, long nr) +{ + unsigned long *p = map + BIT_WORD(start); + const long size = start + nr; + int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); + unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); + + /* First word */ + if (nr - bits_to_set > 0) { + atomic_or(p, mask_to_set); + nr -= bits_to_set; + bits_to_set = BITS_PER_LONG; + mask_to_set = ~0UL; + p++; + } + + /* Full words */ + if (bits_to_set == BITS_PER_LONG) { + while (nr >= BITS_PER_LONG) { + *p = ~0UL; + nr -= BITS_PER_LONG; + p++; + } + } + + /* Last word */ + if (nr) { + mask_to_set &= BITMAP_LAST_WORD_MASK(size); + atomic_or(p, mask_to_set); + } else { + /* If we avoided the full barrier in atomic_or(), issue a + * barrier to account for the assignments in the while loop. + */ + smp_mb(); + } +} + void bitmap_clear(unsigned long *map, long start, long nr) { unsigned long *p = map + BIT_WORD(start);