@@ -3470,6 +3470,108 @@ static int check_extents(struct btrfs_trans_handle *trans,
return ret;
}
+static int check_block_group_used(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_block_group_cache *block_group,
+ struct btrfs_path *path, int repair)
+{
+ struct extent_buffer *leaf;
+ struct btrfs_key key;
+ u64 used = 0;
+ int slot;
+ int err = 0;
+ int ret;
+
+ root = root->fs_info->extent_root;
+ key.objectid = min_t(u64, block_group->key.objectid,
+ BTRFS_SUPER_INFO_OFFSET);
+ key.offset = 0;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ while (1) {
+ leaf = path->nodes[0];
+ slot = path->slots[0];
+
+ if (slot >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0) {
+ err = ret;
+ break;
+ }
+ if (ret) {
+ ret = 0;
+ break;
+ }
+ continue;
+ }
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid < block_group->key.objectid) {
+ path->slots[0]++;
+ continue;
+ }
+
+ if (key.objectid >=
+ block_group->key.objectid + block_group->key.offset)
+ break;
+
+ if (key.type == BTRFS_EXTENT_ITEM_KEY)
+ used += key.offset;
+ path->slots[0]++;
+ }
+ btrfs_release_path(root, path);
+
+ if (!err && btrfs_block_group_used(&block_group->item) != used) {
+ fprintf(stderr, "Block group %llu has a wrong used amount, "
+ "used=%llu, actually used=%llu%s\n",
+ (unsigned long long)block_group->key.objectid,
+ (unsigned long long)
+ btrfs_block_group_used(&block_group->item),
+ (unsigned long long)used, repair ? ", fixing": "");
+ if (repair) {
+ btrfs_set_block_group_used(&block_group->item, used);
+ set_extent_bits(&root->fs_info->block_group_cache,
+ block_group->key.objectid,
+ block_group->key.objectid +
+ block_group->key.offset - 1,
+ EXTENT_DIRTY, GFP_NOFS);
+ }
+ err = 1;
+ }
+
+ return err;
+}
+
+static int check_block_groups_used(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, int repair)
+{
+ struct btrfs_block_group_cache *block_group;
+ struct btrfs_path *path;
+ u64 bytenr = 0;
+ int ret;
+ int err = 0;
+
+ path = btrfs_alloc_path();
+ if (!path)
+ return -ENOMEM;
+
+ path->reada = 2;
+ while ((block_group = btrfs_lookup_first_block_group(root->fs_info,
+ bytenr))) {
+ ret = check_block_group_used(trans, root, block_group, path,
+ repair);
+ if (ret && !err)
+ ret = err;
+ bytenr = block_group->key.objectid + block_group->key.offset;
+ }
+ btrfs_free_path(path);
+
+ return err;
+}
+
static void print_usage(void)
{
fprintf(stderr, "usage: btrfsck dev\n");
@@ -3574,6 +3676,11 @@ int main(int ac, char **av)
if (ret)
fprintf(stderr, "Errors found in extent allocation tree\n");
+ fprintf(stderr, "checking block groups used count\n");
+ ret = check_block_groups_used(trans, root, repair);
+ if (ret)
+ fprintf(stderr, "Errors found in block groups\n");
+
fprintf(stderr, "checking fs roots\n");
ret = check_fs_roots(root, &root_cache);
if (ret)
@@ -2052,6 +2052,9 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
int btrfs_update_block_group(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, u64 num,
int alloc, int mark_free);
+struct btrfs_block_group_cache *btrfs_lookup_first_block_group(struct
+ btrfs_fs_info *info,
+ u64 bytenr);
/* ctree.c */
int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct btrfs_path *path, int level, int slot);
A user reported a problem where all of his block groups had invalid used counts in the block group item. This patch walks the extent tree and counts up the used amount for each block group. If the user specifies repair we can set the correct used value and when the transaction commits we're all set. This was reported and tested by a user and worked. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> --- btrfsck.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ctree.h | 3 ++ 2 files changed, 110 insertions(+), 0 deletions(-)