Btrfs: fix free space tree bitmaps on big-endian systems
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: a5ed918285 ("Btrfs: implement the free space B-tree")
Cc: stable@vger.kernel.org # 4.5+
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>
This commit is contained in:
committed by
David Sterba
parent
08895a8b6b
commit
2fe1d55134
@@ -5524,17 +5524,45 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void le_bitmap_set(u8 *map, unsigned int start, int len)
|
||||||
* 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
|
u8 *p = map + BIT_BYTE(start);
|
||||||
* single word in a bitmap may straddle two pages in the extent buffer.
|
const unsigned int size = start + len;
|
||||||
*/
|
int bits_to_set = BITS_PER_BYTE - (start % BITS_PER_BYTE);
|
||||||
#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
|
u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(start);
|
||||||
#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
|
|
||||||
#define BITMAP_FIRST_BYTE_MASK(start) \
|
while (len - bits_to_set >= 0) {
|
||||||
((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
|
*p |= mask_to_set;
|
||||||
#define BITMAP_LAST_BYTE_MASK(nbits) \
|
len -= bits_to_set;
|
||||||
(BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
|
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
|
* eb_bitmap_offset() - calculate the page and offset of the byte containing the
|
||||||
@@ -5578,7 +5606,7 @@ static inline void eb_bitmap_offset(struct extent_buffer *eb,
|
|||||||
int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
|
int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
|
||||||
unsigned long nr)
|
unsigned long nr)
|
||||||
{
|
{
|
||||||
char *kaddr;
|
u8 *kaddr;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
@@ -5600,13 +5628,13 @@ int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
|
|||||||
void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
|
void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
|
||||||
unsigned long pos, unsigned long len)
|
unsigned long pos, unsigned long len)
|
||||||
{
|
{
|
||||||
char *kaddr;
|
u8 *kaddr;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
const unsigned int size = pos + len;
|
const unsigned int size = pos + len;
|
||||||
int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
|
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);
|
eb_bitmap_offset(eb, start, pos, &i, &offset);
|
||||||
page = eb->pages[i];
|
page = eb->pages[i];
|
||||||
@@ -5617,7 +5645,7 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
|
|||||||
kaddr[offset] |= mask_to_set;
|
kaddr[offset] |= mask_to_set;
|
||||||
len -= bits_to_set;
|
len -= bits_to_set;
|
||||||
bits_to_set = BITS_PER_BYTE;
|
bits_to_set = BITS_PER_BYTE;
|
||||||
mask_to_set = ~0U;
|
mask_to_set = ~(u8)0;
|
||||||
if (++offset >= PAGE_SIZE && len > 0) {
|
if (++offset >= PAGE_SIZE && len > 0) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
page = eb->pages[++i];
|
page = eb->pages[++i];
|
||||||
@@ -5642,13 +5670,13 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
|
|||||||
void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
|
void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
|
||||||
unsigned long pos, unsigned long len)
|
unsigned long pos, unsigned long len)
|
||||||
{
|
{
|
||||||
char *kaddr;
|
u8 *kaddr;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
unsigned long i;
|
unsigned long i;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
const unsigned int size = pos + len;
|
const unsigned int size = pos + len;
|
||||||
int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
|
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);
|
eb_bitmap_offset(eb, start, pos, &i, &offset);
|
||||||
page = eb->pages[i];
|
page = eb->pages[i];
|
||||||
@@ -5659,7 +5687,7 @@ void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
|
|||||||
kaddr[offset] &= ~mask_to_clear;
|
kaddr[offset] &= ~mask_to_clear;
|
||||||
len -= bits_to_clear;
|
len -= bits_to_clear;
|
||||||
bits_to_clear = BITS_PER_BYTE;
|
bits_to_clear = BITS_PER_BYTE;
|
||||||
mask_to_clear = ~0U;
|
mask_to_clear = ~(u8)0;
|
||||||
if (++offset >= PAGE_SIZE && len > 0) {
|
if (++offset >= PAGE_SIZE && len > 0) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
page = eb->pages[++i];
|
page = eb->pages[++i];
|
||||||
|
|||||||
@@ -59,6 +59,28 @@
|
|||||||
*/
|
*/
|
||||||
#define EXTENT_PAGE_PRIVATE 1
|
#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 extent_state;
|
||||||
struct btrfs_root;
|
struct btrfs_root;
|
||||||
struct btrfs_io_bio;
|
struct btrfs_io_bio;
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ static inline u32 free_space_bitmap_size(u64 size, u32 sectorsize)
|
|||||||
return DIV_ROUND_UP((u32)div_u64(size, sectorsize), BITS_PER_BYTE);
|
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;
|
void *mem;
|
||||||
|
|
||||||
@@ -180,8 +180,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_free_space_info *info;
|
struct btrfs_free_space_info *info;
|
||||||
struct btrfs_key key, found_key;
|
struct btrfs_key key, found_key;
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
unsigned long *bitmap;
|
u8 *bitmap, *bitmap_cursor;
|
||||||
char *bitmap_cursor;
|
|
||||||
u64 start, end;
|
u64 start, end;
|
||||||
u64 bitmap_range, i;
|
u64 bitmap_range, i;
|
||||||
u32 bitmap_size, flags, expected_extent_count;
|
u32 bitmap_size, flags, expected_extent_count;
|
||||||
@@ -231,7 +230,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
|
|||||||
block_group->sectorsize);
|
block_group->sectorsize);
|
||||||
last = div_u64(found_key.objectid + found_key.offset - start,
|
last = div_u64(found_key.objectid + found_key.offset - start,
|
||||||
block_group->sectorsize);
|
block_group->sectorsize);
|
||||||
bitmap_set(bitmap, first, last - first);
|
le_bitmap_set(bitmap, first, last - first);
|
||||||
|
|
||||||
extent_count++;
|
extent_count++;
|
||||||
nr++;
|
nr++;
|
||||||
@@ -269,7 +268,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap_cursor = (char *)bitmap;
|
bitmap_cursor = bitmap;
|
||||||
bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
|
bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
|
||||||
i = start;
|
i = start;
|
||||||
while (i < end) {
|
while (i < end) {
|
||||||
@@ -318,7 +317,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
|
|||||||
struct btrfs_free_space_info *info;
|
struct btrfs_free_space_info *info;
|
||||||
struct btrfs_key key, found_key;
|
struct btrfs_key key, found_key;
|
||||||
struct extent_buffer *leaf;
|
struct extent_buffer *leaf;
|
||||||
unsigned long *bitmap;
|
u8 *bitmap;
|
||||||
u64 start, end;
|
u64 start, end;
|
||||||
/* Initialize to silence GCC. */
|
/* Initialize to silence GCC. */
|
||||||
u64 extent_start = 0;
|
u64 extent_start = 0;
|
||||||
@@ -362,7 +361,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
|
|||||||
break;
|
break;
|
||||||
} else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
|
} else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
|
||||||
unsigned long ptr;
|
unsigned long ptr;
|
||||||
char *bitmap_cursor;
|
u8 *bitmap_cursor;
|
||||||
u32 bitmap_pos, data_size;
|
u32 bitmap_pos, data_size;
|
||||||
|
|
||||||
ASSERT(found_key.objectid >= start);
|
ASSERT(found_key.objectid >= start);
|
||||||
@@ -372,7 +371,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
|
|||||||
bitmap_pos = div_u64(found_key.objectid - start,
|
bitmap_pos = div_u64(found_key.objectid - start,
|
||||||
block_group->sectorsize *
|
block_group->sectorsize *
|
||||||
BITS_PER_BYTE);
|
BITS_PER_BYTE);
|
||||||
bitmap_cursor = ((char *)bitmap) + bitmap_pos;
|
bitmap_cursor = bitmap + bitmap_pos;
|
||||||
data_size = free_space_bitmap_size(found_key.offset,
|
data_size = free_space_bitmap_size(found_key.offset,
|
||||||
block_group->sectorsize);
|
block_group->sectorsize);
|
||||||
|
|
||||||
@@ -409,7 +408,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
|
|||||||
offset = start;
|
offset = start;
|
||||||
bitnr = 0;
|
bitnr = 0;
|
||||||
while (offset < end) {
|
while (offset < end) {
|
||||||
bit = !!test_bit(bitnr, bitmap);
|
bit = !!le_test_bit(bitnr, bitmap);
|
||||||
if (prev_bit == 0 && bit == 1) {
|
if (prev_bit == 0 && bit == 1) {
|
||||||
extent_start = offset;
|
extent_start = offset;
|
||||||
} else if (prev_bit == 1 && bit == 0) {
|
} else if (prev_bit == 1 && bit == 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user