| From 2fe1d55134fce05c17ea118a2e37a4af771887bc Mon Sep 17 00:00:00 2001 |
| From: Omar Sandoval <osandov@fb.com> |
| Date: Thu, 22 Sep 2016 17:24:20 -0700 |
| Subject: Btrfs: fix free space tree bitmaps on big-endian systems |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Omar Sandoval <osandov@fb.com> |
| |
| commit 2fe1d55134fce05c17ea118a2e37a4af771887bc upstream. |
| |
| In convert_free_space_to_{bitmaps,extents}(), we buffer the free space |
| bitmaps in memory and copy them directly to/from the extent buffers with |
| {read,write}_extent_buffer(). The extent buffer bitmap helpers use byte |
| granularity, which is equivalent to a little-endian bitmap. This means |
| that on big-endian systems, the in-memory bitmaps will be written to |
| disk byte-swapped. To fix this, use byte-granularity for the bitmaps in |
| memory. |
| |
| Fixes: a5ed91828518 ("Btrfs: implement the free space B-tree") |
| Tested-by: Holger Hoffstรคtte <holger@applied-asynchrony.com> |
| Tested-by: Chandan Rajendra <chandan@linux.vnet.ibm.com> |
| Signed-off-by: Omar Sandoval <osandov@fb.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/extent_io.c | 64 ++++++++++++++++++++++++++++++++------------- |
| fs/btrfs/extent_io.h | 22 +++++++++++++++ |
| fs/btrfs/free-space-tree.c | 17 +++++------ |
| 3 files changed, 76 insertions(+), 27 deletions(-) |
| |
| --- a/fs/btrfs/extent_io.c |
| +++ b/fs/btrfs/extent_io.c |
| @@ -5508,17 +5508,45 @@ void copy_extent_buffer(struct extent_bu |
| } |
| } |
| |
| -/* |
| - * The extent buffer bitmap operations are done with byte granularity because |
| - * bitmap items are not guaranteed to be aligned to a word and therefore a |
| - * single word in a bitmap may straddle two pages in the extent buffer. |
| - */ |
| -#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) |
| -#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1) |
| -#define BITMAP_FIRST_BYTE_MASK(start) \ |
| - ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK) |
| -#define BITMAP_LAST_BYTE_MASK(nbits) \ |
| - (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1))) |
| +void le_bitmap_set(u8 *map, unsigned int start, int len) |
| +{ |
| + u8 *p = map + BIT_BYTE(start); |
| + const unsigned int size = start + len; |
| + int bits_to_set = BITS_PER_BYTE - (start % BITS_PER_BYTE); |
| + u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(start); |
| + |
| + while (len - bits_to_set >= 0) { |
| + *p |= mask_to_set; |
| + len -= bits_to_set; |
| + bits_to_set = BITS_PER_BYTE; |
| + mask_to_set = ~(u8)0; |
| + p++; |
| + } |
| + if (len) { |
| + mask_to_set &= BITMAP_LAST_BYTE_MASK(size); |
| + *p |= mask_to_set; |
| + } |
| +} |
| + |
| +void le_bitmap_clear(u8 *map, unsigned int start, int len) |
| +{ |
| + u8 *p = map + BIT_BYTE(start); |
| + const unsigned int size = start + len; |
| + int bits_to_clear = BITS_PER_BYTE - (start % BITS_PER_BYTE); |
| + u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(start); |
| + |
| + while (len - bits_to_clear >= 0) { |
| + *p &= ~mask_to_clear; |
| + len -= bits_to_clear; |
| + bits_to_clear = BITS_PER_BYTE; |
| + mask_to_clear = ~(u8)0; |
| + p++; |
| + } |
| + if (len) { |
| + mask_to_clear &= BITMAP_LAST_BYTE_MASK(size); |
| + *p &= ~mask_to_clear; |
| + } |
| +} |
| |
| /* |
| * eb_bitmap_offset() - calculate the page and offset of the byte containing the |
| @@ -5562,7 +5590,7 @@ static inline void eb_bitmap_offset(stru |
| int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start, |
| unsigned long nr) |
| { |
| - char *kaddr; |
| + u8 *kaddr; |
| struct page *page; |
| unsigned long i; |
| size_t offset; |
| @@ -5584,13 +5612,13 @@ int extent_buffer_test_bit(struct extent |
| void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start, |
| unsigned long pos, unsigned long len) |
| { |
| - char *kaddr; |
| + u8 *kaddr; |
| struct page *page; |
| unsigned long i; |
| size_t offset; |
| const unsigned int size = pos + len; |
| int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE); |
| - unsigned int mask_to_set = BITMAP_FIRST_BYTE_MASK(pos); |
| + u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(pos); |
| |
| eb_bitmap_offset(eb, start, pos, &i, &offset); |
| page = eb->pages[i]; |
| @@ -5601,7 +5629,7 @@ void extent_buffer_bitmap_set(struct ext |
| kaddr[offset] |= mask_to_set; |
| len -= bits_to_set; |
| bits_to_set = BITS_PER_BYTE; |
| - mask_to_set = ~0U; |
| + mask_to_set = ~(u8)0; |
| if (++offset >= PAGE_SIZE && len > 0) { |
| offset = 0; |
| page = eb->pages[++i]; |
| @@ -5626,13 +5654,13 @@ void extent_buffer_bitmap_set(struct ext |
| void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start, |
| unsigned long pos, unsigned long len) |
| { |
| - char *kaddr; |
| + u8 *kaddr; |
| struct page *page; |
| unsigned long i; |
| size_t offset; |
| const unsigned int size = pos + len; |
| int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE); |
| - unsigned int mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos); |
| + u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos); |
| |
| eb_bitmap_offset(eb, start, pos, &i, &offset); |
| page = eb->pages[i]; |
| @@ -5643,7 +5671,7 @@ void extent_buffer_bitmap_clear(struct e |
| kaddr[offset] &= ~mask_to_clear; |
| len -= bits_to_clear; |
| bits_to_clear = BITS_PER_BYTE; |
| - mask_to_clear = ~0U; |
| + mask_to_clear = ~(u8)0; |
| if (++offset >= PAGE_SIZE && len > 0) { |
| offset = 0; |
| page = eb->pages[++i]; |
| --- a/fs/btrfs/extent_io.h |
| +++ b/fs/btrfs/extent_io.h |
| @@ -58,6 +58,28 @@ |
| */ |
| #define EXTENT_PAGE_PRIVATE 1 |
| |
| +/* |
| + * The extent buffer bitmap operations are done with byte granularity instead of |
| + * word granularity for two reasons: |
| + * 1. The bitmaps must be little-endian on disk. |
| + * 2. Bitmap items are not guaranteed to be aligned to a word and therefore a |
| + * single word in a bitmap may straddle two pages in the extent buffer. |
| + */ |
| +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) |
| +#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1) |
| +#define BITMAP_FIRST_BYTE_MASK(start) \ |
| + ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK) |
| +#define BITMAP_LAST_BYTE_MASK(nbits) \ |
| + (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1))) |
| + |
| +static inline int le_test_bit(int nr, const u8 *addr) |
| +{ |
| + return 1U & (addr[BIT_BYTE(nr)] >> (nr & (BITS_PER_BYTE-1))); |
| +} |
| + |
| +extern void le_bitmap_set(u8 *map, unsigned int start, int len); |
| +extern void le_bitmap_clear(u8 *map, unsigned int start, int len); |
| + |
| struct extent_state; |
| struct btrfs_root; |
| struct btrfs_io_bio; |
| --- a/fs/btrfs/free-space-tree.c |
| +++ b/fs/btrfs/free-space-tree.c |
| @@ -151,7 +151,7 @@ static inline u32 free_space_bitmap_size |
| return DIV_ROUND_UP((u32)div_u64(size, sectorsize), BITS_PER_BYTE); |
| } |
| |
| -static unsigned long *alloc_bitmap(u32 bitmap_size) |
| +static u8 *alloc_bitmap(u32 bitmap_size) |
| { |
| void *mem; |
| |
| @@ -180,8 +180,7 @@ int convert_free_space_to_bitmaps(struct |
| struct btrfs_free_space_info *info; |
| struct btrfs_key key, found_key; |
| struct extent_buffer *leaf; |
| - unsigned long *bitmap; |
| - char *bitmap_cursor; |
| + u8 *bitmap, *bitmap_cursor; |
| u64 start, end; |
| u64 bitmap_range, i; |
| u32 bitmap_size, flags, expected_extent_count; |
| @@ -231,7 +230,7 @@ int convert_free_space_to_bitmaps(struct |
| block_group->sectorsize); |
| last = div_u64(found_key.objectid + found_key.offset - start, |
| block_group->sectorsize); |
| - bitmap_set(bitmap, first, last - first); |
| + le_bitmap_set(bitmap, first, last - first); |
| |
| extent_count++; |
| nr++; |
| @@ -269,7 +268,7 @@ int convert_free_space_to_bitmaps(struct |
| goto out; |
| } |
| |
| - bitmap_cursor = (char *)bitmap; |
| + bitmap_cursor = bitmap; |
| bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS; |
| i = start; |
| while (i < end) { |
| @@ -318,7 +317,7 @@ int convert_free_space_to_extents(struct |
| struct btrfs_free_space_info *info; |
| struct btrfs_key key, found_key; |
| struct extent_buffer *leaf; |
| - unsigned long *bitmap; |
| + u8 *bitmap; |
| u64 start, end; |
| /* Initialize to silence GCC. */ |
| u64 extent_start = 0; |
| @@ -362,7 +361,7 @@ int convert_free_space_to_extents(struct |
| break; |
| } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) { |
| unsigned long ptr; |
| - char *bitmap_cursor; |
| + u8 *bitmap_cursor; |
| u32 bitmap_pos, data_size; |
| |
| ASSERT(found_key.objectid >= start); |
| @@ -372,7 +371,7 @@ int convert_free_space_to_extents(struct |
| bitmap_pos = div_u64(found_key.objectid - start, |
| block_group->sectorsize * |
| BITS_PER_BYTE); |
| - bitmap_cursor = ((char *)bitmap) + bitmap_pos; |
| + bitmap_cursor = bitmap + bitmap_pos; |
| data_size = free_space_bitmap_size(found_key.offset, |
| block_group->sectorsize); |
| |
| @@ -409,7 +408,7 @@ int convert_free_space_to_extents(struct |
| offset = start; |
| bitnr = 0; |
| while (offset < end) { |
| - bit = !!test_bit(bitnr, bitmap); |
| + bit = !!le_test_bit(bitnr, bitmap); |
| if (prev_bit == 0 && bit == 1) { |
| extent_start = offset; |
| } else if (prev_bit == 1 && bit == 0) { |