Merge branch 'for-linus-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mason...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 15 Oct 2016 00:44:56 +0000 (17:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 15 Oct 2016 00:44:56 +0000 (17:44 -0700)
Pull btrfs fixes from Chris Mason:
 "Some fixes from Omar and Dave Sterba for our new free space tree.

  This isn't heavily used yet, but as we move toward making it the new
  default we wanted to nail down an endian bug"

* 'for-linus-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux-btrfs:
  btrfs: tests: uninline member definitions in free_space_extent
  btrfs: tests: constify free space extent specs
  Btrfs: expand free space tree sanity tests to catch endianness bug
  Btrfs: fix extent buffer bitmap tests on big-endian systems
  Btrfs: catch invalid free space trees
  Btrfs: fix mount -o clear_cache,space_cache=v2
  Btrfs: fix free space tree bitmaps on big-endian systems

fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/free-space-tree.c
fs/btrfs/tests/extent-io-tests.c
fs/btrfs/tests/free-space-tree-tests.c
include/uapi/linux/btrfs.h

index 6c21bad..0b8ce2b 100644 (file)
@@ -252,7 +252,8 @@ struct btrfs_super_block {
 #define BTRFS_FEATURE_COMPAT_SAFE_CLEAR                0ULL
 
 #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_CLEAR     0ULL
index e720d3e..3a57f99 100644 (file)
@@ -2586,6 +2586,7 @@ int open_ctree(struct super_block *sb,
        int num_backups_tried = 0;
        int backup_index = 0;
        int max_active;
+       int clear_free_space_tree = 0;
 
        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);
@@ -3148,6 +3149,26 @@ retry_root_backup:
        if (sb->s_flags & MS_RDONLY)
                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) &&
            !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
                btrfs_info(fs_info, "creating free space tree");
@@ -3185,18 +3206,6 @@ retry_root_backup:
 
        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) {
                btrfs_info(fs_info, "creating UUID tree");
                ret = btrfs_create_uuid_tree(fs_info);
index ee40384..66a7551 100644 (file)
@@ -5558,17 +5558,45 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
        }
 }
 
-/*
- * 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
@@ -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,
                           unsigned long nr)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
@@ -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,
                              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];
@@ -5651,7 +5679,7 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
                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];
@@ -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,
                                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];
@@ -5693,7 +5721,7 @@ void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
                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];
index 4a094f1..ab31d14 100644 (file)
  */
 #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;
index e4a42a8..57401b4 100644 (file)
@@ -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);
 }
 
-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 btrfs_trans_handle *trans,
        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 btrfs_trans_handle *trans,
                                                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++;
@@ -270,7 +269,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
                goto out;
        }
 
-       bitmap_cursor = (char *)bitmap;
+       bitmap_cursor = bitmap;
        bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
        i = start;
        while (i < end) {
@@ -319,7 +318,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
        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;
@@ -363,7 +362,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                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);
@@ -373,7 +372,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                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);
 
@@ -410,7 +409,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
        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) {
@@ -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_VALID);
        clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
 
        ret = btrfs_commit_transaction(trans, tree_root);
@@ -1253,6 +1253,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
                return PTR_ERR(trans);
 
        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;
 
        ret = clear_free_space_tree(trans, free_space_root);
index d19ab03..caad80b 100644 (file)
@@ -273,20 +273,37 @@ out:
        return ret;
 }
 
-/**
- * test_bit_in_byte - Determine whether a bit is set in a byte
- * @nr: bit number to test
- * @addr: Address to start counting from
- */
-static inline int test_bit_in_byte(int nr, const u8 *addr)
+static int check_eb_bitmap(unsigned long *bitmap, struct extent_buffer *eb,
+                          unsigned long len)
 {
-       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,
                             unsigned long len)
 {
-       unsigned long i, x;
+       unsigned long i, j;
+       u32 x;
+       int ret;
 
        memset(bitmap, 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,
 
        bitmap_set(bitmap, 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");
-               return -EINVAL;
+               return ret;
        }
 
        bitmap_clear(bitmap, 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");
-               return -EINVAL;
+               return ret;
        }
 
        /* Straddling pages test */
@@ -316,9 +335,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                        sizeof(long) * BITS_PER_BYTE);
                extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0,
                                        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");
-                       return -EINVAL;
+                       return ret;
                }
 
                bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
@@ -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_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0,
                                        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");
-                       return -EINVAL;
+                       return ret;
                }
        }
 
@@ -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.
         */
        x = 0;
-       for (i = 0; i < len / sizeof(long); i++) {
-               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffUL;
-               bitmap[i] = x;
-       }
-       write_extent_buffer(eb, bitmap, 0, len);
-
-       for (i = 0; i < len * BITS_PER_BYTE; i++) {
-               int bit, bit1;
-
-               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;
+       bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
+       extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
+       for (i = 0; i < len * BITS_PER_BYTE / 32; i++) {
+               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffU;
+               for (j = 0; j < 32; j++) {
+                       if (x & (1U << j)) {
+                               bitmap_set(bitmap, i * 32 + j, 1);
+                               extent_buffer_bitmap_set(eb, 0, i * 32 + j, 1);
+                       }
                }
+       }
 
-               bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
-                                               i % BITS_PER_BYTE);
-               if (bit1 != bit) {
-                       test_msg("Testing bit pattern with offset failed\n");
-                       return -EINVAL;
-               }
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
+               test_msg("Random bit pattern failed\n");
+               return ret;
        }
 
        return 0;
index 7508d3b..6e14404 100644 (file)
 #include "../transaction.h"
 
 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,
                                      struct btrfs_fs_info *fs_info,
                                      struct btrfs_block_group_cache *cache,
                                      struct btrfs_path *path,
-                                     struct free_space_extent *extents,
+                                     const struct free_space_extent * const extents,
                                      unsigned int num_extents)
 {
        struct btrfs_free_space_info *info;
@@ -126,7 +121,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
                                    struct btrfs_fs_info *fs_info,
                                    struct btrfs_block_group_cache *cache,
                                    struct btrfs_path *path,
-                                   struct free_space_extent *extents,
+                                   const struct free_space_extent * const extents,
                                    unsigned int num_extents)
 {
        struct btrfs_free_space_info *info;
@@ -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,
                                  struct btrfs_fs_info *fs_info,
                                  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},
        };
 
@@ -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,
                           struct btrfs_fs_info *fs_info,
                           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;
 
        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,
 static int test_remove_beginning(struct btrfs_trans_handle *trans,
                                 struct btrfs_fs_info *fs_info,
                                 struct btrfs_block_group_cache *cache,
-                                struct btrfs_path *path)
+                                struct btrfs_path *path,
+                                u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid + BITMAP_RANGE,
-                       cache->key.offset - BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid + alignment,
+                       cache->key.offset - alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
-                                           cache->key.objectid, BITMAP_RANGE);
+                                           cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -224,17 +222,18 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans,
 static int test_remove_end(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, cache->key.offset - BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, cache->key.offset - alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
                                            cache->key.objectid +
-                                           cache->key.offset - BITMAP_RANGE,
-                                           BITMAP_RANGE);
+                                           cache->key.offset - alignment,
+                                           alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -247,18 +246,19 @@ static int test_remove_end(struct btrfs_trans_handle *trans,
 static int test_remove_middle(struct btrfs_trans_handle *trans,
                              struct btrfs_fs_info *fs_info,
                              struct btrfs_block_group_cache *cache,
-                             struct btrfs_path *path)
+                             struct btrfs_path *path,
+                             u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, BITMAP_RANGE},
-               {cache->key.objectid + 2 * BITMAP_RANGE,
-                       cache->key.offset - 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, alignment},
+               {cache->key.objectid + 2 * alignment,
+                       cache->key.offset - 2 * alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
-                                           cache->key.objectid + BITMAP_RANGE,
-                                           BITMAP_RANGE);
+                                           cache->key.objectid + alignment,
+                                           alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -271,10 +271,11 @@ static int test_remove_middle(struct btrfs_trans_handle *trans,
 static int test_merge_left(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, 2 * alignment},
        };
        int ret;
 
@@ -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,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -308,10 +309,11 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
 static int test_merge_right(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid + BITMAP_RANGE, 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid + alignment, 2 * alignment},
        };
        int ret;
 
@@ -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,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -346,10 +348,11 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
 static int test_merge_both(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, 3 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, 3 * alignment},
        };
        int ret;
 
@@ -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,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -391,12 +394,13 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
 static int test_merge_none(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, BITMAP_RANGE},
-               {cache->key.objectid + 2 * BITMAP_RANGE, BITMAP_RANGE},
-               {cache->key.objectid + 4 * BITMAP_RANGE, BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, alignment},
+               {cache->key.objectid + 2 * alignment, alignment},
+               {cache->key.objectid + 4 * alignment, alignment},
        };
        int ret;
 
@@ -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,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 4 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 4 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -438,10 +442,11 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
 typedef int (*test_func_t)(struct btrfs_trans_handle *,
                           struct btrfs_fs_info *,
                           struct btrfs_block_group_cache *,
-                          struct btrfs_path *);
+                          struct btrfs_path *,
+                          u32 alignment);
 
-static int run_test(test_func_t test_func, int bitmaps,
-               u32 sectorsize, u32 nodesize)
+static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize,
+                   u32 nodesize, u32 alignment)
 {
        struct btrfs_fs_info *fs_info;
        struct btrfs_root *root = NULL;
@@ -480,7 +485,7 @@ static int run_test(test_func_t test_func, int bitmaps,
        btrfs_set_header_nritems(root->node, 0);
        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) {
                test_msg("Couldn't allocate dummy block group cache\n");
                ret = -ENOMEM;
@@ -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)
                goto out;
 
@@ -539,15 +544,27 @@ out:
        return ret;
 }
 
-static int run_test_both_formats(test_func_t test_func,
-       u32 sectorsize, u32 nodesize)
+static int run_test_both_formats(test_func_t test_func, u32 sectorsize,
+                                u32 nodesize, u32 alignment)
 {
+       int test_ret = 0;
        int ret;
 
-       ret = run_test(test_func, 0, sectorsize, nodesize);
-       if (ret)
-               return ret;
-       return run_test(test_func, 1, sectorsize, nodesize);
+       ret = run_test(test_func, 0, sectorsize, nodesize, alignment);
+       if (ret) {
+               test_msg("%pf failed with extents, sectorsize=%u, nodesize=%u, alignment=%u\n",
+                        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)
@@ -563,18 +580,30 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
                test_merge_both,
                test_merge_none,
        };
+       u32 bitmap_alignment;
+       int test_ret = 0;
        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");
        for (i = 0; i < ARRAY_SIZE(tests); i++) {
-               int ret = run_test_both_formats(tests[i], sectorsize,
-                       nodesize);
-               if (ret) {
-                       test_msg("%pf : sectorsize %u failed\n",
-                               tests[i], sectorsize);
-                       return ret;
-               }
+               int ret;
+
+               ret = run_test_both_formats(tests[i], sectorsize, nodesize,
+                                           sectorsize);
+               if (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;
 }
index ac5eacd..db4c253 100644 (file)
@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args {
  * Used by:
  * 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_DEFAULT_SUBVOL  (1ULL << 1)