diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-09-24 11:23:25 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 15:41:59 -0400 |
commit | 9b49c9b9f93e148815f2544d0c91f43b6d72eea9 (patch) | |
tree | 2cd29ee73e641a5069128d87a668d4fff3626502 | |
parent | 60582d1e933649f14f46f816bb75049058679e01 (diff) |
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.c | 34 |
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; } |