summaryrefslogtreecommitdiff
path: root/extent-tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'extent-tree.c')
-rw-r--r--extent-tree.c495
1 files changed, 437 insertions, 58 deletions
diff --git a/extent-tree.c b/extent-tree.c
index 5bed3c2..381572d 100644
--- a/extent-tree.c
+++ b/extent-tree.c
@@ -26,6 +26,7 @@
#include "transaction.h"
#include "crc32c.h"
#include "volumes.h"
+#include "free-space-cache.h"
#define BLOCK_GROUP_DATA EXTENT_WRITEBACK
#define BLOCK_GROUP_METADATA EXTENT_UPTODATE
@@ -115,7 +116,8 @@ static int cache_block_group(struct btrfs_root *root,
last = max_t(u64, block_group->key.objectid, BTRFS_SUPER_INFO_OFFSET);
key.objectid = last;
key.offset = 0;
- btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+ key.type = 0;
+
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
if (ret < 0)
goto err;
@@ -142,14 +144,18 @@ static int cache_block_group(struct btrfs_root *root,
break;
}
- if (btrfs_key_type(&key) == BTRFS_EXTENT_ITEM_KEY) {
+ if (key.type == BTRFS_EXTENT_ITEM_KEY ||
+ key.type == BTRFS_METADATA_ITEM_KEY) {
if (key.objectid > last) {
hole_size = key.objectid - last;
set_extent_dirty(free_space_cache, last,
last + hole_size - 1,
GFP_NOFS);
}
- last = key.objectid + key.offset;
+ if (key.type == BTRFS_METADATA_ITEM_KEY)
+ last = key.objectid + root->leafsize;
+ else
+ last = key.objectid + key.offset;
}
next:
path->slots[0]++;
@@ -1024,6 +1030,9 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
int want;
int ret;
int err = 0;
+ int skinny_metadata =
+ btrfs_fs_incompat(root->fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -1034,15 +1043,46 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
extra_size = btrfs_extent_inline_ref_size(want);
else
extra_size = -1;
+
+ if (owner < BTRFS_FIRST_FREE_OBJECTID && skinny_metadata) {
+ skinny_metadata = 1;
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ key.offset = owner;
+ } else if (skinny_metadata) {
+ skinny_metadata = 0;
+ }
+
+again:
ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
if (ret < 0) {
err = ret;
goto out;
}
+
+ /*
+ * We may be a newly converted file system which still has the old fat
+ * extent entries for metadata, so try and see if we have one of those.
+ */
+ if (ret > 0 && skinny_metadata) {
+ skinny_metadata = 0;
+ if (path->slots[0]) {
+ path->slots[0]--;
+ btrfs_item_key_to_cpu(path->nodes[0], &key,
+ path->slots[0]);
+ if (key.objectid == bytenr &&
+ key.type == BTRFS_EXTENT_ITEM_KEY &&
+ key.offset == num_bytes)
+ ret = 0;
+ }
+ if (ret) {
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+ goto again;
+ }
+ }
+
if (ret) {
printf("Failed to find [%llu, %u, %llu]\n", key.objectid, key.type, key.offset);
- btrfs_print_leaf(root, path->nodes[0]);
- btrfs_free_path(path);
return -ENOENT;
}
@@ -1067,8 +1107,9 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
}
#endif
if (item_size < sizeof(*ei)) {
- printf("Size is %u, needs to be %u, slot %d\n", item_size,
- sizeof(*ei), path->slots[0]);
+ printf("Size is %u, needs to be %u, slot %d\n",
+ (unsigned)item_size,
+ (unsigned)sizeof(*ei), path->slots[0]);
btrfs_print_leaf(root, leaf);
return -EINVAL;
}
@@ -1080,11 +1121,13 @@ static int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
ptr = (unsigned long)(ei + 1);
end = (unsigned long)ei + item_size;
- if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+ if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !skinny_metadata) {
ptr += sizeof(struct btrfs_tree_block_info);
BUG_ON(ptr > end);
- } else {
- BUG_ON(!(flags & BTRFS_EXTENT_FLAG_DATA));
+ } else if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) {
+ if (!(flags & BTRFS_EXTENT_FLAG_DATA)) {
+ return -EIO;
+ }
}
err = -ENOENT;
@@ -1438,7 +1481,7 @@ int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr,
- u64 num_bytes, u64 *refs, u64 *flags)
+ u64 offset, int metadata, u64 *refs, u64 *flags)
{
struct btrfs_path *path;
int ret;
@@ -1449,21 +1492,57 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
u64 num_refs;
u64 extent_flags;
- WARN_ON(num_bytes < root->sectorsize);
+ if (metadata &&
+ !btrfs_fs_incompat(root->fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) {
+ offset = root->leafsize;
+ metadata = 0;
+ }
+
path = btrfs_alloc_path();
path->reada = 1;
+
key.objectid = bytenr;
- key.offset = num_bytes;
- btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+ key.offset = offset;
+ if (metadata)
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ else
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+
+again:
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
0, 0);
if (ret < 0)
goto out;
+
+ /*
+ * Deal with the fact that we may have mixed SKINNY and normal refs. If
+ * we didn't find what we wanted check and see if we have a normal ref
+ * right next to us, or re-search if we are on the edge of the leaf just
+ * to make sure.
+ */
+ if (ret > 0 && metadata) {
+ if (path->slots) {
+ path->slots[0]--;
+ btrfs_item_key_to_cpu(path->nodes[0], &key,
+ path->slots[0]);
+ if (key.objectid == bytenr &&
+ key.type == BTRFS_METADATA_ITEM_KEY)
+ ret = 0;
+ }
+
+ if (ret) {
+ btrfs_release_path(root, path);
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = root->leafsize;
+ metadata = 0;
+ goto again;
+ }
+ }
+
if (ret != 0) {
- btrfs_print_leaf(root, path->nodes[0]);
- printk("failed to find block number %Lu\n",
- (unsigned long long)bytenr);
- BUG();
+ ret = -EIO;
+ goto out;
}
l = path->nodes[0];
@@ -1484,9 +1563,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
extent_flags = BTRFS_BLOCK_FLAG_FULL_BACKREF;
#else
BUG();
-#endif
- }
- BUG_ON(num_refs == 0);
+#endif
+ }
item = btrfs_item_ptr(l, path->slots[0], struct btrfs_extent_item);
if (refs)
*refs = num_refs;
@@ -1499,7 +1577,7 @@ out:
int btrfs_set_block_flags(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
- u64 bytenr, u64 num_bytes, u64 flags)
+ u64 bytenr, int level, u64 flags)
{
struct btrfs_path *path;
int ret;
@@ -1507,17 +1585,47 @@ int btrfs_set_block_flags(struct btrfs_trans_handle *trans,
struct extent_buffer *l;
struct btrfs_extent_item *item;
u32 item_size;
+ int skinny_metadata =
+ btrfs_fs_incompat(root->fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
- WARN_ON(num_bytes < root->sectorsize);
path = btrfs_alloc_path();
path->reada = 1;
+
key.objectid = bytenr;
- key.offset = num_bytes;
- btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+ if (skinny_metadata) {
+ key.offset = level;
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ } else {
+ key.offset = root->leafsize;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ }
+
+again:
ret = btrfs_search_slot(trans, root->fs_info->extent_root, &key, path,
0, 0);
if (ret < 0)
goto out;
+
+ if (ret > 0 && skinny_metadata) {
+ skinny_metadata = 0;
+ if (path->slots[0]--) {
+ path->slots[0]--;
+ btrfs_item_key_to_cpu(path->nodes[0], &key,
+ path->slots[0]);
+ if (key.objectid == bytenr &&
+ key.offset == root->leafsize &&
+ key.type == BTRFS_EXTENT_ITEM_KEY)
+ ret = 0;
+ }
+ if (ret) {
+ btrfs_release_path(root, path);
+ key.offset = root->leafsize;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ goto again;
+ }
+ }
+
if (ret != 0) {
btrfs_print_leaf(root, path->nodes[0]);
printk("failed to find block number %Lu\n",
@@ -1705,7 +1813,6 @@ int btrfs_write_dirty_block_groups(struct btrfs_trans_handle *trans,
cache = (struct btrfs_block_group_cache *)(unsigned long)ptr;
ret = write_one_cache_group(trans, root, path, cache);
- BUG_ON(ret);
}
btrfs_free_path(path);
return 0;
@@ -1736,7 +1843,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;
}
@@ -1760,6 +1872,8 @@ static void set_avail_alloc_bits(struct btrfs_fs_info *fs_info, u64 flags)
u64 extra_flags = flags & (BTRFS_BLOCK_GROUP_RAID0 |
BTRFS_BLOCK_GROUP_RAID1 |
BTRFS_BLOCK_GROUP_RAID10 |
+ BTRFS_BLOCK_GROUP_RAID5 |
+ BTRFS_BLOCK_GROUP_RAID6 |
BTRFS_BLOCK_GROUP_DUP);
if (extra_flags) {
if (flags & BTRFS_BLOCK_GROUP_DATA)
@@ -1826,12 +1940,12 @@ static int update_block_group(struct btrfs_trans_handle *trans,
u64 end;
/* block accounting for super block */
- old_val = btrfs_super_bytes_used(&info->super_copy);
+ old_val = btrfs_super_bytes_used(info->super_copy);
if (alloc)
old_val += num_bytes;
else
old_val -= num_bytes;
- btrfs_set_super_bytes_used(&info->super_copy, old_val);
+ btrfs_set_super_bytes_used(info->super_copy, old_val);
/* block accounting for root item */
old_val = btrfs_root_used(&root->root_item);
@@ -1855,6 +1969,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;
@@ -1890,6 +2005,10 @@ static int update_pinned_extents(struct btrfs_root *root,
}
while (num > 0) {
cache = btrfs_lookup_block_group(fs_info, bytenr);
+ if (!cache) {
+ len = min((u64)root->sectorsize, num);
+ goto next;
+ }
WARN_ON(!cache);
len = min(num, cache->key.offset -
(bytenr - cache->key.objectid));
@@ -1902,6 +2021,7 @@ static int update_pinned_extents(struct btrfs_root *root,
cache->space_info->bytes_pinned -= len;
fs_info->total_pinned -= len;
}
+next:
bytenr += len;
num -= len;
}
@@ -1949,6 +2069,21 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
return 0;
}
+static int extent_root_pending_ops(struct btrfs_fs_info *info)
+{
+ u64 start;
+ u64 end;
+ int ret;
+
+ ret = find_first_extent_bit(&info->extent_ins, 0, &start,
+ &end, EXTENT_LOCKED);
+ if (!ret) {
+ ret = find_first_extent_bit(&info->pending_del, 0, &start, &end,
+ EXTENT_LOCKED);
+ }
+ return ret == 0;
+
+}
static int finish_current_insert(struct btrfs_trans_handle *trans,
struct btrfs_root *extent_root)
{
@@ -1960,6 +2095,9 @@ static int finish_current_insert(struct btrfs_trans_handle *trans,
struct pending_extent_op *extent_op;
struct btrfs_key key;
int ret;
+ int skinny_metadata =
+ btrfs_fs_incompat(extent_root->fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
path = btrfs_alloc_path();
@@ -1975,14 +2113,19 @@ static int finish_current_insert(struct btrfs_trans_handle *trans,
if (extent_op->type == PENDING_EXTENT_INSERT) {
key.objectid = start;
- key.offset = end + 1 - start;
- key.type = BTRFS_EXTENT_ITEM_KEY;
+ if (skinny_metadata) {
+ key.offset = extent_op->level;
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ } else {
+ key.offset = extent_op->num_bytes;
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ }
ret = alloc_reserved_tree_block(trans, extent_root,
extent_root->root_key.objectid,
trans->transid,
extent_op->flags,
&extent_op->key,
- extent_op->level, &key);
+ extent_op->level, &key);
} else {
BUG_ON(1);
}
@@ -2033,6 +2176,12 @@ pinit:
return 0;
}
+void btrfs_pin_extent(struct btrfs_fs_info *fs_info,
+ u64 bytenr, u64 num_bytes)
+{
+ update_pinned_extents(fs_info->extent_root, bytenr, num_bytes, 1);
+}
+
/*
* remove an extent from the root, returns 0 on success
*/
@@ -2057,7 +2206,16 @@ static int __free_extent(struct btrfs_trans_handle *trans,
int num_to_del = 1;
u32 item_size;
u64 refs;
+ int skinny_metadata =
+ btrfs_fs_incompat(extent_root->fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+ if (root->fs_info->free_extent_hook) {
+ root->fs_info->free_extent_hook(trans, root, bytenr, num_bytes,
+ parent, root_objectid, owner_objectid,
+ owner_offset, refs_to_drop);
+
+ }
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
@@ -2066,6 +2224,8 @@ static int __free_extent(struct btrfs_trans_handle *trans,
path->leave_spinning = 1;
is_data = owner_objectid >= BTRFS_FIRST_FREE_OBJECTID;
+ if (is_data)
+ skinny_metadata = 0;
BUG_ON(!is_data && refs_to_drop != 1);
ret = lookup_extent_backref(trans, extent_root, path, &iref,
@@ -2084,6 +2244,11 @@ static int __free_extent(struct btrfs_trans_handle *trans,
found_extent = 1;
break;
}
+ if (key.type == BTRFS_METADATA_ITEM_KEY &&
+ key.offset == owner_objectid) {
+ found_extent = 1;
+ break;
+ }
if (path->slots[0] - extent_slot > 5)
break;
extent_slot--;
@@ -2103,11 +2268,37 @@ static int __free_extent(struct btrfs_trans_handle *trans,
path->leave_spinning = 1;
key.objectid = bytenr;
- key.type = BTRFS_EXTENT_ITEM_KEY;
- key.offset = num_bytes;
+
+ if (skinny_metadata) {
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ key.offset = owner_objectid;
+ } else {
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+ }
ret = btrfs_search_slot(trans, extent_root,
&key, path, -1, 1);
+ if (ret > 0 && skinny_metadata && path->slots[0]) {
+ path->slots[0]--;
+ btrfs_item_key_to_cpu(path->nodes[0],
+ &key,
+ path->slots[0]);
+ if (key.objectid == bytenr &&
+ key.type == BTRFS_EXTENT_ITEM_KEY &&
+ key.offset == num_bytes)
+ ret = 0;
+ }
+
+ if (ret > 0 && skinny_metadata) {
+ skinny_metadata = 0;
+ btrfs_release_path(extent_root, path);
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = num_bytes;
+ ret = btrfs_search_slot(trans, extent_root,
+ &key, path, -1, 1);
+ }
+
if (ret) {
printk(KERN_ERR "umm, got %d back from search"
", was looking for %llu\n", ret,
@@ -2118,8 +2309,6 @@ static int __free_extent(struct btrfs_trans_handle *trans,
extent_slot = path->slots[0];
}
} else {
- btrfs_print_leaf(extent_root, path->nodes[0]);
- WARN_ON(1);
printk(KERN_ERR "btrfs unable to find ref byte nr %llu "
"parent %llu root %llu owner %llu offset %llu\n",
(unsigned long long)bytenr,
@@ -2127,6 +2316,8 @@ static int __free_extent(struct btrfs_trans_handle *trans,
(unsigned long long)root_objectid,
(unsigned long long)owner_objectid,
(unsigned long long)owner_offset);
+ ret = -EIO;
+ goto fail;
}
leaf = path->nodes[0];
@@ -2162,7 +2353,8 @@ static int __free_extent(struct btrfs_trans_handle *trans,
BUG_ON(item_size < sizeof(*ei));
ei = btrfs_item_ptr(leaf, extent_slot,
struct btrfs_extent_item);
- if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID) {
+ if (owner_objectid < BTRFS_FIRST_FREE_OBJECTID &&
+ key.type == BTRFS_EXTENT_ITEM_KEY) {
struct btrfs_tree_block_info *bi;
BUG_ON(item_size < sizeof(*ei) + sizeof(*bi));
bi = (struct btrfs_tree_block_info *)(ei + 1);
@@ -2232,10 +2424,9 @@ static int __free_extent(struct btrfs_trans_handle *trans,
BUG_ON(ret);
}
- ret = update_block_group(trans, root, bytenr, num_bytes, 0,
- mark_free);
- BUG_ON(ret);
+ update_block_group(trans, root, bytenr, num_bytes, 0, mark_free);
}
+fail:
btrfs_free_path(path);
finish_current_insert(trans, extent_root);
return ret;
@@ -2374,6 +2565,8 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
WARN_ON(num_bytes < root->sectorsize);
btrfs_set_key_type(ins, BTRFS_EXTENT_ITEM_KEY);
+ search_start = stripe_align(root, search_start);
+
if (hint_byte) {
block_group = btrfs_lookup_first_block_group(info, hint_byte);
if (!block_group)
@@ -2389,6 +2582,7 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
total_needed += empty_size;
check_failed:
+ search_start = stripe_align(root, search_start);
if (!block_group) {
block_group = btrfs_lookup_first_block_group(info,
search_start);
@@ -2401,7 +2595,6 @@ check_failed:
if (ret)
goto error;
- search_start = stripe_align(root, search_start);
ins->objectid = search_start;
ins->offset = num_bytes;
@@ -2533,7 +2726,13 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
struct btrfs_extent_inline_ref *iref;
struct btrfs_path *path;
struct extent_buffer *leaf;
- u32 size = sizeof(*extent_item) + sizeof(*block_info) + sizeof(*iref);
+ u32 size = sizeof(*extent_item) + sizeof(*iref);
+ int skinny_metadata =
+ btrfs_fs_incompat(fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA);
+
+ if (!skinny_metadata)
+ size += sizeof(*block_info);
path = btrfs_alloc_path();
BUG_ON(!path);
@@ -2550,27 +2749,25 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
btrfs_set_extent_generation(leaf, extent_item, generation);
btrfs_set_extent_flags(leaf, extent_item,
flags | BTRFS_EXTENT_FLAG_TREE_BLOCK);
- block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
- btrfs_set_tree_block_key(leaf, block_info, key);
- btrfs_set_tree_block_level(leaf, block_info, level);
+ if (skinny_metadata) {
+ iref = (struct btrfs_extent_inline_ref *)(extent_item + 1);
+ } else {
+ block_info = (struct btrfs_tree_block_info *)(extent_item + 1);
+ btrfs_set_tree_block_key(leaf, block_info, key);
+ btrfs_set_tree_block_level(leaf, block_info, level);
+ iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
+ }
- iref = (struct btrfs_extent_inline_ref *)(block_info + 1);
btrfs_set_extent_inline_ref_type(leaf, iref, BTRFS_TREE_BLOCK_REF_KEY);
btrfs_set_extent_inline_ref_offset(leaf, iref, root_objectid);
btrfs_mark_buffer_dirty(leaf);
btrfs_free_path(path);
- ret = update_block_group(trans, root, ins->objectid, ins->offset,
+ ret = update_block_group(trans, root, ins->objectid, root->leafsize,
1, 0);
- if (ret) {
- printk(KERN_ERR "btrfs update block group failed for %llu "
- "%llu\n", (unsigned long long)ins->objectid,
- (unsigned long long)ins->offset);
- BUG();
- }
- return ret;
+ return 0;
}
static int alloc_tree_block(struct btrfs_trans_handle *trans,
@@ -2604,6 +2801,11 @@ static int alloc_tree_block(struct btrfs_trans_handle *trans,
set_state_private(&root->fs_info->extent_ins,
ins->objectid, (unsigned long)extent_op);
} else {
+ if (btrfs_fs_incompat(root->fs_info,
+ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)) {
+ ins->offset = level;
+ ins->type = BTRFS_METADATA_ITEM_KEY;
+ }
ret = alloc_reserved_tree_block(trans, root, root_objectid,
generation, flags,
key, level, ins);
@@ -2971,18 +3173,27 @@ out:
int btrfs_free_block_groups(struct btrfs_fs_info *info)
{
+ struct btrfs_space_info *sinfo;
+ struct btrfs_block_group_cache *cache;
u64 start;
u64 end;
u64 ptr;
int ret;
+
while(1) {
ret = find_first_extent_bit(&info->block_group_cache, 0,
&start, &end, (unsigned int)-1);
if (ret)
break;
ret = get_state_private(&info->block_group_cache, start, &ptr);
- if (!ret)
- kfree((void *)(unsigned long)ptr);
+ if (!ret) {
+ cache = (struct btrfs_block_group_cache *)ptr;
+ if (cache->free_space_ctl) {
+ btrfs_remove_free_space_cache(cache);
+ kfree(cache->free_space_ctl);
+ }
+ kfree(cache);
+ }
clear_extent_bits(&info->block_group_cache, start,
end, (unsigned int)-1, GFP_NOFS);
}
@@ -2994,6 +3205,13 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
clear_extent_dirty(&info->free_space_cache, start,
end, GFP_NOFS);
}
+
+ while (!list_empty(&info->space_info)) {
+ sinfo = list_entry(info->space_info.next,
+ struct btrfs_space_info, list);
+ list_del_init(&sinfo->list);
+ kfree(sinfo);
+ }
return 0;
}
@@ -3153,7 +3371,6 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
finish_current_insert(trans, extent_root);
ret = del_pending_extents(trans, extent_root);
- BUG_ON(ret);
set_avail_alloc_bits(extent_root->fs_info, type);
return 0;
}
@@ -3185,7 +3402,7 @@ int btrfs_make_block_groups(struct btrfs_trans_handle *trans,
extent_root = root->fs_info->extent_root;
block_group_cache = &root->fs_info->block_group_cache;
chunk_objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
- total_bytes = btrfs_super_total_bytes(&root->fs_info->super_copy);
+ total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy);
group_align = 64 * root->sectorsize;
cur_start = 0;
@@ -3269,3 +3486,165 @@ 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;
+ if (key.type == BTRFS_METADATA_ITEM_KEY)
+ bytes_used += root->leafsize;
+ 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;
+
+ root = root->fs_info->extent_root;
+
+ while(extent_root_pending_ops(fs_info)) {
+ ret = finish_current_insert(trans, root);
+ if (ret)
+ return ret;
+ ret = del_pending_extents(trans, root);
+ if (ret)
+ return ret;
+ }
+
+ while(1) {
+ cache = btrfs_lookup_first_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);
+ }
+
+ 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);
+ } else if (key.type == BTRFS_METADATA_ITEM_KEY) {
+ bytes_used += root->leafsize;
+ ret = btrfs_update_block_group(trans, root,
+ key.objectid, root->leafsize, 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;
+}