summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--btrfsck.c36
-rw-r--r--convert.c62
-rw-r--r--ctree.h3
-rw-r--r--extent-tree.c148
4 files changed, 174 insertions, 75 deletions
diff --git a/btrfsck.c b/btrfsck.c
index ab66b122..a5dbbee1 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 291dc27d..8f572eff 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 5e770d28..6f128696 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 01dfa3f6..544ab2ff 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;
+}