Re: [PATCH] Btrfs: dynamically remove unused block groups

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Nov 30, 2010 at 08:46, Josef Bacik <josef@xxxxxxxxxx> wrote:
> Btrfs only allocates chunks as we need them, however we do not delete chunks as
> we stop using them. ÂThis patch adds this capability. ÂWhenever we clear the
> last bit of used space in a block group we try and mark it read only, and then
> when the last pinned space is finally removed we queue up the deletion work.
> I've tested this with xfstests and my enospc tests. ÂWhen filling up the disk
> I see that we've allocated the entire disk of chunks, and then when I do rm *
> there is a bunch of space freed up. ÂThanks,

Stupid user question:

I have a btrfs filesystem on a 2.6.36 kernel that used to have ~800GB
of data on it.  Then I deleted ~500GB of it (moved it elsewhere), but
my space usage as reported by df and the btrfs tool didn't decrease
appreciably.  Might this be why?

Thanks,
Josh



> Signed-off-by: Josef Bacik <josef@xxxxxxxxxx>
> ---
> Âfs/btrfs/ctree.h    |  Â3 +
> Âfs/btrfs/extent-tree.c | Â148 ++++++++++++++++++++++++++++++++++++++++++-----
> Âfs/btrfs/volumes.c   |  52 +++++++++++------
> Âfs/btrfs/volumes.h   |  Â4 +
> Â4 files changed, 174 insertions(+), 33 deletions(-)
>
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 8db9234..50ec64b 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -839,6 +839,9 @@ struct btrfs_block_group_cache {
> Â Â Â Â * Today it will only have one thing on it, but that may change
> Â Â Â Â */
> Â Â Â Âstruct list_head cluster_list;
> +
> + Â Â Â /* Worker for deleting the block group if its empty */
> + Â Â Â struct btrfs_work work;
> Â};
>
> Âstruct reloc_control;
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 43aa62a..87aae66 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -64,6 +64,11 @@ static int find_next_key(struct btrfs_path *path, int level,
> Â Â Â Â Â Â Â Â Â Â Â Â struct btrfs_key *key);
> Âstatic void dump_space_info(struct btrfs_space_info *info, u64 bytes,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Âint dump_block_groups);
> +static int btrfs_set_block_group_ro_trans(struct btrfs_trans_handle *trans,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct btrfs_root *root,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct btrfs_block_group_cache
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â *cache);
> +static int set_block_group_ro_lock(struct btrfs_block_group_cache *cache);
>
> Âstatic noinline int
> Âblock_group_cache_done(struct btrfs_block_group_cache *cache)
> @@ -4052,6 +4057,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
> Â Â Â Âu64 old_val;
> Â Â Â Âu64 byte_in_group;
> Â Â Â Âint factor;
> + Â Â Â int empty = 0;
>
> Â Â Â Â/* block accounting for super block */
> Â Â Â Âspin_lock(&info->delalloc_lock);
> @@ -4064,6 +4070,7 @@ static int update_block_group(struct btrfs_trans_handle *trans,
> Â Â Â Âspin_unlock(&info->delalloc_lock);
>
> Â Â Â Âwhile (total) {
> + Â Â Â Â Â Â Â empty = 0;
> Â Â Â Â Â Â Â Âcache = btrfs_lookup_block_group(info, bytenr);
> Â Â Â Â Â Â Â Âif (!cache)
> Â Â Â Â Â Â Â Â Â Â Â Âreturn -1;
> @@ -4096,6 +4103,12 @@ 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) {
> + Â Â Â Â Â Â Â Â Â Â Â /*
> + Â Â Â Â Â Â Â Â Â Â Â Â* We raced with setting the block group read only, we
> + Â Â Â Â Â Â Â Â Â Â Â Â* need to change it back to rw
> + Â Â Â Â Â Â Â Â Â Â Â Â*/
> + Â Â Â Â Â Â Â Â Â Â Â if (cache->ro)
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â empty = -1;
> Â Â Â Â Â Â Â Â Â Â Â Âold_val += num_bytes;
> Â Â Â Â Â Â Â Â Â Â Â Âbtrfs_set_block_group_used(&cache->item, old_val);
> Â Â Â Â Â Â Â Â Â Â Â Âcache->reserved -= num_bytes;
> @@ -4106,6 +4119,8 @@ static int update_block_group(struct btrfs_trans_handle *trans,
> Â Â Â Â Â Â Â Â Â Â Â Âspin_unlock(&cache->space_info->lock);
> Â Â Â Â Â Â Â Â} else {
> Â Â Â Â Â Â Â Â Â Â Â Âold_val -= num_bytes;
> + Â Â Â Â Â Â Â Â Â Â Â if (old_val == 0)
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â empty = 1;
> Â Â Â Â Â Â Â Â Â Â Â Âbtrfs_set_block_group_used(&cache->item, old_val);
> Â Â Â Â Â Â Â Â Â Â Â Âcache->pinned += num_bytes;
> Â Â Â Â Â Â Â Â Â Â Â Âcache->space_info->bytes_pinned += num_bytes;
> @@ -4118,6 +4133,29 @@ static int update_block_group(struct btrfs_trans_handle *trans,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â bytenr, bytenr + num_bytes - 1,
> Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â GFP_NOFS | __GFP_NOFAIL);
> Â Â Â Â Â Â Â Â}
> + Â Â Â Â Â Â Â /*
> + Â Â Â Â Â Â Â Â* So we need to deal with 2 cases here
> + Â Â Â Â Â Â Â Â*
> + Â Â Â Â Â Â Â Â* 1) empty == 1, which means the block group is empty and
> + Â Â Â Â Â Â Â Â* needs to be marked ro so we can remove it later
> + Â Â Â Â Â Â Â Â*
> + Â Â Â Â Â Â Â Â* -or-
> + Â Â Â Â Â Â Â Â*
> + Â Â Â Â Â Â Â Â* 2) empty == -1, which means the block group was previously
> + Â Â Â Â Â Â Â Â* empty and marked read only, but not before somebody tried to
> + Â Â Â Â Â Â Â Â* make an allocation, so go ahead and mark it rw.
> + Â Â Â Â Â Â Â Â*/
> + Â Â Â Â Â Â Â switch (empty) {
> + Â Â Â Â Â Â Â case -1:
> + Â Â Â Â Â Â Â Â Â Â Â btrfs_set_block_group_rw(root, cache);
> + Â Â Â Â Â Â Â Â Â Â Â break;
> + Â Â Â Â Â Â Â case 1:
> + Â Â Â Â Â Â Â Â Â Â Â btrfs_set_block_group_ro_trans(trans, root, cache);
> + Â Â Â Â Â Â Â Â Â Â Â break;
> + Â Â Â Â Â Â Â default:
> + Â Â Â Â Â Â Â Â Â Â Â break;
> + Â Â Â Â Â Â Â }
> +
> Â Â Â Â Â Â Â Âbtrfs_put_block_group(cache);
> Â Â Â Â Â Â Â Âtotal -= num_bytes;
> Â Â Â Â Â Â Â Âbytenr += num_bytes;
> @@ -4288,6 +4326,17 @@ static int unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
> Â Â Â Â Â Â Â Â Â Â Â Âcache->reserved_pinned -= len;
> Â Â Â Â Â Â Â Â Â Â Â Âcache->space_info->bytes_reserved += len;
> Â Â Â Â Â Â Â Â}
> +
> + Â Â Â Â Â Â Â if (btrfs_block_group_used(&cache->item) == 0 &&
> + Â Â Â Â Â Â Â Â Â cache->pinned == 0) {
> + Â Â Â Â Â Â Â Â Â Â Â int ret = 0;
> +
> + Â Â Â Â Â Â Â Â Â Â Â if (!cache->ro)
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ret = set_block_group_ro_lock(cache);
> + Â Â Â Â Â Â Â Â Â Â Â if (!ret)
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â btrfs_queue_worker(&fs_info->generic_worker,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â&cache->work);
> + Â Â Â Â Â Â Â }
> Â Â Â Â Â Â Â Âspin_unlock(&cache->lock);
> Â Â Â Â Â Â Â Âspin_unlock(&cache->space_info->lock);
> Â Â Â Â}
> @@ -7905,7 +7954,7 @@ static u64 update_block_group_flags(struct btrfs_root *root, u64 flags)
> Â Â Â Âreturn flags;
> Â}
>
> -static int set_block_group_ro(struct btrfs_block_group_cache *cache)
> +static int set_block_group_ro_lock(struct btrfs_block_group_cache *cache)
> Â{
> Â Â Â Âstruct btrfs_space_info *sinfo = cache->space_info;
> Â Â Â Âu64 num_bytes;
> @@ -7914,8 +7963,6 @@ static int set_block_group_ro(struct btrfs_block_group_cache *cache)
> Â Â Â Âif (cache->ro)
> Â Â Â Â Â Â Â Âreturn 0;
>
> - Â Â Â spin_lock(&sinfo->lock);
> - Â Â Â spin_lock(&cache->lock);
> Â Â Â Ânum_bytes = cache->key.offset - cache->reserved - cache->pinned -
> Â Â Â Â Â Â Â Â Â Âcache->bytes_super - btrfs_block_group_used(&cache->item);
>
> @@ -7928,37 +7975,67 @@ static int set_block_group_ro(struct btrfs_block_group_cache *cache)
> Â Â Â Â Â Â Â Âcache->ro = 1;
> Â Â Â Â Â Â Â Âret = 0;
> Â Â Â Â}
> +
> + Â Â Â return ret;
> +}
> +
> +static int set_block_group_ro(struct btrfs_block_group_cache *cache)
> +{
> + Â Â Â struct btrfs_space_info *sinfo = cache->space_info;
> + Â Â Â int ret;
> +
> + Â Â Â spin_lock(&sinfo->lock);
> + Â Â Â spin_lock(&cache->lock);
> + Â Â Â ret = set_block_group_ro_lock(cache);
> Â Â Â Âspin_unlock(&cache->lock);
> Â Â Â Âspin_unlock(&sinfo->lock);
> +
> Â Â Â Âreturn ret;
> Â}
>
> -int btrfs_set_block_group_ro(struct btrfs_root *root,
> - Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct btrfs_block_group_cache *cache)
> -
> +static int btrfs_set_block_group_ro_trans(struct btrfs_trans_handle *trans,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct btrfs_root *root,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â struct btrfs_block_group_cache
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â *cache)
> Â{
> - Â Â Â struct btrfs_trans_handle *trans;
> Â Â Â Âu64 alloc_flags;
> Â Â Â Âint ret;
> + Â Â Â bool alloc = true;
>
> - Â Â Â BUG_ON(cache->ro);
> + Â Â Â /*
> + Â Â Â Â* If we're trying to set the block group as read only in a transaction
> + Â Â Â Â* commit then avoid doing the chunk alloc to make lockdep happy.
> + Â Â Â Â*/
> + Â Â Â if (trans->transaction->in_commit)
> + Â Â Â Â Â Â Â alloc = false;
>
> - Â Â Â trans = btrfs_join_transaction(root, 1);
> - Â Â Â BUG_ON(IS_ERR(trans));
> + Â Â Â if (cache->ro)
> + Â Â Â Â Â Â Â return 0;
>
> Â Â Â Âalloc_flags = update_block_group_flags(root, cache->flags);
> - Â Â Â if (alloc_flags != cache->flags)
> + Â Â Â if (alloc && alloc_flags != cache->flags)
> Â Â Â Â Â Â Â Âdo_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
>
> Â Â Â Âret = set_block_group_ro(cache);
> - Â Â Â if (!ret)
> - Â Â Â Â Â Â Â goto out;
> + Â Â Â if (!ret || !alloc)
> + Â Â Â Â Â Â Â return ret;
> Â Â Â Âalloc_flags = get_alloc_profile(root, cache->space_info->flags);
> Â Â Â Âret = do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1);
> Â Â Â Âif (ret < 0)
> - Â Â Â Â Â Â Â goto out;
> - Â Â Â ret = set_block_group_ro(cache);
> -out:
> + Â Â Â Â Â Â Â return ret;
> + Â Â Â return set_block_group_ro(cache);
> +}
> +
> +int btrfs_set_block_group_ro(struct btrfs_root *root,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Âstruct btrfs_block_group_cache *cache)
> +{
> + Â Â Â struct btrfs_trans_handle *trans;
> + Â Â Â int ret;
> +
> + Â Â Â trans = btrfs_join_transaction(root, 0);
> + Â Â Â if (IS_ERR(trans))
> + Â Â Â Â Â Â Â return PTR_ERR(trans);
> + Â Â Â ret = btrfs_set_block_group_ro_trans(trans, root, cache);
> Â Â Â Âbtrfs_end_transaction(trans, root);
> Â Â Â Âreturn ret;
> Â}
> @@ -8206,6 +8283,43 @@ static void __link_block_group(struct btrfs_space_info *space_info,
> Â Â Â Âup_write(&space_info->groups_sem);
> Â}
>
> +static void block_group_delete_fn(struct btrfs_work *work)
> +{
> + Â Â Â struct btrfs_block_group_cache *cache;
> + Â Â Â struct btrfs_fs_info *info;
> + Â Â Â struct btrfs_trans_handle *trans;
> + Â Â Â struct btrfs_root *root;
> + Â Â Â u64 chunk_tree;
> + Â Â Â u64 chunk_objectid;
> + Â Â Â int ret;
> +
> + Â Â Â /*
> + Â Â Â Â* If anything fails in here, just mark the block group as rw and
> + Â Â Â Â* return.
> + Â Â Â Â*/
> + Â Â Â cache = container_of(work, struct btrfs_block_group_cache, work);
> + Â Â Â info = cache->fs_info;
> + Â Â Â root = info->extent_root;
> + Â Â Â chunk_tree = info->chunk_root->root_key.objectid;
> + Â Â Â chunk_objectid = btrfs_block_group_chunk_objectid(&cache->item);
> +
> + Â Â Â if (!cache->ro) {
> + Â Â Â Â Â Â Â WARN_ON_ONCE(1);
> + Â Â Â Â Â Â Â return;
> + Â Â Â }
> +
> + Â Â Â trans = btrfs_start_transaction(info->extent_root, 0);
> + Â Â Â if (IS_ERR(trans)) {
> + Â Â Â Â Â Â Â btrfs_set_block_group_rw(root, cache);
> + Â Â Â Â Â Â Â return;
> + Â Â Â }
> + Â Â Â ret = btrfs_remove_chunk(trans, root, chunk_tree, chunk_objectid,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âcache->key.objectid);
> + Â Â Â if (ret)
> + Â Â Â Â Â Â Â btrfs_set_block_group_rw(root, cache);
> + Â Â Â btrfs_end_transaction(trans, root);
> +}
> +
> Âint btrfs_read_block_groups(struct btrfs_root *root)
> Â{
> Â Â Â Âstruct btrfs_path *path;
> @@ -8257,6 +8371,7 @@ int btrfs_read_block_groups(struct btrfs_root *root)
> Â Â Â Â Â Â Â Âcache->fs_info = info;
> Â Â Â Â Â Â Â ÂINIT_LIST_HEAD(&cache->list);
> Â Â Â Â Â Â Â ÂINIT_LIST_HEAD(&cache->cluster_list);
> + Â Â Â Â Â Â Â cache->work.func = block_group_delete_fn;
>
> Â Â Â Â Â Â Â Âif (need_clear)
> Â Â Â Â Â Â Â Â Â Â Â Âcache->disk_cache_state = BTRFS_DC_CLEAR;
> @@ -8379,6 +8494,7 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
> Â Â Â Âspin_lock_init(&cache->tree_lock);
> Â Â Â ÂINIT_LIST_HEAD(&cache->list);
> Â Â Â ÂINIT_LIST_HEAD(&cache->cluster_list);
> + Â Â Â cache->work.func = block_group_delete_fn;
>
> Â Â Â Âbtrfs_set_block_group_used(&cache->item, bytes_used);
> Â Â Â Âbtrfs_set_block_group_chunk_objectid(&cache->item, chunk_objectid);
> diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
> index cc04dc1..49c055b 100644
> --- a/fs/btrfs/volumes.c
> +++ b/fs/btrfs/volumes.c
> @@ -1726,13 +1726,13 @@ static int btrfs_del_sys_chunk(struct btrfs_root *root, u64 chunk_objectid, u64
> Â Â Â Âreturn ret;
> Â}
>
> -static int btrfs_relocate_chunk(struct btrfs_root *root,
> - Â Â Â Â Â Â Â Â Â Â Â Âu64 chunk_tree, u64 chunk_objectid,
> - Â Â Â Â Â Â Â Â Â Â Â Âu64 chunk_offset)
> +int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
> + Â Â Â Â Â Â Â Â Â Â Âstruct btrfs_root *root,
> + Â Â Â Â Â Â Â Â Â Â Âu64 chunk_tree, u64 chunk_objectid,
> + Â Â Â Â Â Â Â Â Â Â Âu64 chunk_offset)
> Â{
> Â Â Â Âstruct extent_map_tree *em_tree;
> Â Â Â Âstruct btrfs_root *extent_root;
> - Â Â Â struct btrfs_trans_handle *trans;
> Â Â Â Âstruct extent_map *em;
> Â Â Â Âstruct map_lookup *map;
> Â Â Â Âint ret;
> @@ -1742,18 +1742,6 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
> Â Â Â Âextent_root = root->fs_info->extent_root;
> Â Â Â Âem_tree = &root->fs_info->mapping_tree.map_tree;
>
> - Â Â Â ret = btrfs_can_relocate(extent_root, chunk_offset);
> - Â Â Â if (ret)
> - Â Â Â Â Â Â Â return -ENOSPC;
> -
> - Â Â Â /* step one, relocate all the extents inside this chunk */
> - Â Â Â ret = btrfs_relocate_block_group(extent_root, chunk_offset);
> - Â Â Â if (ret)
> - Â Â Â Â Â Â Â return ret;
> -
> - Â Â Â trans = btrfs_start_transaction(root, 0);
> - Â Â Â BUG_ON(!trans);
> -
> Â Â Â Âlock_chunks(root);
>
> Â Â Â Â/*
> @@ -1804,10 +1792,40 @@ static int btrfs_relocate_chunk(struct btrfs_root *root,
> Â Â Â Âfree_extent_map(em);
>
> Â Â Â Âunlock_chunks(root);
> - Â Â Â btrfs_end_transaction(trans, root);
> Â Â Â Âreturn 0;
> Â}
>
> +static int btrfs_relocate_chunk(struct btrfs_root *root,
> + Â Â Â Â Â Â Â Â Â Â Â Âu64 chunk_tree, u64 chunk_objectid,
> + Â Â Â Â Â Â Â Â Â Â Â Âu64 chunk_offset)
> +{
> + Â Â Â struct btrfs_root *extent_root;
> + Â Â Â struct btrfs_trans_handle *trans;
> + Â Â Â int ret;
> +
> + Â Â Â root = root->fs_info->chunk_root;
> + Â Â Â extent_root = root->fs_info->extent_root;
> +
> + Â Â Â ret = btrfs_can_relocate(extent_root, chunk_offset);
> + Â Â Â if (ret)
> + Â Â Â Â Â Â Â return -ENOSPC;
> +
> + Â Â Â /* step one, relocate all the extents inside this chunk */
> + Â Â Â ret = btrfs_relocate_block_group(extent_root, chunk_offset);
> + Â Â Â if (ret)
> + Â Â Â Â Â Â Â return ret;
> +
> + Â Â Â trans = btrfs_start_transaction(root, 0);
> + Â Â Â BUG_ON(!trans);
> +
> + Â Â Â ret = btrfs_remove_chunk(trans, root, chunk_tree, chunk_objectid,
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âchunk_offset);
> +
> + Â Â Â btrfs_end_transaction(trans, root);
> +
> + Â Â Â return ret;
> +}
> +
> Âstatic int btrfs_relocate_sys_chunks(struct btrfs_root *root)
> Â{
> Â Â Â Âstruct btrfs_root *chunk_root = root->fs_info->chunk_root;
> diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
> index 2b638b6..4917cc0 100644
> --- a/fs/btrfs/volumes.h
> +++ b/fs/btrfs/volumes.h
> @@ -183,4 +183,8 @@ int btrfs_chunk_readonly(struct btrfs_root *root, u64 chunk_offset);
> Âint find_free_dev_extent(struct btrfs_trans_handle *trans,
> Â Â Â Â Â Â Â Â Â Â Â Â struct btrfs_device *device, u64 num_bytes,
> Â Â Â Â Â Â Â Â Â Â Â Â u64 *start, u64 *max_avail);
> +int btrfs_remove_chunk(struct btrfs_trans_handle *trans,
> + Â Â Â Â Â Â Â Â Â Â Âstruct btrfs_root *root,
> + Â Â Â Â Â Â Â Â Â Â Âu64 chunk_tree, u64 chunk_objectid,
> + Â Â Â Â Â Â Â Â Â Â Âu64 chunk_offset);
> Â#endif
> --
> 1.6.6.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at Âhttp://vger.kernel.org/majordomo-info.html
>
ÿô.nlj·Ÿ®‰­†+%ŠË±é¥Šwÿº{.nlj·¥Š{±ýžØn‡r¡öë¨è&£ûz¹Þúzf£¢·hšˆ§~†­†Ûÿÿïÿ‘ê_èæ+v‰¨þ)ßø

[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux