From 2681e00f00fe3dc0eb5d78dfeb344d18fccaeb6c Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 17 Apr 2015 14:02:15 -0400 Subject: [PATCH] btrfs-progs: check for matching free space in cache 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 Signed-off-by: David Sterba --- ctree.h | 1 + extent-tree.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ free-space-cache.c | 11 ++++++++++ 3 files changed, 63 insertions(+) diff --git a/ctree.h b/ctree.h index 20427718..10dc8388 100644 --- 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; diff --git a/extent-tree.c b/extent-tree.c index e8545ef6..c24af6a0 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -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); diff --git a/free-space-cache.c b/free-space-cache.c index 99ad4200..67f00fd2 100644 --- a/free-space-cache.c +++ b/free-space-cache.c @@ -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;