From: Chris Mason Date: Thu, 9 Feb 2012 02:29:13 +0000 (-0500) Subject: btrfsck: fix block group accounting during repair X-Git-Tag: upstream/0.20.rc1~50 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5fd54bc94b6cdf78706b0f078b4abee2cbae8651;p=platform%2Fupstream%2Fbtrfs-progs.git btrfsck: fix block group accounting during repair Signed-off-by: Chris Mason --- diff --git a/btrfsck.c b/btrfsck.c index ab66b12..a5dbbee 100644 --- 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) { diff --git a/convert.c b/convert.c index 291dc27..8f572ef 100644 --- 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 --- 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); diff --git a/extent-tree.c b/extent-tree.c index 01dfa3f..544ab2f 100644 --- a/extent-tree.c +++ b/extent-tree.c @@ -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; +}