Commit d9ed71e5 authored by Chris Mason's avatar Chris Mason

Merge branch 'fst-fixes' of...

Merge branch 'fst-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux into for-linus-4.9
Signed-off-by: default avatarChris Mason <clm@fb.com>
parents 19c4d2f9 0e675785
...@@ -252,7 +252,8 @@ struct btrfs_super_block { ...@@ -252,7 +252,8 @@ struct btrfs_super_block {
#define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL #define BTRFS_FEATURE_COMPAT_SAFE_CLEAR 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SUPP \ #define BTRFS_FEATURE_COMPAT_RO_SUPP \
(BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE) (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE | \
BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID)
#define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL #define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL
#define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL #define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL
......
...@@ -2586,6 +2586,7 @@ int open_ctree(struct super_block *sb, ...@@ -2586,6 +2586,7 @@ int open_ctree(struct super_block *sb,
int num_backups_tried = 0; int num_backups_tried = 0;
int backup_index = 0; int backup_index = 0;
int max_active; int max_active;
int clear_free_space_tree = 0;
tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL); tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL); chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
...@@ -3148,6 +3149,26 @@ int open_ctree(struct super_block *sb, ...@@ -3148,6 +3149,26 @@ int open_ctree(struct super_block *sb,
if (sb->s_flags & MS_RDONLY) if (sb->s_flags & MS_RDONLY)
return 0; return 0;
if (btrfs_test_opt(fs_info, CLEAR_CACHE) &&
btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
clear_free_space_tree = 1;
} else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID)) {
btrfs_warn(fs_info, "free space tree is invalid");
clear_free_space_tree = 1;
}
if (clear_free_space_tree) {
btrfs_info(fs_info, "clearing free space tree");
ret = btrfs_clear_free_space_tree(fs_info);
if (ret) {
btrfs_warn(fs_info,
"failed to clear free space tree: %d", ret);
close_ctree(tree_root);
return ret;
}
}
if (btrfs_test_opt(tree_root->fs_info, FREE_SPACE_TREE) && if (btrfs_test_opt(tree_root->fs_info, FREE_SPACE_TREE) &&
!btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) { !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
btrfs_info(fs_info, "creating free space tree"); btrfs_info(fs_info, "creating free space tree");
...@@ -3185,18 +3206,6 @@ int open_ctree(struct super_block *sb, ...@@ -3185,18 +3206,6 @@ int open_ctree(struct super_block *sb,
btrfs_qgroup_rescan_resume(fs_info); btrfs_qgroup_rescan_resume(fs_info);
if (btrfs_test_opt(tree_root->fs_info, CLEAR_CACHE) &&
btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
btrfs_info(fs_info, "clearing free space tree");
ret = btrfs_clear_free_space_tree(fs_info);
if (ret) {
btrfs_warn(fs_info,
"failed to clear free space tree: %d", ret);
close_ctree(tree_root);
return ret;
}
}
if (!fs_info->uuid_root) { if (!fs_info->uuid_root) {
btrfs_info(fs_info, "creating UUID tree"); btrfs_info(fs_info, "creating UUID tree");
ret = btrfs_create_uuid_tree(fs_info); ret = btrfs_create_uuid_tree(fs_info);
......
...@@ -5558,17 +5558,45 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src, ...@@ -5558,17 +5558,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
...@@ -5612,7 +5640,7 @@ static inline void eb_bitmap_offset(struct extent_buffer *eb, ...@@ -5612,7 +5640,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;
...@@ -5634,13 +5662,13 @@ int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start, ...@@ -5634,13 +5662,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];
...@@ -5651,7 +5679,7 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start, ...@@ -5651,7 +5679,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];
...@@ -5676,13 +5704,13 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start, ...@@ -5676,13 +5704,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];
...@@ -5693,7 +5721,7 @@ void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start, ...@@ -5693,7 +5721,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 @@ ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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++;
...@@ -270,7 +269,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans, ...@@ -270,7 +269,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) {
...@@ -319,7 +318,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, ...@@ -319,7 +318,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;
...@@ -363,7 +362,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, ...@@ -363,7 +362,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);
...@@ -373,7 +372,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, ...@@ -373,7 +372,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);
...@@ -410,7 +409,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans, ...@@ -410,7 +409,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) {
...@@ -1185,6 +1184,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info) ...@@ -1185,6 +1184,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info)
} }
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE); btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags); clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
ret = btrfs_commit_transaction(trans, tree_root); ret = btrfs_commit_transaction(trans, tree_root);
...@@ -1253,6 +1253,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info) ...@@ -1253,6 +1253,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
return PTR_ERR(trans); return PTR_ERR(trans);
btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE); btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE);
btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
fs_info->free_space_root = NULL; fs_info->free_space_root = NULL;
ret = clear_free_space_tree(trans, free_space_root); ret = clear_free_space_tree(trans, free_space_root);
......
...@@ -273,20 +273,37 @@ static int test_find_delalloc(u32 sectorsize) ...@@ -273,20 +273,37 @@ static int test_find_delalloc(u32 sectorsize)
return ret; return ret;
} }
/** static int check_eb_bitmap(unsigned long *bitmap, struct extent_buffer *eb,
* test_bit_in_byte - Determine whether a bit is set in a byte unsigned long len)
* @nr: bit number to test
* @addr: Address to start counting from
*/
static inline int test_bit_in_byte(int nr, const u8 *addr)
{ {
return 1UL & (addr[nr / BITS_PER_BYTE] >> (nr & (BITS_PER_BYTE - 1))); unsigned long i;
for (i = 0; i < len * BITS_PER_BYTE; i++) {
int bit, bit1;
bit = !!test_bit(i, bitmap);
bit1 = !!extent_buffer_test_bit(eb, 0, i);
if (bit1 != bit) {
test_msg("Bits do not match\n");
return -EINVAL;
}
bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
i % BITS_PER_BYTE);
if (bit1 != bit) {
test_msg("Offset bits do not match\n");
return -EINVAL;
}
}
return 0;
} }
static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
unsigned long len) unsigned long len)
{ {
unsigned long i, x; unsigned long i, j;
u32 x;
int ret;
memset(bitmap, 0, len); memset(bitmap, 0, len);
memset_extent_buffer(eb, 0, 0, len); memset_extent_buffer(eb, 0, 0, len);
...@@ -297,16 +314,18 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, ...@@ -297,16 +314,18 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
bitmap_set(bitmap, 0, len * BITS_PER_BYTE); bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { ret = check_eb_bitmap(bitmap, eb, len);
if (ret) {
test_msg("Setting all bits failed\n"); test_msg("Setting all bits failed\n");
return -EINVAL; return ret;
} }
bitmap_clear(bitmap, 0, len * BITS_PER_BYTE); bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE); extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { ret = check_eb_bitmap(bitmap, eb, len);
if (ret) {
test_msg("Clearing all bits failed\n"); test_msg("Clearing all bits failed\n");
return -EINVAL; return ret;
} }
/* Straddling pages test */ /* Straddling pages test */
...@@ -316,9 +335,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, ...@@ -316,9 +335,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
sizeof(long) * BITS_PER_BYTE); sizeof(long) * BITS_PER_BYTE);
extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0, extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0,
sizeof(long) * BITS_PER_BYTE); sizeof(long) * BITS_PER_BYTE);
if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { ret = check_eb_bitmap(bitmap, eb, len);
if (ret) {
test_msg("Setting straddling pages failed\n"); test_msg("Setting straddling pages failed\n");
return -EINVAL; return ret;
} }
bitmap_set(bitmap, 0, len * BITS_PER_BYTE); bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
...@@ -328,9 +348,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, ...@@ -328,9 +348,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE); extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
extent_buffer_bitmap_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0, extent_buffer_bitmap_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0,
sizeof(long) * BITS_PER_BYTE); sizeof(long) * BITS_PER_BYTE);
if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) { ret = check_eb_bitmap(bitmap, eb, len);
if (ret) {
test_msg("Clearing straddling pages failed\n"); test_msg("Clearing straddling pages failed\n");
return -EINVAL; return ret;
} }
} }
...@@ -339,28 +360,22 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb, ...@@ -339,28 +360,22 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
* something repetitive that could miss some hypothetical off-by-n bug. * something repetitive that could miss some hypothetical off-by-n bug.
*/ */
x = 0; x = 0;
for (i = 0; i < len / sizeof(long); i++) { bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffUL; extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
bitmap[i] = x; for (i = 0; i < len * BITS_PER_BYTE / 32; i++) {
} x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffU;
write_extent_buffer(eb, bitmap, 0, len); for (j = 0; j < 32; j++) {
if (x & (1U << j)) {
for (i = 0; i < len * BITS_PER_BYTE; i++) { bitmap_set(bitmap, i * 32 + j, 1);
int bit, bit1; extent_buffer_bitmap_set(eb, 0, i * 32 + j, 1);
}
bit = !!test_bit_in_byte(i, (u8 *)bitmap);
bit1 = !!extent_buffer_test_bit(eb, 0, i);
if (bit1 != bit) {
test_msg("Testing bit pattern failed\n");
return -EINVAL;
} }
}
bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE, ret = check_eb_bitmap(bitmap, eb, len);
i % BITS_PER_BYTE); if (ret) {
if (bit1 != bit) { test_msg("Random bit pattern failed\n");
test_msg("Testing bit pattern with offset failed\n"); return ret;
return -EINVAL;
}
} }
return 0; return 0;
......
...@@ -24,20 +24,15 @@ ...@@ -24,20 +24,15 @@
#include "../transaction.h" #include "../transaction.h"
struct free_space_extent { struct free_space_extent {
u64 start, length; u64 start;
u64 length;
}; };
/*
* The test cases align their operations to this in order to hit some of the
* edge cases in the bitmap code.
*/
#define BITMAP_RANGE (BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE)
static int __check_free_space_extents(struct btrfs_trans_handle *trans, static int __check_free_space_extents(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path, struct btrfs_path *path,
struct free_space_extent *extents, const struct free_space_extent * const extents,
unsigned int num_extents) unsigned int num_extents)
{ {
struct btrfs_free_space_info *info; struct btrfs_free_space_info *info;
...@@ -126,7 +121,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans, ...@@ -126,7 +121,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path, struct btrfs_path *path,
struct free_space_extent *extents, const struct free_space_extent * const extents,
unsigned int num_extents) unsigned int num_extents)
{ {
struct btrfs_free_space_info *info; struct btrfs_free_space_info *info;
...@@ -168,9 +163,10 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans, ...@@ -168,9 +163,10 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
static int test_empty_block_group(struct btrfs_trans_handle *trans, static int test_empty_block_group(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid, cache->key.offset}, {cache->key.objectid, cache->key.offset},
}; };
...@@ -181,9 +177,10 @@ static int test_empty_block_group(struct btrfs_trans_handle *trans, ...@@ -181,9 +177,10 @@ static int test_empty_block_group(struct btrfs_trans_handle *trans,
static int test_remove_all(struct btrfs_trans_handle *trans, static int test_remove_all(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = {}; const struct free_space_extent extents[] = {};
int ret; int ret;
ret = __remove_from_free_space_tree(trans, fs_info, cache, path, ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
...@@ -201,16 +198,17 @@ static int test_remove_all(struct btrfs_trans_handle *trans, ...@@ -201,16 +198,17 @@ static int test_remove_all(struct btrfs_trans_handle *trans,
static int test_remove_beginning(struct btrfs_trans_handle *trans, static int test_remove_beginning(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid + BITMAP_RANGE, {cache->key.objectid + alignment,
cache->key.offset - BITMAP_RANGE}, cache->key.offset - alignment},
}; };
int ret; int ret;
ret = __remove_from_free_space_tree(trans, fs_info, cache, path, ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid, BITMAP_RANGE); cache->key.objectid, alignment);
if (ret) { if (ret) {
test_msg("Could not remove free space\n"); test_msg("Could not remove free space\n");
return ret; return ret;
...@@ -224,17 +222,18 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans, ...@@ -224,17 +222,18 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans,
static int test_remove_end(struct btrfs_trans_handle *trans, static int test_remove_end(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid, cache->key.offset - BITMAP_RANGE}, {cache->key.objectid, cache->key.offset - alignment},
}; };
int ret; int ret;
ret = __remove_from_free_space_tree(trans, fs_info, cache, path, ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + cache->key.objectid +
cache->key.offset - BITMAP_RANGE, cache->key.offset - alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not remove free space\n"); test_msg("Could not remove free space\n");
return ret; return ret;
...@@ -247,18 +246,19 @@ static int test_remove_end(struct btrfs_trans_handle *trans, ...@@ -247,18 +246,19 @@ static int test_remove_end(struct btrfs_trans_handle *trans,
static int test_remove_middle(struct btrfs_trans_handle *trans, static int test_remove_middle(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid, BITMAP_RANGE}, {cache->key.objectid, alignment},
{cache->key.objectid + 2 * BITMAP_RANGE, {cache->key.objectid + 2 * alignment,
cache->key.offset - 2 * BITMAP_RANGE}, cache->key.offset - 2 * alignment},
}; };
int ret; int ret;
ret = __remove_from_free_space_tree(trans, fs_info, cache, path, ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + BITMAP_RANGE, cache->key.objectid + alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not remove free space\n"); test_msg("Could not remove free space\n");
return ret; return ret;
...@@ -271,10 +271,11 @@ static int test_remove_middle(struct btrfs_trans_handle *trans, ...@@ -271,10 +271,11 @@ static int test_remove_middle(struct btrfs_trans_handle *trans,
static int test_merge_left(struct btrfs_trans_handle *trans, static int test_merge_left(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid, 2 * BITMAP_RANGE}, {cache->key.objectid, 2 * alignment},
}; };
int ret; int ret;
...@@ -287,15 +288,15 @@ static int test_merge_left(struct btrfs_trans_handle *trans, ...@@ -287,15 +288,15 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid, BITMAP_RANGE); cache->key.objectid, alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + BITMAP_RANGE, cache->key.objectid + alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
...@@ -308,10 +309,11 @@ static int test_merge_left(struct btrfs_trans_handle *trans, ...@@ -308,10 +309,11 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
static int test_merge_right(struct btrfs_trans_handle *trans, static int test_merge_right(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid + BITMAP_RANGE, 2 * BITMAP_RANGE}, {cache->key.objectid + alignment, 2 * alignment},
}; };
int ret; int ret;
...@@ -324,16 +326,16 @@ static int test_merge_right(struct btrfs_trans_handle *trans, ...@@ -324,16 +326,16 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + 2 * BITMAP_RANGE, cache->key.objectid + 2 * alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + BITMAP_RANGE, cache->key.objectid + alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
...@@ -346,10 +348,11 @@ static int test_merge_right(struct btrfs_trans_handle *trans, ...@@ -346,10 +348,11 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
static int test_merge_both(struct btrfs_trans_handle *trans, static int test_merge_both(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid, 3 * BITMAP_RANGE}, {cache->key.objectid, 3 * alignment},
}; };
int ret; int ret;
...@@ -362,23 +365,23 @@ static int test_merge_both(struct btrfs_trans_handle *trans, ...@@ -362,23 +365,23 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid, BITMAP_RANGE); cache->key.objectid, alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + 2 * BITMAP_RANGE, cache->key.objectid + 2 * alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + BITMAP_RANGE, cache->key.objectid + alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
...@@ -391,12 +394,13 @@ static int test_merge_both(struct btrfs_trans_handle *trans, ...@@ -391,12 +394,13 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
static int test_merge_none(struct btrfs_trans_handle *trans, static int test_merge_none(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_block_group_cache *cache, struct btrfs_block_group_cache *cache,
struct btrfs_path *path) struct btrfs_path *path,
u32 alignment)
{ {
struct free_space_extent extents[] = { const struct free_space_extent extents[] = {
{cache->key.objectid, BITMAP_RANGE}, {cache->key.objectid, alignment},
{cache->key.objectid + 2 * BITMAP_RANGE, BITMAP_RANGE}, {cache->key.objectid + 2 * alignment, alignment},
{cache->key.objectid + 4 * BITMAP_RANGE, BITMAP_RANGE}, {cache->key.objectid + 4 * alignment, alignment},
}; };
int ret; int ret;
...@@ -409,23 +413,23 @@ static int test_merge_none(struct btrfs_trans_handle *trans, ...@@ -409,23 +413,23 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid, BITMAP_RANGE); cache->key.objectid, alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + 4 * BITMAP_RANGE, cache->key.objectid + 4 * alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
} }
ret = __add_to_free_space_tree(trans, fs_info, cache, path, ret = __add_to_free_space_tree(trans, fs_info, cache, path,
cache->key.objectid + 2 * BITMAP_RANGE, cache->key.objectid + 2 * alignment,
BITMAP_RANGE); alignment);
if (ret) { if (ret) {
test_msg("Could not add free space\n"); test_msg("Could not add free space\n");
return ret; return ret;
...@@ -438,10 +442,11 @@ static int test_merge_none(struct btrfs_trans_handle *trans, ...@@ -438,10 +442,11 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
typedef int (*test_func_t)(struct btrfs_trans_handle *, typedef int (*test_func_t)(struct btrfs_trans_handle *,
struct btrfs_fs_info *, struct btrfs_fs_info *,
struct btrfs_block_group_cache *, struct btrfs_block_group_cache *,
struct btrfs_path *); struct btrfs_path *,
u32 alignment);
static int run_test(test_func_t test_func, int bitmaps, static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize,
u32 sectorsize, u32 nodesize) u32 nodesize, u32 alignment)
{ {
struct btrfs_fs_info *fs_info; struct btrfs_fs_info *fs_info;
struct btrfs_root *root = NULL; struct btrfs_root *root = NULL;
...@@ -480,7 +485,7 @@ static int run_test(test_func_t test_func, int bitmaps, ...@@ -480,7 +485,7 @@ static int run_test(test_func_t test_func, int bitmaps,
btrfs_set_header_nritems(root->node, 0); btrfs_set_header_nritems(root->node, 0);
root->alloc_bytenr += 2 * nodesize; root->alloc_bytenr += 2 * nodesize;
cache = btrfs_alloc_dummy_block_group(8 * BITMAP_RANGE, sectorsize); cache = btrfs_alloc_dummy_block_group(8 * alignment, sectorsize);
if (!cache) { if (!cache) {
test_msg("Couldn't allocate dummy block group cache\n"); test_msg("Couldn't allocate dummy block group cache\n");
ret = -ENOMEM; ret = -ENOMEM;
...@@ -514,7 +519,7 @@ static int run_test(test_func_t test_func, int bitmaps, ...@@ -514,7 +519,7 @@ static int run_test(test_func_t test_func, int bitmaps,
} }
} }
ret = test_func(&trans, root->fs_info, cache, path); ret = test_func(&trans, root->fs_info, cache, path, alignment);
if (ret) if (ret)
goto out; goto out;
...@@ -539,15 +544,27 @@ static int run_test(test_func_t test_func, int bitmaps, ...@@ -539,15 +544,27 @@ static int run_test(test_func_t test_func, int bitmaps,
return ret; return ret;
} }
static int run_test_both_formats(test_func_t test_func, static int run_test_both_formats(test_func_t test_func, u32 sectorsize,
u32 sectorsize, u32 nodesize) u32 nodesize, u32 alignment)
{ {
int test_ret = 0;
int ret; int ret;
ret = run_test(test_func, 0, sectorsize, nodesize); ret = run_test(test_func, 0, sectorsize, nodesize, alignment);
if (ret) if (ret) {
return ret; test_msg("%pf failed with extents, sectorsize=%u, nodesize=%u, alignment=%u\n",
return run_test(test_func, 1, sectorsize, nodesize); test_func, sectorsize, nodesize, alignment);
test_ret = ret;
}
ret = run_test(test_func, 1, sectorsize, nodesize, alignment);
if (ret) {
test_msg("%pf failed with bitmaps, sectorsize=%u, nodesize=%u, alignment=%u\n",
test_func, sectorsize, nodesize, alignment);
test_ret = ret;
}
return test_ret;
} }
int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize) int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
...@@ -563,18 +580,30 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize) ...@@ -563,18 +580,30 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
test_merge_both, test_merge_both,
test_merge_none, test_merge_none,
}; };
u32 bitmap_alignment;
int test_ret = 0;
int i; int i;
/*
* Align some operations to a page to flush out bugs in the extent
* buffer bitmap handling of highmem.
*/
bitmap_alignment = BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE;
test_msg("Running free space tree tests\n"); test_msg("Running free space tree tests\n");
for (i = 0; i < ARRAY_SIZE(tests); i++) { for (i = 0; i < ARRAY_SIZE(tests); i++) {
int ret = run_test_both_formats(tests[i], sectorsize, int ret;
nodesize);
if (ret) { ret = run_test_both_formats(tests[i], sectorsize, nodesize,
test_msg("%pf : sectorsize %u failed\n", sectorsize);
tests[i], sectorsize); if (ret)
return ret; test_ret = ret;
}
ret = run_test_both_formats(tests[i], sectorsize, nodesize,
bitmap_alignment);
if (ret)
test_ret = ret;
} }
return 0; return test_ret;
} }
...@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args { ...@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args {
* Used by: * Used by:
* struct btrfs_ioctl_feature_flags * struct btrfs_ioctl_feature_flags
*/ */
#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0) #define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
/*
* Older kernels (< 4.9) on big-endian systems produced broken free space tree
* bitmaps, and btrfs-progs also used to corrupt the free space tree (versions
* < 4.7.3). If this bit is clear, then the free space tree cannot be trusted.
* btrfs-progs can also intentionally clear this bit to ask the kernel to
* rebuild the free space tree, however this might not work on older kernels
* that do not know about this bit. If not sure, clear the cache manually on
* first mount when booting older kernel versions.
*/
#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1)
#define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0)
#define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1) #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment