summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/bitmap.c199
1 files changed, 102 insertions, 97 deletions
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);
-/**
- * bitmap_find_free_region - find a contiguous aligned mem region
- * @bitmap: an array of unsigned longs corresponding to the bitmap
- * @bits: number of bits in the bitmap
- * @order: region size to find (size is actually 1<<order)
+/*
+ * 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
*
- * Find a sequence of free (zero) bits in a bitmap and allocate
- * them (set them to one). Only consider sequences of length a
- * power ('order') of two, aligned to that power of two, which
- * makes the search algorithm much faster.
+ * Can set, verify and/or release a region of bits in a bitmap,
+ * depending on which combination of REG_OP_* flag bits is set.
*
- * Return the bit offset in bitmap of the allocated sequence,
- * or -errno on failure.
+ * 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.
*/
-int bitmap_find_free_region(unsigned long *bitmap, int bits, int order)
+
+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; /* number of bits in region */
- int nlongs; /* num longs spanned by region in bitmap */
+ 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 of bits [0 .. nbitsinlong-1] */
+ unsigned long mask; /* bitmask for one long of region */
int i; /* scans bitmap by longs */
+ int ret = 0; /* return value */
- nbits = 1 << order;
- nlongs = (nbits + (BITS_PER_LONG - 1)) / BITS_PER_LONG;
- nbitsinlong = nbits;
- if (nbitsinlong > BITS_PER_LONG)
- nbitsinlong = BITS_PER_LONG;
+ /*
+ * 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);
- /* make a mask of the order */
+ /*
+ * Can't do "mask = (1UL << nbitsinlong) - 1", as that
+ * overflows if nbitsinlong == BITS_PER_LONG.
+ */
mask = (1UL << (nbitsinlong - 1));
mask += mask - 1;
+ mask <<= offset;
- /* 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;
+ 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;
}
- return -ENOMEM;
+done:
+ return ret;
+}
+
+/**
+ * bitmap_find_free_region - find a contiguous aligned mem region
+ * @bitmap: array of unsigned longs corresponding to the bitmap
+ * @bits: number of bits in the bitmap
+ * @order: region size (log base 2 of number of bits) to find
+ *
+ * Find a region of free (zero) bits in a @bitmap of @bits bits and
+ * allocate them (set them to one). Only consider regions of length
+ * a power (@order) of two, aligned to that power of two, which
+ * makes the search algorithm much faster.
+ *
+ * Return the bit offset in bitmap of the allocated region,
+ * or -errno on failure.
+ */
+int bitmap_find_free_region(unsigned long *bitmap, int bits, int order)
+{
+ int pos; /* scans bitmap by regions of size order */
+
+ 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<<order)
+ * @bitmap: array of unsigned longs corresponding to the bitmap
+ * @pos: beginning of bit region to release
+ * @order: region size (log base 2 of number of bits) to release
*
* This is the complement to __bitmap_find_free_region and releases
* the found region (by clearing it in the bitmap).
+ *
+ * No return value.
*/
void bitmap_release_region(unsigned long *bitmap, int pos, int order)
{
- int nbits; /* number of bits in region */
- int nlongs; /* num longs spanned by region in bitmap */
- int index; /* index first long of region in bitmap */
- int offset; /* bit offset region in bitmap[index] */
- int nbitsinlong; /* num bits of region in each spanned long */
- unsigned long mask; /* bitmask of bits [0 .. nbitsinlong-1] */
- int i; /* scans bitmap by longs */
-
- nbits = 1 << order;
- nlongs = (nbits + (BITS_PER_LONG - 1)) / BITS_PER_LONG;
- index = pos / BITS_PER_LONG;
- offset = pos - (index * BITS_PER_LONG);
-
- nbitsinlong = nbits;
- if (nbitsinlong > 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<<order)
+ * @bitmap: array of unsigned longs corresponding to the bitmap
+ * @pos: beginning of bit region to allocate
+ * @order: region size (log base 2 of number of bits) to allocate
*
* Allocate (set bits in) a specified region of a bitmap.
+ *
* Return 0 on success, or -EBUSY if specified region wasn't
* free (not all bits were zero).
*/
int bitmap_allocate_region(unsigned long *bitmap, int pos, int order)
{
- int nbits; /* number of bits in region */
- int nlongs; /* num longs spanned by region in bitmap */
- int index; /* index first long of region in bitmap */
- int offset; /* bit offset region in bitmap[index] */
- int nbitsinlong; /* num bits of region in each spanned long */
- unsigned long mask; /* bitmask of bits [0 .. nbitsinlong-1] */
- int i; /* scans bitmap by longs */
-
- nbits = 1 << order;
- nlongs = (nbits + (BITS_PER_LONG - 1)) / BITS_PER_LONG;
- index = pos / BITS_PER_LONG;
- offset = pos - (index * BITS_PER_LONG);
-
- nbitsinlong = nbits;
- if (nbitsinlong > 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);