btrfs-progs: check for matching free space in cache
authorJosef Bacik <jbacik@fb.com>
Fri, 17 Apr 2015 18:02:15 +0000 (14:02 -0400)
committerDavid Sterba <dsterba@suse.cz>
Fri, 24 Apr 2015 13:42:04 +0000 (15:42 +0200)
We have this check in the kernel but not in userspace, which makes fsck
fail when we wouldn't have a problem in the kernel.  This was meant to
catch this case because it really isn't good, unfortunately it will
require a design change to fix in the kernel so in the meantime add this
check so we can be sure our tests only catch real problems.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
ctree.h
extent-tree.c
free-space-cache.c

diff --git a/ctree.h b/ctree.h
index 2042771..10dc838 100644 (file)
--- a/ctree.h
+++ b/ctree.h
@@ -936,6 +936,7 @@ struct btrfs_block_group_cache {
        struct btrfs_block_group_item item;
        struct btrfs_space_info *space_info;
        struct btrfs_free_space_ctl *free_space_ctl;
+       u64 bytes_super;
        u64 pinned;
        u64 flags;
        int cached;
index e8545ef..c24af6a 100644 (file)
@@ -3140,6 +3140,54 @@ error:
        return ret;
 }
 
+static void account_super_bytes(struct btrfs_fs_info *fs_info,
+                               struct btrfs_block_group_cache *cache)
+{
+       u64 bytenr;
+       u64 *logical;
+       int stripe_len;
+       int i, nr, ret;
+
+       if (cache->key.objectid < BTRFS_SUPER_INFO_OFFSET) {
+               stripe_len = BTRFS_SUPER_INFO_OFFSET - cache->key.objectid;
+               cache->bytes_super += stripe_len;
+       }
+
+       for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+               bytenr = btrfs_sb_offset(i);
+               ret = btrfs_rmap_block(&fs_info->mapping_tree,
+                                      cache->key.objectid, bytenr,
+                                      0, &logical, &nr, &stripe_len);
+               if (ret)
+                       return;
+
+               while (nr--) {
+                       u64 start, len;
+
+                       if (logical[nr] > cache->key.objectid +
+                           cache->key.offset)
+                               continue;
+
+                       if (logical[nr] + stripe_len <= cache->key.objectid)
+                               continue;
+
+                       start = logical[nr];
+                       if (start < cache->key.objectid) {
+                               start = cache->key.objectid;
+                               len = (logical[nr] + stripe_len) - start;
+                       } else {
+                               len = min_t(u64, stripe_len,
+                                           cache->key.objectid +
+                                           cache->key.offset - start);
+                       }
+
+                       cache->bytes_super += len;
+               }
+
+               kfree(logical);
+       }
+}
+
 int btrfs_read_block_groups(struct btrfs_root *root)
 {
        struct btrfs_path *path;
@@ -3201,6 +3249,8 @@ int btrfs_read_block_groups(struct btrfs_root *root)
                if (btrfs_chunk_readonly(root, cache->key.objectid))
                        cache->ro = 1;
 
+               account_super_bytes(info, cache);
+
                ret = update_space_info(info, cache->flags, found_key.offset,
                                        btrfs_block_group_used(&cache->item),
                                        &space_info);
@@ -3242,6 +3292,7 @@ btrfs_add_block_group(struct btrfs_fs_info *fs_info, u64 bytes_used, u64 type,
        cache->flags = type;
        btrfs_set_block_group_flags(&cache->item, type);
 
+       account_super_bytes(fs_info, cache);
        ret = update_space_info(fs_info, cache->flags, size, bytes_used,
                                &cache->space_info);
        BUG_ON(ret);
index 99ad420..67f00fd 100644 (file)
@@ -428,7 +428,9 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_free_space_ctl *ctl = block_group->free_space_ctl;
        struct btrfs_path *path;
+       u64 used = btrfs_block_group_used(&block_group->item);
        int ret = 0;
+       int matched;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -438,6 +440,15 @@ int load_free_space_cache(struct btrfs_fs_info *fs_info,
                                      block_group->key.objectid);
        btrfs_free_path(path);
 
+       matched = (ctl->free_space == (block_group->key.offset - used -
+                                      block_group->bytes_super));
+       if (ret == 1 && !matched) {
+               __btrfs_remove_free_space_cache(ctl);
+               printf("block group %llu has wrong amount of free space",
+                      block_group->key.objectid);
+               ret = -1;
+       }
+
        if (ret < 0) {
                ret = 0;