Converting from a bitmap to a u32 or u64 array of bits and back can
involve endianness conversions depending on platform. Import the Linux
functions that take care of this.

Signed-off-by: Ahmad Fatoum <[email protected]>
---
 include/linux/bitmap.h | 101 ++++++++++++++++++++++++++++++++++++++++
 lib/bitmap.c           | 103 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 7d06fac68d08..9ec1ee2d1422 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -56,6 +56,10 @@
  * bitmap_find_free_region(bitmap, bits, order)        Find and allocate bit 
region
  * bitmap_release_region(bitmap, pos, order)   Free specified bit region
  * bitmap_allocate_region(bitmap, pos, order)  Allocate specified bit region
+ * bitmap_from_arr32(dst, buf, nbits)           Copy nbits from u32[] buf to 
dst
+ * bitmap_from_arr64(dst, buf, nbits)           Copy nbits from u64[] buf to 
dst
+ * bitmap_to_arr32(buf, src, nbits)             Copy nbits from buf to u32[] 
dst
+ * bitmap_to_arr64(buf, src, nbits)             Copy nbits from buf to u64[] 
dst
  */
 
 /*
@@ -178,6 +182,103 @@ static inline void bitmap_copy(unsigned long *dst, const 
unsigned long *src,
        }
 }
 
+/*
+ * Copy bitmap and clear tail bits in last word.
+ */
+static inline void bitmap_copy_clear_tail(unsigned long *dst,
+               const unsigned long *src, unsigned int nbits)
+{
+       bitmap_copy(dst, src, nbits);
+       if (nbits % BITS_PER_LONG)
+               dst[nbits / BITS_PER_LONG] &= BITMAP_LAST_WORD_MASK(nbits);
+}
+
+/*
+ * On 32-bit systems bitmaps are represented as u32 arrays internally. On LE64
+ * machines the order of hi and lo parts of numbers match the bitmap structure.
+ * In both cases conversion is not needed when copying data from/to arrays of
+ * u32. But in LE64 case, typecast in bitmap_copy_clear_tail() may lead
+ * to out-of-bound access. To avoid that, both LE and BE variants of 64-bit
+ * architectures are not using bitmap_copy_clear_tail().
+ */
+#if BITS_PER_LONG == 64
+void bitmap_from_arr32(unsigned long *bitmap, const u32 *buf,
+                                                       unsigned int nbits);
+void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap,
+                                                       unsigned int nbits);
+#else
+#define bitmap_from_arr32(bitmap, buf, nbits)                  \
+       bitmap_copy_clear_tail((unsigned long *) (bitmap),      \
+                       (const unsigned long *) (buf), (nbits))
+#define bitmap_to_arr32(buf, bitmap, nbits)                    \
+       bitmap_copy_clear_tail((unsigned long *) (buf),         \
+                       (const unsigned long *) (bitmap), (nbits))
+#endif
+
+/*
+ * On 64-bit systems bitmaps are represented as u64 arrays internally. On LE32
+ * machines the order of hi and lo parts of numbers match the bitmap structure.
+ * In both cases conversion is not needed when copying data from/to arrays of
+ * u64.
+ */
+#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN)
+void bitmap_from_arr64(unsigned long *bitmap, const u64 *buf, unsigned int 
nbits);
+void bitmap_to_arr64(u64 *buf, const unsigned long *bitmap, unsigned int 
nbits);
+#else
+#define bitmap_from_arr64(bitmap, buf, nbits)                  \
+       bitmap_copy_clear_tail((unsigned long *)(bitmap), (const unsigned long 
*)(buf), (nbits))
+#define bitmap_to_arr64(buf, bitmap, nbits)                    \
+       bitmap_copy_clear_tail((unsigned long *)(buf), (const unsigned long 
*)(bitmap), (nbits))
+#endif
+
+/**
+ * BITMAP_FROM_U64() - Represent u64 value in the format suitable for bitmap.
+ * @n: u64 value
+ *
+ * Linux bitmaps are internally arrays of unsigned longs, i.e. 32-bit
+ * integers in 32-bit environment, and 64-bit integers in 64-bit one.
+ *
+ * There are four combinations of endianness and length of the word in linux
+ * ABIs: LE64, BE64, LE32 and BE32.
+ *
+ * On 64-bit kernels 64-bit LE and BE numbers are naturally ordered in
+ * bitmaps and therefore don't require any special handling.
+ *
+ * On 32-bit kernels 32-bit LE ABI orders lo word of 64-bit number in memory
+ * prior to hi, and 32-bit BE orders hi word prior to lo. The bitmap on the
+ * other hand is represented as an array of 32-bit words and the position of
+ * bit N may therefore be calculated as: word #(N/32) and bit #(N%32) in that
+ * word.  For example, bit #42 is located at 10th position of 2nd word.
+ * It matches 32-bit LE ABI, and we can simply let the compiler store 64-bit
+ * values in memory as it usually does. But for BE we need to swap hi and lo
+ * words manually.
+ *
+ * With all that, the macro BITMAP_FROM_U64() does explicit reordering of hi 
and
+ * lo parts of u64.  For LE32 it does nothing, and for BE environment it swaps
+ * hi and lo words, as is expected by bitmap.
+ */
+#if BITS_PER_LONG == 64
+#define BITMAP_FROM_U64(n) (n)
+#else
+#define BITMAP_FROM_U64(n) ((unsigned long) ((u64)(n) & ULONG_MAX)), \
+                               ((unsigned long) ((u64)(n) >> 32))
+#endif
+
+/**
+ * bitmap_from_u64 - Check and swap words within u64.
+ *  @mask: source bitmap
+ *  @dst:  destination bitmap
+ *
+ * In 32-bit Big Endian kernel, when using ``(u32 *)(&val)[*]``
+ * to read u64 mask, we will get the wrong word.
+ * That is ``(u32 *)(&val)[0]`` gets the upper 32 bits,
+ * but we expect the lower 32-bits of u64.
+ */
+static inline void bitmap_from_u64(unsigned long *dst, u64 mask)
+{
+       bitmap_from_arr64(dst, &mask, 64);
+}
+
 static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
                        const unsigned long *src2, int nbits)
 {
diff --git a/lib/bitmap.c b/lib/bitmap.c
index dfc0f06b13c4..7614c4d21337 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -847,3 +847,106 @@ unsigned long *bitmap_xzalloc(unsigned int nbits)
 {
        return xzalloc(BITS_TO_LONGS(nbits) * sizeof(unsigned long));
 }
+
+#if BITS_PER_LONG == 64
+/**
+ * bitmap_from_arr32 - copy the contents of u32 array of bits to bitmap
+ *     @bitmap: array of unsigned longs, the destination bitmap
+ *     @buf: array of u32 (in host byte order), the source bitmap
+ *     @nbits: number of bits in @bitmap
+ */
+void bitmap_from_arr32(unsigned long *bitmap, const u32 *buf, unsigned int 
nbits)
+{
+       unsigned int i, halfwords;
+
+       halfwords = DIV_ROUND_UP(nbits, 32);
+       for (i = 0; i < halfwords; i++) {
+               bitmap[i/2] = (unsigned long) buf[i];
+               if (++i < halfwords)
+                       bitmap[i/2] |= ((unsigned long) buf[i]) << 32;
+       }
+
+       /* Clear tail bits in last word beyond nbits. */
+       if (nbits % BITS_PER_LONG)
+               bitmap[(halfwords - 1) / 2] &= BITMAP_LAST_WORD_MASK(nbits);
+}
+EXPORT_SYMBOL(bitmap_from_arr32);
+
+/**
+ * bitmap_to_arr32 - copy the contents of bitmap to a u32 array of bits
+ *     @buf: array of u32 (in host byte order), the dest bitmap
+ *     @bitmap: array of unsigned longs, the source bitmap
+ *     @nbits: number of bits in @bitmap
+ */
+void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, unsigned int nbits)
+{
+       unsigned int i, halfwords;
+
+       halfwords = DIV_ROUND_UP(nbits, 32);
+       for (i = 0; i < halfwords; i++) {
+               buf[i] = (u32) (bitmap[i/2] & UINT_MAX);
+               if (++i < halfwords)
+                       buf[i] = (u32) (bitmap[i/2] >> 32);
+       }
+
+       /* Clear tail bits in last element of array beyond nbits. */
+       if (nbits % BITS_PER_LONG)
+               buf[halfwords - 1] &= (u32) (UINT_MAX >> ((-nbits) & 31));
+}
+EXPORT_SYMBOL(bitmap_to_arr32);
+#endif
+
+#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN)
+/**
+ * bitmap_from_arr64 - copy the contents of u64 array of bits to bitmap
+ *     @bitmap: array of unsigned longs, the destination bitmap
+ *     @buf: array of u64 (in host byte order), the source bitmap
+ *     @nbits: number of bits in @bitmap
+ */
+void bitmap_from_arr64(unsigned long *bitmap, const u64 *buf, unsigned int 
nbits)
+{
+       int n;
+
+       for (n = nbits; n > 0; n -= 64) {
+               u64 val = *buf++;
+
+               *bitmap++ = val;
+               if (n > 32)
+                       *bitmap++ = val >> 32;
+       }
+
+       /*
+        * Clear tail bits in the last word beyond nbits.
+        *
+        * Negative index is OK because here we point to the word next
+        * to the last word of the bitmap, except for nbits == 0, which
+        * is tested implicitly.
+        */
+       if (nbits % BITS_PER_LONG)
+               bitmap[-1] &= BITMAP_LAST_WORD_MASK(nbits);
+}
+EXPORT_SYMBOL(bitmap_from_arr64);
+
+/**
+ * bitmap_to_arr64 - copy the contents of bitmap to a u64 array of bits
+ *     @buf: array of u64 (in host byte order), the dest bitmap
+ *     @bitmap: array of unsigned longs, the source bitmap
+ *     @nbits: number of bits in @bitmap
+ */
+void bitmap_to_arr64(u64 *buf, const unsigned long *bitmap, unsigned int nbits)
+{
+       const unsigned long *end = bitmap + BITS_TO_LONGS(nbits);
+
+       while (bitmap < end) {
+               *buf = *bitmap++;
+               if (bitmap < end)
+                       *buf |= (u64)(*bitmap++) << 32;
+               buf++;
+       }
+
+       /* Clear tail bits in the last element of array beyond nbits. */
+       if (nbits % 64)
+               buf[-1] &= GENMASK_ULL((nbits - 1) % 64, 0);
+}
+EXPORT_SYMBOL(bitmap_to_arr64);
+#endif
-- 
2.39.2


Reply via email to