From 87ef2bb46bfc4be0b40799e68115cbe28d80a1bd Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Thu, 30 Oct 2008 11:23:27 -0400 Subject: [PATCH] Btrfs: prevent looping forever in finish_current_insert and del_pending_extents finish_current_insert and del_pending_extents process extent tree modifications that build up while we are changing the extent tree. It is a confusing bit of code that prevents recursion. Both functions run through a list of pending operations and both funcs add to the list of pending operations. If you have two procs in either one of them, they can end up looping forever making more work for each other. This patch makes them walk forward through the list of pending changes instead of always trying to process the entire list. At transaction commit time, we catch any changes that were left over. Signed-off-by: Chris Mason --- fs/btrfs/extent-tree.c | 54 +++++++++++++++++++++++------------------- fs/btrfs/transaction.c | 12 ++++++++++ 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 155c8dc56a22..fada9c22a021 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -45,9 +45,9 @@ struct pending_extent_op { }; static int finish_current_insert(struct btrfs_trans_handle *trans, struct - btrfs_root *extent_root); + btrfs_root *extent_root, int all); static int del_pending_extents(struct btrfs_trans_handle *trans, struct - btrfs_root *extent_root); + btrfs_root *extent_root, int all); static struct btrfs_block_group_cache * __btrfs_find_block_group(struct btrfs_root *root, struct btrfs_block_group_cache *hint, @@ -711,8 +711,8 @@ static int __btrfs_update_extent_ref(struct btrfs_trans_handle *trans, parent, ref_root, ref_generation, owner_objectid); BUG_ON(ret); - finish_current_insert(trans, extent_root); - del_pending_extents(trans, extent_root); + finish_current_insert(trans, extent_root, 0); + del_pending_extents(trans, extent_root, 0); out: btrfs_free_path(path); return ret; @@ -784,8 +784,8 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, ref_root, ref_generation, owner_objectid); BUG_ON(ret); - finish_current_insert(trans, root->fs_info->extent_root); - del_pending_extents(trans, root->fs_info->extent_root); + finish_current_insert(trans, root->fs_info->extent_root, 0); + del_pending_extents(trans, root->fs_info->extent_root, 0); btrfs_free_path(path); return 0; @@ -810,8 +810,8 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, int btrfs_extent_post_op(struct btrfs_trans_handle *trans, struct btrfs_root *root) { - finish_current_insert(trans, root->fs_info->extent_root); - del_pending_extents(trans, root->fs_info->extent_root); + finish_current_insert(trans, root->fs_info->extent_root, 1); + del_pending_extents(trans, root->fs_info->extent_root, 1); return 0; } @@ -1292,8 +1292,8 @@ static int write_one_cache_group(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); btrfs_release_path(extent_root, path); fail: - finish_current_insert(trans, extent_root); - pending_ret = del_pending_extents(trans, extent_root); + finish_current_insert(trans, extent_root, 0); + pending_ret = del_pending_extents(trans, extent_root, 0); if (ret) return ret; if (pending_ret) @@ -1690,7 +1690,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans, } static int finish_current_insert(struct btrfs_trans_handle *trans, - struct btrfs_root *extent_root) + struct btrfs_root *extent_root, int all) { u64 start; u64 end; @@ -1714,7 +1714,7 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, &end, EXTENT_WRITEBACK); if (ret) { mutex_unlock(&info->extent_ins_mutex); - if (search) { + if (search && all) { search = 0; continue; } @@ -1723,7 +1723,7 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, ret = try_lock_extent(&info->extent_ins, start, end, GFP_NOFS); if (!ret) { - search = end+1; + search = end + 1; mutex_unlock(&info->extent_ins_mutex); cond_resched(); continue; @@ -1785,7 +1785,10 @@ static int finish_current_insert(struct btrfs_trans_handle *trans, } kfree(extent_op); unlock_extent(&info->extent_ins, start, end, GFP_NOFS); - search = 0; + if (all) + search = 0; + else + search = end + 1; cond_resched(); } @@ -1992,7 +1995,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, #endif } btrfs_free_path(path); - finish_current_insert(trans, extent_root); + finish_current_insert(trans, extent_root, 0); return ret; } @@ -2001,7 +2004,7 @@ static int __free_extent(struct btrfs_trans_handle *trans, * them from the extent map */ static int del_pending_extents(struct btrfs_trans_handle *trans, struct - btrfs_root *extent_root) + btrfs_root *extent_root, int all) { int ret; int err = 0; @@ -2023,7 +2026,7 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct EXTENT_WRITEBACK); if (ret) { mutex_unlock(&info->extent_ins_mutex); - if (search) { + if (all && search) { search = 0; continue; } @@ -2088,7 +2091,10 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct err = ret; unlock_extent(extent_ins, start, end, GFP_NOFS); - search = 0; + if (all) + search = 0; + else + search = end + 1; cond_resched(); } return err; @@ -2155,8 +2161,8 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, root_objectid, ref_generation, owner_objectid, pin, pin == 0); - finish_current_insert(trans, root->fs_info->extent_root); - pending_ret = del_pending_extents(trans, root->fs_info->extent_root); + finish_current_insert(trans, root->fs_info->extent_root, 0); + pending_ret = del_pending_extents(trans, root->fs_info->extent_root, 0); return ret ? ret : pending_ret; } @@ -2580,8 +2586,8 @@ static int __btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans, trans->alloc_exclude_start = 0; trans->alloc_exclude_nr = 0; btrfs_free_path(path); - finish_current_insert(trans, extent_root); - pending_ret = del_pending_extents(trans, extent_root); + finish_current_insert(trans, extent_root, 0); + pending_ret = del_pending_extents(trans, extent_root, 0); if (ret) goto out; @@ -5229,8 +5235,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans, sizeof(cache->item)); BUG_ON(ret); - finish_current_insert(trans, extent_root); - ret = del_pending_extents(trans, extent_root); + finish_current_insert(trans, extent_root, 0); + ret = del_pending_extents(trans, extent_root, 0); BUG_ON(ret); set_avail_alloc_bits(extent_root->fs_info, type); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 924af6f2aeac..968b84f17a19 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -430,7 +430,10 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans, u64 old_root_bytenr; struct btrfs_root *tree_root = root->fs_info->tree_root; + btrfs_extent_post_op(trans, root); btrfs_write_dirty_block_groups(trans, root); + btrfs_extent_post_op(trans, root); + while(1) { old_root_bytenr = btrfs_root_bytenr(&root->root_item); if (old_root_bytenr == root->node->start) @@ -440,11 +443,15 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans, btrfs_set_root_level(&root->root_item, btrfs_header_level(root->node)); btrfs_set_root_generation(&root->root_item, trans->transid); + + btrfs_extent_post_op(trans, root); + ret = btrfs_update_root(trans, tree_root, &root->root_key, &root->root_item); BUG_ON(ret); btrfs_write_dirty_block_groups(trans, root); + btrfs_extent_post_op(trans, root); } return 0; } @@ -459,15 +466,20 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans, struct list_head *next; struct extent_buffer *eb; + btrfs_extent_post_op(trans, fs_info->tree_root); + eb = btrfs_lock_root_node(fs_info->tree_root); btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb, 0); btrfs_tree_unlock(eb); free_extent_buffer(eb); + btrfs_extent_post_op(trans, fs_info->tree_root); + while(!list_empty(&fs_info->dirty_cowonly_roots)) { next = fs_info->dirty_cowonly_roots.next; list_del_init(next); root = list_entry(next, struct btrfs_root, dirty_list); + update_cowonly_root(trans, root); } return 0;