diff --git a/lib/bitmap.c b/lib/bitmap.c index f49eabe09271..8acab0e176ef 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -676,138 +676,143 @@ int bitmap_bitremap(int oldbit, const unsigned long *old, } EXPORT_SYMBOL(bitmap_bitremap); +/* + * Common code for bitmap_*_region() routines. + * bitmap: array of unsigned longs corresponding to the bitmap + * pos: the beginning of the region + * order: region size (log base 2 of number of bits) + * reg_op: operation(s) to perform on that region of bitmap + * + * Can set, verify and/or release a region of bits in a bitmap, + * depending on which combination of REG_OP_* flag bits is set. + * + * A region of a bitmap is a sequence of bits in the bitmap, of + * some size '1 << order' (a power of two), aligned to that same + * '1 << order' power of two. + * + * Returns 1 if REG_OP_ISFREE succeeds (region is all zero bits). + * Returns 0 in all other cases and reg_ops. + */ + +enum { + REG_OP_ISFREE, /* true if region is all zero bits */ + REG_OP_ALLOC, /* set all bits in region */ + REG_OP_RELEASE, /* clear all bits in region */ +}; + +static int __reg_op(unsigned long *bitmap, int pos, int order, int reg_op) +{ + int nbits_reg; /* number of bits in region */ + int index; /* index first long of region in bitmap */ + int offset; /* bit offset region in bitmap[index] */ + int nlongs_reg; /* num longs spanned by region in bitmap */ + int nbitsinlong; /* num bits of region in each spanned long */ + unsigned long mask; /* bitmask for one long of region */ + int i; /* scans bitmap by longs */ + int ret = 0; /* return value */ + + /* + * Either nlongs_reg == 1 (for small orders that fit in one long) + * or (offset == 0 && mask == ~0UL) (for larger multiword orders.) + */ + nbits_reg = 1 << order; + index = pos / BITS_PER_LONG; + offset = pos - (index * BITS_PER_LONG); + nlongs_reg = BITS_TO_LONGS(nbits_reg); + nbitsinlong = min(nbits_reg, BITS_PER_LONG); + + /* + * Can't do "mask = (1UL << nbitsinlong) - 1", as that + * overflows if nbitsinlong == BITS_PER_LONG. + */ + mask = (1UL << (nbitsinlong - 1)); + mask += mask - 1; + mask <<= offset; + + switch (reg_op) { + case REG_OP_ISFREE: + for (i = 0; i < nlongs_reg; i++) { + if (bitmap[index + i] & mask) + goto done; + } + ret = 1; /* all bits in region free (zero) */ + break; + + case REG_OP_ALLOC: + for (i = 0; i < nlongs_reg; i++) + bitmap[index + i] |= mask; + break; + + case REG_OP_RELEASE: + for (i = 0; i < nlongs_reg; i++) + bitmap[index + i] &= ~mask; + break; + } +done: + return ret; +} + /** * bitmap_find_free_region - find a contiguous aligned mem region - * @bitmap: an array of unsigned longs corresponding to the bitmap + * @bitmap: array of unsigned longs corresponding to the bitmap * @bits: number of bits in the bitmap - * @order: region size to find (size is actually 1< BITS_PER_LONG) - nbitsinlong = BITS_PER_LONG; - - /* make a mask of the order */ - mask = (1UL << (nbitsinlong - 1)); - mask += mask - 1; - - /* run up the bitmap nbitsinlong at a time */ - for (i = 0; i < bits; i += nbitsinlong) { - int index = i / BITS_PER_LONG; - int offset = i - (index * BITS_PER_LONG); - int j, space = 1; - - /* find space in the bitmap */ - for (j = 0; j < nlongs; j++) - if ((bitmap[index + j] & (mask << offset))) { - space = 0; - break; - } - - /* keep looking */ - if (unlikely(!space)) - continue; - - for (j = 0; j < nlongs; j++) - /* set region in bitmap */ - bitmap[index + j] |= (mask << offset); - - return i; - } - return -ENOMEM; + for (pos = 0; pos < bits; pos += (1 << order)) + if (__reg_op(bitmap, pos, order, REG_OP_ISFREE)) + break; + if (pos == bits) + return -ENOMEM; + __reg_op(bitmap, pos, order, REG_OP_ALLOC); + return pos; } EXPORT_SYMBOL(bitmap_find_free_region); /** * bitmap_release_region - release allocated bitmap region - * @bitmap: a pointer to the bitmap - * @pos: the beginning of the region - * @order: the order of the bits to release (number is 1< BITS_PER_LONG) - nbitsinlong = BITS_PER_LONG; - - mask = (1UL << (nbitsinlong - 1)); - mask += mask - 1; - - for (i = 0; i < nlongs; i++) - bitmap[index + i] &= ~(mask << offset); + __reg_op(bitmap, pos, order, REG_OP_RELEASE); } EXPORT_SYMBOL(bitmap_release_region); /** * bitmap_allocate_region - allocate bitmap region - * @bitmap: a pointer to the bitmap - * @pos: the beginning of the region - * @order: the order of the bits to allocate (number is 1< BITS_PER_LONG) - nbitsinlong = BITS_PER_LONG; - - mask = (1UL << (nbitsinlong - 1)); - mask += mask - 1; - - for (i = 0; i < nlongs; i++) - if (bitmap[index + i] & (mask << offset)) - return -EBUSY; - for (i = 0; i < nlongs; i++) - bitmap[index + i] |= (mask << offset); + if (!__reg_op(bitmap, pos, order, REG_OP_ISFREE)) + return -EBUSY; + __reg_op(bitmap, pos, order, REG_OP_ALLOC); return 0; } EXPORT_SYMBOL(bitmap_allocate_region);