btrfsck: fix block group accounting during repair
authorChris Mason <chris.mason@oracle.com>
Thu, 9 Feb 2012 02:29:13 +0000 (21:29 -0500)
committerChris Mason <chris.mason@oracle.com>
Thu, 9 Feb 2012 02:29:13 +0000 (21:29 -0500)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
btrfsck.c
convert.c
ctree.h
extent-tree.c

index ab66b12..a5dbbee 100644 (file)
--- a/btrfsck.c
+++ b/btrfsck.c
@@ -2725,7 +2725,7 @@ static int add_root_to_pending(struct extent_buffer *buf,
 static int delete_extent_records(struct btrfs_trans_handle *trans,
                                 struct btrfs_root *root,
                                 struct btrfs_path *path,
-                                u64 bytenr)
+                                u64 bytenr, u64 new_len)
 {
        struct btrfs_key key;
        struct btrfs_key found_key;
@@ -2787,7 +2787,7 @@ static int delete_extent_records(struct btrfs_trans_handle *trans,
 
                if (found_key.type == BTRFS_EXTENT_ITEM_KEY) {
                        ret = btrfs_update_block_group(trans, root, bytenr,
-                                                      found_key.offset, 0, 1);
+                                                      found_key.offset, 0, 0);
                        if (ret)
                                break;
                }
@@ -2959,7 +2959,7 @@ static int fixup_extent_refs(struct btrfs_trans_handle *trans,
 
        /* step one, delete all the existing records */
        ret = delete_extent_records(trans, info->extent_root, path,
-                                   rec->start);
+                                   rec->start, rec->max_size);
 
        if (ret < 0)
                goto out;
@@ -2987,19 +2987,16 @@ out:
        return ret;
 }
 
-static int check_extent_refs(struct btrfs_root *root,
-                     struct cache_tree *extent_cache, int repair)
+static int check_extent_refs(struct btrfs_trans_handle *trans,
+                            struct btrfs_root *root,
+                            struct cache_tree *extent_cache, int repair)
 {
        struct extent_record *rec;
        struct cache_extent *cache;
-       struct btrfs_trans_handle *trans = NULL;
        int err = 0;
        int ret = 0;
        int fixed = 0;
 
-       if (repair)
-               trans = btrfs_start_transaction(root, 1);
-
        if (repair) {
                /*
                 * if we're doing a repair, we have to make sure
@@ -3074,12 +3071,12 @@ repair_abort:
                        fprintf(stderr, "failed to repair damaged filesystem, aborting\n");
                        exit(1);
                }
-               btrfs_commit_transaction(trans, root);
        }
        return err;
 }
 
-static int check_extents(struct btrfs_root *root, int repair)
+static int check_extents(struct btrfs_trans_handle *trans,
+                        struct btrfs_root *root, int repair)
 {
        struct cache_tree extent_cache;
        struct cache_tree seen;
@@ -3160,7 +3157,7 @@ static int check_extents(struct btrfs_root *root, int repair)
                if (ret != 0)
                        break;
        }
-       ret = check_extent_refs(root, &extent_cache, repair);
+       ret = check_extent_refs(trans, root, &extent_cache, repair);
        return ret;
 }
 
@@ -3182,6 +3179,7 @@ int main(int ac, char **av)
        struct cache_tree root_cache;
        struct btrfs_root *root;
        struct btrfs_fs_info *info;
+       struct btrfs_trans_handle *trans = NULL;
        u64 bytenr = 0;
        int ret;
        int num;
@@ -3242,9 +3240,16 @@ int main(int ac, char **av)
        root = info->fs_root;
 
        fprintf(stderr, "checking extents\n");
-       ret = check_extents(root, repair);
+       if (repair)
+               trans = btrfs_start_transaction(root, 1);
+
+       ret = check_extents(trans, root, repair);
        if (ret)
                goto out;
+
+       if (repair)
+               btrfs_fix_block_accounting(trans, root);
+
        fprintf(stderr, "checking fs roots\n");
        ret = check_fs_roots(root, &root_cache);
        if (ret)
@@ -3254,6 +3259,11 @@ int main(int ac, char **av)
        ret = check_root_refs(root, &root_cache);
 out:
        free_root_recs(&root_cache);
+       if (repair) {
+               ret = btrfs_commit_transaction(trans, root);
+               if (ret)
+                       exit(1);
+       }
        close_ctree(root);
 
        if (found_old_backref) {
index 291dc27..8f572ef 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1504,66 +1504,6 @@ fail:
        return new_root;
 }
 
-/*
- * Fixup block accounting. The initial block accounting created by
- * make_block_groups isn't accuracy in this case.
- */
-static int fixup_block_accounting(struct btrfs_trans_handle *trans,
-                                 struct btrfs_root *root)
-{
-       int ret;
-       int slot;
-       u64 start = 0;
-       u64 bytes_used = 0;
-       struct btrfs_path path;
-       struct btrfs_key key;
-       struct extent_buffer *leaf;
-       struct btrfs_block_group_cache *cache;
-       struct btrfs_fs_info *fs_info = root->fs_info;
-
-       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;
-       }
-
-       btrfs_init_path(&path);
-       key.offset = 0;
-       key.objectid = 0;
-       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
-       ret = btrfs_search_slot(trans, root->fs_info->extent_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)
-                               return ret;
-                       if (ret > 0)
-                               break;
-                       leaf = path.nodes[0];
-                       slot = path.slots[0];
-               }
-               btrfs_item_key_to_cpu(leaf, &key, slot);
-               if (key.type == BTRFS_EXTENT_ITEM_KEY) {
-                       bytes_used += key.offset;
-                       ret = btrfs_update_block_group(trans, root,
-                                 key.objectid, key.offset, 1, 0);
-                       BUG_ON(ret);
-               }
-               path.slots[0]++;
-       }
-       btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used);
-       btrfs_release_path(root, &path);
-       return 0;
-}
-
 static int create_chunk_mapping(struct btrfs_trans_handle *trans,
                                struct btrfs_root *root)
 {
@@ -1735,7 +1675,7 @@ static int init_btrfs(struct btrfs_root *root)
        ret = btrfs_make_block_groups(trans, root);
        if (ret)
                goto err;
-       ret = fixup_block_accounting(trans, root);
+       ret = btrfs_fixup_block_accounting(trans, root);
        if (ret)
                goto err;
        ret = create_chunk_mapping(trans, root);
diff --git a/ctree.h b/ctree.h
index 5e770d2..6f12869 100644 (file)
--- a/ctree.h
+++ b/ctree.h
@@ -1785,6 +1785,9 @@ static inline u32 btrfs_level_size(struct btrfs_root *root, int level) {
        btrfs_item_offset_nr(leaf, slot)))
 
 /* extent-tree.c */
+int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root);
+int btrfs_check_block_accounting(struct btrfs_root *root);
 void btrfs_pin_extent(struct btrfs_fs_info *fs_info, u64 bytenr, u64 num_bytes);
 int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
                         struct btrfs_root *root);
index 01dfa3f..544ab2f 100644 (file)
@@ -1734,7 +1734,12 @@ static int update_space_info(struct btrfs_fs_info *info, u64 flags,
        if (found) {
                found->total_bytes += total_bytes;
                found->bytes_used += bytes_used;
-               WARN_ON(found->total_bytes < found->bytes_used);
+               if (found->total_bytes < found->bytes_used) {
+                       fprintf(stderr, "warning, bad space info total_bytes "
+                               "%llu used %llu\n",
+                              (unsigned long long)found->total_bytes,
+                              (unsigned long long)found->bytes_used);
+               }
                *space_info = found;
                return 0;
        }
@@ -1853,6 +1858,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
 
                old_val = btrfs_block_group_used(&cache->item);
                num_bytes = min(total, cache->key.offset - byte_in_group);
+
                if (alloc) {
                        old_val += num_bytes;
                        cache->space_info->bytes_used += num_bytes;
@@ -3274,3 +3280,143 @@ int btrfs_update_block_group(struct btrfs_trans_handle *trans,
        return update_block_group(trans, root, bytenr, num_bytes,
                                  alloc, mark_free);
 }
+
+static int btrfs_count_extents_in_block_group(struct btrfs_root *root,
+                                             struct btrfs_path *path, u64 start,
+                                             u64 len,
+                                             u64 *total)
+{
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       u64 bytes_used = 0;
+       int ret;
+       int slot;
+
+       key.offset = 0;
+       key.objectid = start;
+       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+       ret = btrfs_search_slot(NULL, root->fs_info->extent_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)
+                               return ret;
+                       if (ret > 0)
+                               break;
+                       leaf = path->nodes[0];
+                       slot = path->slots[0];
+               }
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+               if (key.objectid > start + len)
+                       break;
+               if (key.type == BTRFS_EXTENT_ITEM_KEY)
+                       bytes_used += key.offset;
+               path->slots[0]++;
+       }
+       *total = bytes_used;
+       btrfs_release_path(root, path);
+       return 0;
+}
+
+int btrfs_check_block_accounting(struct btrfs_root *root)
+{
+       int ret;
+       u64 start = 0;
+       u64 bytes_used = 0;
+       struct btrfs_path path;
+       struct btrfs_block_group_cache *cache;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+
+       btrfs_init_path(&path);
+
+       while(1) {
+               cache = btrfs_lookup_block_group(fs_info, start);
+               if (!cache)
+                       break;
+
+               ret = btrfs_count_extents_in_block_group(root, &path,
+                                                        cache->key.objectid,
+                                                        cache->key.offset,
+                                                        &bytes_used);
+
+               if (ret == 0) {
+                       u64 on_disk = btrfs_block_group_used(&cache->item);
+                       if (on_disk != bytes_used) {
+                               fprintf(stderr, "bad block group accounting found %llu "
+                                       "expected %llu block group %llu\n",
+                                       (unsigned long long)bytes_used,
+                                       (unsigned long long)on_disk,
+                                       (unsigned long long)cache->key.objectid);
+                       }
+               }
+               start = cache->key.objectid + cache->key.offset;
+
+               cache->space_info->bytes_used = 0;
+       }
+       return 0;
+}
+
+/*
+ * Fixup block accounting. The initial block accounting created by
+ * make_block_groups isn't accuracy in this case.
+ */
+int btrfs_fix_block_accounting(struct btrfs_trans_handle *trans,
+                              struct btrfs_root *root)
+{
+       int ret;
+       int slot;
+       u64 start = 0;
+       u64 bytes_used = 0;
+       struct btrfs_path path;
+       struct btrfs_key key;
+       struct extent_buffer *leaf;
+       struct btrfs_block_group_cache *cache;
+       struct btrfs_fs_info *fs_info = root->fs_info;
+
+       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;
+       }
+
+       btrfs_init_path(&path);
+       key.offset = 0;
+       key.objectid = 0;
+       btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+       ret = btrfs_search_slot(trans, root->fs_info->extent_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)
+                               return ret;
+                       if (ret > 0)
+                               break;
+                       leaf = path.nodes[0];
+                       slot = path.slots[0];
+               }
+               btrfs_item_key_to_cpu(leaf, &key, slot);
+               if (key.type == BTRFS_EXTENT_ITEM_KEY) {
+                       bytes_used += key.offset;
+                       ret = btrfs_update_block_group(trans, root,
+                                 key.objectid, key.offset, 1, 0);
+                       BUG_ON(ret);
+               }
+               path.slots[0]++;
+       }
+       btrfs_set_super_bytes_used(&root->fs_info->super_copy, bytes_used);
+       btrfs_release_path(root, &path);
+       return 0;
+}