diff mbox

Btrfs-progs: check block group used count and fix if specified

Message ID 1351773294-1458-1-git-send-email-jbacik@fusionio.com (mailing list archive)
State New, archived
Headers show

Commit Message

Josef Bacik Nov. 1, 2012, 12:34 p.m. UTC
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(-)

Comments

Chris Mason Nov. 1, 2012, 1:37 p.m. UTC | #1
On Thu, Nov 01, 2012 at 06:34:54AM -0600, Josef Bacik wrote:
> 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,

Josef and I hashed this out a little bit on irc.  My fsck repair code
already tries to fix the block group accounting, but I think there is a
key part his code does differently (correctly ;):

> +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;
> +}

My code reuses btrfs_fix_block_group_acounting, which does this:

	start = 0;
        while(1) {
                cache = btrfs_lookup_block_group(fs_info, start);
                if (!cache)
                        break; 
                start = cache->key.objectid + cache->key.offset;
                btrfs_set_block_group_used(&cache->item, 0);
                cache->space_info->bytes_used = 0;
                set_extent_bits(&root->fs_info->block_group_cache,
                                cache->key.objectid,
                                cache->key.objectid + cache->key.offset -1,
                                BLOCK_GROUP_DIRTY, GFP_NOFS);
        }

Using btrfs_lookup_first_block_group here should fix things.  It must be
breaking out too soon and so the accounting isn't updated properly.

-chris
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/btrfsck.c b/btrfsck.c
index 67f4a9d..a5f995d 100644
--- a/btrfsck.c
+++ b/btrfsck.c
@@ -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)
diff --git a/ctree.h b/ctree.h
index 293b24f..b125ede 100644
--- a/ctree.h
+++ b/ctree.h
@@ -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);