summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Mason2008-09-24 17:23:25 +0200
committerChris Mason2008-09-25 21:41:59 +0200
commit9b49c9b9f93e148815f2544d0c91f43b6d72eea9 (patch)
tree2cd29ee73e641a5069128d87a668d4fff3626502
parentAdd Btrfs to fs/Kconfig and fs/Makefile (diff)
downloadkernel-qcow2-linux-9b49c9b9f93e148815f2544d0c91f43b6d72eea9.tar.gz
kernel-qcow2-linux-9b49c9b9f93e148815f2544d0c91f43b6d72eea9.tar.xz
kernel-qcow2-linux-9b49c9b9f93e148815f2544d0c91f43b6d72eea9.zip
Btrfs: Fix allocation completions in tree log replay
After a crash, the tree log code uses btrfs_alloc_logged_extent to record allocations of data extents that it finds in the log tree. These come in basically random order, which does not fit how btrfs_remove_free_space() expects to be called. btrfs_remove_free_space was changed to support recording an extent allocation in the middle of a region of free space. Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/free-space-cache.c34
1 files changed, 34 insertions, 0 deletions
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 01c26e8ae555..f1d9b6bc23ba 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -306,11 +306,45 @@ int btrfs_remove_free_space(struct btrfs_block_group_cache *block_group,
ret = link_free_space(block_group, info);
BUG_ON(ret);
+ } else if (info && info->offset < offset &&
+ info->offset + info->bytes >= offset + bytes) {
+ u64 old_start = info->offset;
+ /*
+ * we're freeing space in the middle of the info,
+ * this can happen during tree log replay
+ *
+ * first unlink the old info and then
+ * insert it again after the hole we're creating
+ */
+ unlink_free_space(block_group, info);
+ if (offset + bytes < info->offset + info->bytes) {
+ u64 old_end = info->offset + info->bytes;
+
+ info->offset = offset + bytes;
+ info->bytes = old_end - info->offset;
+ ret = link_free_space(block_group, info);
+ BUG_ON(ret);
+ } else {
+ /* the hole we're creating ends at the end
+ * of the info struct, just free the info
+ */
+ kfree(info);
+ }
+
+ /* step two, insert a new info struct to cover anything
+ * before the hole
+ */
+ spin_unlock(&block_group->lock);
+ ret = btrfs_add_free_space(block_group, old_start,
+ offset - old_start);
+ BUG_ON(ret);
+ goto out_nolock;
} else {
WARN_ON(1);
}
out:
spin_unlock(&block_group->lock);
+out_nolock:
return ret;
}