Re: [PATCH] Btrfs: remove transaction from send

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

 



On Thu, Mar 13, 2014 at 03:42:13PM -0400, Josef Bacik wrote:
> Lets try this again.  We can deadlock the box if we send on a box and try to
> write onto the same fs with the app that is trying to listen to the send pipe.
> This is because the writer could get stuck waiting for a transaction commit
> which is being blocked by the send.  So fix this by making sure looking at the
> commit roots is always going to be consistent.  We do this by keeping track of
> which roots need to have their commit roots swapped during commit, and then
> taking the commit_root_sem and swapping them all at once.  Then make sure we
> take a read lock on the commit_root_sem in cases where we search the commit root
> to make sure we're always looking at a consistent view of the commit roots.
> Previously we had problems with this because we would swap a fs tree commit root
> and then swap the extent tree commit root independently which would cause the
> backref walking code to screw up sometimes.  With this patch we no longer
> deadlock and pass all the weird send/receive corner cases.  Thanks,

   There's something still going on here. I managed to get about twice
as far through my test as I had before, but I again got an "unexpected
EOF in stream", with btrfs send returning 1. As before, I have this in
syslog:

Mar 13 22:09:12 s_src@amelia kernel: BTRFS error (device sda2): did not find backref in send_root. inode=1786631, offset=825257984, disk_byte=36504023040 found extent=36504023040\x0a

   So, on the evidence of one data point (I'll have another one when I
wake up tomorrow morning), this has made the problem harder to trigger
but it's still possible.

   Hugo.

> Reportedy-by: Hugo Mills <hugo@xxxxxxxxxxxxx>
> Signed-off-by: Josef Bacik <jbacik@xxxxxx>
> ---
>  fs/btrfs/backref.c     | 33 +++++++++++++++----
>  fs/btrfs/ctree.c       | 88 --------------------------------------------------
>  fs/btrfs/ctree.h       |  3 +-
>  fs/btrfs/disk-io.c     |  3 +-
>  fs/btrfs/extent-tree.c | 20 ++++++------
>  fs/btrfs/inode-map.c   | 14 ++++----
>  fs/btrfs/send.c        | 57 ++------------------------------
>  fs/btrfs/transaction.c | 45 ++++++++++++++++----------
>  fs/btrfs/transaction.h |  1 +
>  9 files changed, 77 insertions(+), 187 deletions(-)
> 
> diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
> index 860f4f2..0be0e94 100644
> --- a/fs/btrfs/backref.c
> +++ b/fs/btrfs/backref.c
> @@ -329,7 +329,10 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
>  		goto out;
>  	}
>  
> -	root_level = btrfs_old_root_level(root, time_seq);
> +	if (path->search_commit_root)
> +		root_level = btrfs_header_level(root->commit_root);
> +	else
> +		root_level = btrfs_old_root_level(root, time_seq);
>  
>  	if (root_level + 1 == level) {
>  		srcu_read_unlock(&fs_info->subvol_srcu, index);
> @@ -1092,9 +1095,9 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
>   *
>   * returns 0 on success, < 0 on error.
>   */
> -int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> -				struct btrfs_fs_info *fs_info, u64 bytenr,
> -				u64 time_seq, struct ulist **roots)
> +static int __btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> +				  struct btrfs_fs_info *fs_info, u64 bytenr,
> +				  u64 time_seq, struct ulist **roots)
>  {
>  	struct ulist *tmp;
>  	struct ulist_node *node = NULL;
> @@ -1130,6 +1133,20 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
>  	return 0;
>  }
>  
> +int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
> +			 struct btrfs_fs_info *fs_info, u64 bytenr,
> +			 u64 time_seq, struct ulist **roots)
> +{
> +	int ret;
> +
> +	if (!trans)
> +		down_read(&fs_info->commit_root_sem);
> +	ret = __btrfs_find_all_roots(trans, fs_info, bytenr, time_seq, roots);
> +	if (!trans)
> +		up_read(&fs_info->commit_root_sem);
> +	return ret;
> +}
> +
>  /*
>   * this makes the path point to (inum INODE_ITEM ioff)
>   */
> @@ -1509,6 +1526,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
>  		if (IS_ERR(trans))
>  			return PTR_ERR(trans);
>  		btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem);
> +	} else {
> +		down_read(&fs_info->commit_root_sem);
>  	}
>  
>  	ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid,
> @@ -1519,8 +1538,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
>  
>  	ULIST_ITER_INIT(&ref_uiter);
>  	while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) {
> -		ret = btrfs_find_all_roots(trans, fs_info, ref_node->val,
> -					   tree_mod_seq_elem.seq, &roots);
> +		ret = __btrfs_find_all_roots(trans, fs_info, ref_node->val,
> +					     tree_mod_seq_elem.seq, &roots);
>  		if (ret)
>  			break;
>  		ULIST_ITER_INIT(&root_uiter);
> @@ -1542,6 +1561,8 @@ out:
>  	if (!search_commit_root) {
>  		btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
>  		btrfs_end_transaction(trans, fs_info->extent_root);
> +	} else {
> +		up_read(&fs_info->commit_root_sem);
>  	}
>  
>  	return ret;
> diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
> index 88d1b1e..9d89c16 100644
> --- a/fs/btrfs/ctree.c
> +++ b/fs/btrfs/ctree.c
> @@ -5360,7 +5360,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>  {
>  	int ret;
>  	int cmp;
> -	struct btrfs_trans_handle *trans = NULL;
>  	struct btrfs_path *left_path = NULL;
>  	struct btrfs_path *right_path = NULL;
>  	struct btrfs_key left_key;
> @@ -5378,9 +5377,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>  	u64 right_blockptr;
>  	u64 left_gen;
>  	u64 right_gen;
> -	u64 left_start_ctransid;
> -	u64 right_start_ctransid;
> -	u64 ctransid;
>  
>  	left_path = btrfs_alloc_path();
>  	if (!left_path) {
> @@ -5404,21 +5400,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>  	right_path->search_commit_root = 1;
>  	right_path->skip_locking = 1;
>  
> -	spin_lock(&left_root->root_item_lock);
> -	left_start_ctransid = btrfs_root_ctransid(&left_root->root_item);
> -	spin_unlock(&left_root->root_item_lock);
> -
> -	spin_lock(&right_root->root_item_lock);
> -	right_start_ctransid = btrfs_root_ctransid(&right_root->root_item);
> -	spin_unlock(&right_root->root_item_lock);
> -
> -	trans = btrfs_join_transaction(left_root);
> -	if (IS_ERR(trans)) {
> -		ret = PTR_ERR(trans);
> -		trans = NULL;
> -		goto out;
> -	}
> -
>  	/*
>  	 * Strategy: Go to the first items of both trees. Then do
>  	 *
> @@ -5482,67 +5463,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
>  	advance_left = advance_right = 0;
>  
>  	while (1) {
> -		/*
> -		 * We need to make sure the transaction does not get committed
> -		 * while we do anything on commit roots. This means, we need to
> -		 * join and leave transactions for every item that we process.
> -		 */
> -		if (trans && btrfs_should_end_transaction(trans, left_root)) {
> -			btrfs_release_path(left_path);
> -			btrfs_release_path(right_path);
> -
> -			ret = btrfs_end_transaction(trans, left_root);
> -			trans = NULL;
> -			if (ret < 0)
> -				goto out;
> -		}
> -		/* now rejoin the transaction */
> -		if (!trans) {
> -			trans = btrfs_join_transaction(left_root);
> -			if (IS_ERR(trans)) {
> -				ret = PTR_ERR(trans);
> -				trans = NULL;
> -				goto out;
> -			}
> -
> -			spin_lock(&left_root->root_item_lock);
> -			ctransid = btrfs_root_ctransid(&left_root->root_item);
> -			spin_unlock(&left_root->root_item_lock);
> -			if (ctransid != left_start_ctransid)
> -				left_start_ctransid = 0;
> -
> -			spin_lock(&right_root->root_item_lock);
> -			ctransid = btrfs_root_ctransid(&right_root->root_item);
> -			spin_unlock(&right_root->root_item_lock);
> -			if (ctransid != right_start_ctransid)
> -				right_start_ctransid = 0;
> -
> -			if (!left_start_ctransid || !right_start_ctransid) {
> -				WARN(1, KERN_WARNING
> -					"BTRFS: btrfs_compare_tree detected "
> -					"a change in one of the trees while "
> -					"iterating. This is probably a "
> -					"bug.\n");
> -				ret = -EIO;
> -				goto out;
> -			}
> -
> -			/*
> -			 * the commit root may have changed, so start again
> -			 * where we stopped
> -			 */
> -			left_path->lowest_level = left_level;
> -			right_path->lowest_level = right_level;
> -			ret = btrfs_search_slot(NULL, left_root,
> -					&left_key, left_path, 0, 0);
> -			if (ret < 0)
> -				goto out;
> -			ret = btrfs_search_slot(NULL, right_root,
> -					&right_key, right_path, 0, 0);
> -			if (ret < 0)
> -				goto out;
> -		}
> -
>  		if (advance_left && !left_end_reached) {
>  			ret = tree_advance(left_root, left_path, &left_level,
>  					left_root_level,
> @@ -5672,14 +5592,6 @@ out:
>  	btrfs_free_path(left_path);
>  	btrfs_free_path(right_path);
>  	kfree(tmp_buf);
> -
> -	if (trans) {
> -		if (!ret)
> -			ret = btrfs_end_transaction(trans, left_root);
> -		else
> -			btrfs_end_transaction(trans, left_root);
> -	}
> -
>  	return ret;
>  }
>  
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index 2a9d32e..4253ab2 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -1440,7 +1440,7 @@ struct btrfs_fs_info {
>  	 */
>  	struct mutex ordered_extent_flush_mutex;
>  
> -	struct rw_semaphore extent_commit_sem;
> +	struct rw_semaphore commit_root_sem;
>  
>  	struct rw_semaphore cleanup_work_sem;
>  
> @@ -1711,7 +1711,6 @@ struct btrfs_root {
>  	struct btrfs_block_rsv *block_rsv;
>  
>  	/* free ino cache stuff */
> -	struct mutex fs_commit_mutex;
>  	struct btrfs_free_space_ctl *free_ino_ctl;
>  	enum btrfs_caching_type cached;
>  	spinlock_t cache_lock;
> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
> index d9698fd..a152a96 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -1549,7 +1549,6 @@ int btrfs_init_fs_root(struct btrfs_root *root)
>  	root->subv_writers = writers;
>  
>  	btrfs_init_free_ino_ctl(root);
> -	mutex_init(&root->fs_commit_mutex);
>  	spin_lock_init(&root->cache_lock);
>  	init_waitqueue_head(&root->cache_wait);
>  
> @@ -2327,7 +2326,7 @@ int open_ctree(struct super_block *sb,
>  	mutex_init(&fs_info->transaction_kthread_mutex);
>  	mutex_init(&fs_info->cleaner_mutex);
>  	mutex_init(&fs_info->volume_mutex);
> -	init_rwsem(&fs_info->extent_commit_sem);
> +	init_rwsem(&fs_info->commit_root_sem);
>  	init_rwsem(&fs_info->cleanup_work_sem);
>  	init_rwsem(&fs_info->subvol_sem);
>  	sema_init(&fs_info->uuid_tree_rescan_sem, 1);
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index c6b6a6e..696f0b6 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -419,7 +419,7 @@ static noinline void caching_thread(struct btrfs_work *work)
>  again:
>  	mutex_lock(&caching_ctl->mutex);
>  	/* need to make sure the commit_root doesn't disappear */
> -	down_read(&fs_info->extent_commit_sem);
> +	down_read(&fs_info->commit_root_sem);
>  
>  next:
>  	ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
> @@ -443,10 +443,10 @@ next:
>  				break;
>  
>  			if (need_resched() ||
> -			    rwsem_is_contended(&fs_info->extent_commit_sem)) {
> +			    rwsem_is_contended(&fs_info->commit_root_sem)) {
>  				caching_ctl->progress = last;
>  				btrfs_release_path(path);
> -				up_read(&fs_info->extent_commit_sem);
> +				up_read(&fs_info->commit_root_sem);
>  				mutex_unlock(&caching_ctl->mutex);
>  				cond_resched();
>  				goto again;
> @@ -513,7 +513,7 @@ next:
>  
>  err:
>  	btrfs_free_path(path);
> -	up_read(&fs_info->extent_commit_sem);
> +	up_read(&fs_info->commit_root_sem);
>  
>  	free_excluded_extents(extent_root, block_group);
>  
> @@ -633,10 +633,10 @@ static int cache_block_group(struct btrfs_block_group_cache *cache,
>  		return 0;
>  	}
>  
> -	down_write(&fs_info->extent_commit_sem);
> +	down_write(&fs_info->commit_root_sem);
>  	atomic_inc(&caching_ctl->count);
>  	list_add_tail(&caching_ctl->list, &fs_info->caching_block_groups);
> -	up_write(&fs_info->extent_commit_sem);
> +	up_write(&fs_info->commit_root_sem);
>  
>  	btrfs_get_block_group(cache);
>  
> @@ -5470,7 +5470,7 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
>  	struct btrfs_block_group_cache *cache;
>  	struct btrfs_space_info *space_info;
>  
> -	down_write(&fs_info->extent_commit_sem);
> +	down_write(&fs_info->commit_root_sem);
>  
>  	list_for_each_entry_safe(caching_ctl, next,
>  				 &fs_info->caching_block_groups, list) {
> @@ -5489,7 +5489,7 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
>  	else
>  		fs_info->pinned_extents = &fs_info->freed_extents[0];
>  
> -	up_write(&fs_info->extent_commit_sem);
> +	up_write(&fs_info->commit_root_sem);
>  
>  	list_for_each_entry_rcu(space_info, &fs_info->space_info, list)
>  		percpu_counter_set(&space_info->total_bytes_pinned, 0);
> @@ -8255,14 +8255,14 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
>  	struct btrfs_caching_control *caching_ctl;
>  	struct rb_node *n;
>  
> -	down_write(&info->extent_commit_sem);
> +	down_write(&info->commit_root_sem);
>  	while (!list_empty(&info->caching_block_groups)) {
>  		caching_ctl = list_entry(info->caching_block_groups.next,
>  					 struct btrfs_caching_control, list);
>  		list_del(&caching_ctl->list);
>  		put_caching_control(caching_ctl);
>  	}
> -	up_write(&info->extent_commit_sem);
> +	up_write(&info->commit_root_sem);
>  
>  	spin_lock(&info->block_group_cache_lock);
>  	while ((n = rb_last(&info->block_group_cache_tree)) != NULL) {
> diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c
> index ab485e5..cc8ca19 100644
> --- a/fs/btrfs/inode-map.c
> +++ b/fs/btrfs/inode-map.c
> @@ -55,7 +55,7 @@ static int caching_kthread(void *data)
>  	key.type = BTRFS_INODE_ITEM_KEY;
>  again:
>  	/* need to make sure the commit_root doesn't disappear */
> -	mutex_lock(&root->fs_commit_mutex);
> +	down_read(&fs_info->commit_root_sem);
>  
>  	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
>  	if (ret < 0)
> @@ -88,7 +88,7 @@ again:
>  				btrfs_item_key_to_cpu(leaf, &key, 0);
>  				btrfs_release_path(path);
>  				root->cache_progress = last;
> -				mutex_unlock(&root->fs_commit_mutex);
> +				up_read(&fs_info->commit_root_sem);
>  				schedule_timeout(1);
>  				goto again;
>  			} else
> @@ -127,7 +127,7 @@ next:
>  	btrfs_unpin_free_ino(root);
>  out:
>  	wake_up(&root->cache_wait);
> -	mutex_unlock(&root->fs_commit_mutex);
> +	up_read(&fs_info->commit_root_sem);
>  
>  	btrfs_free_path(path);
>  
> @@ -223,11 +223,11 @@ again:
>  		 * or the caching work is done.
>  		 */
>  
> -		mutex_lock(&root->fs_commit_mutex);
> +		down_write(&root->fs_info->commit_root_sem);
>  		spin_lock(&root->cache_lock);
>  		if (root->cached == BTRFS_CACHE_FINISHED) {
>  			spin_unlock(&root->cache_lock);
> -			mutex_unlock(&root->fs_commit_mutex);
> +			up_write(&root->fs_info->commit_root_sem);
>  			goto again;
>  		}
>  		spin_unlock(&root->cache_lock);
> @@ -240,7 +240,7 @@ again:
>  		else
>  			__btrfs_add_free_space(pinned, objectid, 1);
>  
> -		mutex_unlock(&root->fs_commit_mutex);
> +		up_write(&root->fs_info->commit_root_sem);
>  	}
>  }
>  
> @@ -250,7 +250,7 @@ again:
>   * and others will just be dropped, because the commit root we were
>   * searching has changed.
>   *
> - * Must be called with root->fs_commit_mutex held
> + * Must be called with root->fs_info->commit_root_sem held
>   */
>  void btrfs_unpin_free_ino(struct btrfs_root *root)
>  {
> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
> index 6463691..0148999 100644
> --- a/fs/btrfs/send.c
> +++ b/fs/btrfs/send.c
> @@ -1268,8 +1268,10 @@ static int find_extent_clone(struct send_ctx *sctx,
>  	}
>  	logical = disk_byte + btrfs_file_extent_offset(eb, fi);
>  
> +	down_read(&sctx->send_root->fs_info->commit_root_sem);
>  	ret = extent_from_logical(sctx->send_root->fs_info, disk_byte, tmp_path,
>  				  &found_key, &flags);
> +	up_read(&sctx->send_root->fs_info->commit_root_sem);
>  	btrfs_release_path(tmp_path);
>  
>  	if (ret < 0)
> @@ -5311,57 +5313,21 @@ out:
>  static int full_send_tree(struct send_ctx *sctx)
>  {
>  	int ret;
> -	struct btrfs_trans_handle *trans = NULL;
>  	struct btrfs_root *send_root = sctx->send_root;
>  	struct btrfs_key key;
>  	struct btrfs_key found_key;
>  	struct btrfs_path *path;
>  	struct extent_buffer *eb;
>  	int slot;
> -	u64 start_ctransid;
> -	u64 ctransid;
>  
>  	path = alloc_path_for_send();
>  	if (!path)
>  		return -ENOMEM;
>  
> -	spin_lock(&send_root->root_item_lock);
> -	start_ctransid = btrfs_root_ctransid(&send_root->root_item);
> -	spin_unlock(&send_root->root_item_lock);
> -
>  	key.objectid = BTRFS_FIRST_FREE_OBJECTID;
>  	key.type = BTRFS_INODE_ITEM_KEY;
>  	key.offset = 0;
>  
> -join_trans:
> -	/*
> -	 * We need to make sure the transaction does not get committed
> -	 * while we do anything on commit roots. Join a transaction to prevent
> -	 * this.
> -	 */
> -	trans = btrfs_join_transaction(send_root);
> -	if (IS_ERR(trans)) {
> -		ret = PTR_ERR(trans);
> -		trans = NULL;
> -		goto out;
> -	}
> -
> -	/*
> -	 * Make sure the tree has not changed after re-joining. We detect this
> -	 * by comparing start_ctransid and ctransid. They should always match.
> -	 */
> -	spin_lock(&send_root->root_item_lock);
> -	ctransid = btrfs_root_ctransid(&send_root->root_item);
> -	spin_unlock(&send_root->root_item_lock);
> -
> -	if (ctransid != start_ctransid) {
> -		WARN(1, KERN_WARNING "BTRFS: the root that you're trying to "
> -				     "send was modified in between. This is "
> -				     "probably a bug.\n");
> -		ret = -EIO;
> -		goto out;
> -	}
> -
>  	ret = btrfs_search_slot_for_read(send_root, &key, path, 1, 0);
>  	if (ret < 0)
>  		goto out;
> @@ -5369,19 +5335,6 @@ join_trans:
>  		goto out_finish;
>  
>  	while (1) {
> -		/*
> -		 * When someone want to commit while we iterate, end the
> -		 * joined transaction and rejoin.
> -		 */
> -		if (btrfs_should_end_transaction(trans, send_root)) {
> -			ret = btrfs_end_transaction(trans, send_root);
> -			trans = NULL;
> -			if (ret < 0)
> -				goto out;
> -			btrfs_release_path(path);
> -			goto join_trans;
> -		}
> -
>  		eb = path->nodes[0];
>  		slot = path->slots[0];
>  		btrfs_item_key_to_cpu(eb, &found_key, slot);
> @@ -5409,12 +5362,6 @@ out_finish:
>  
>  out:
>  	btrfs_free_path(path);
> -	if (trans) {
> -		if (!ret)
> -			ret = btrfs_end_transaction(trans, send_root);
> -		else
> -			btrfs_end_transaction(trans, send_root);
> -	}
>  	return ret;
>  }
>  
> diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
> index a999b85..ab8e6fd 100644
> --- a/fs/btrfs/transaction.c
> +++ b/fs/btrfs/transaction.c
> @@ -75,10 +75,21 @@ void btrfs_put_transaction(struct btrfs_transaction *transaction)
>  	}
>  }
>  
> -static noinline void switch_commit_root(struct btrfs_root *root)
> +static noinline void switch_commit_roots(struct btrfs_transaction *trans,
> +					 struct btrfs_fs_info *fs_info)
>  {
> -	free_extent_buffer(root->commit_root);
> -	root->commit_root = btrfs_root_node(root);
> +	struct btrfs_root *root, *tmp;
> +
> +	down_write(&fs_info->commit_root_sem);
> +	list_for_each_entry_safe(root, tmp, &trans->switch_commits,
> +				 dirty_list) {
> +		list_del_init(&root->dirty_list);
> +		free_extent_buffer(root->commit_root);
> +		root->commit_root = btrfs_root_node(root);
> +		if (is_fstree(root->objectid))
> +			btrfs_unpin_free_ino(root);
> +	}
> +	up_write(&fs_info->commit_root_sem);
>  }
>  
>  static inline void extwriter_counter_inc(struct btrfs_transaction *trans,
> @@ -208,6 +219,7 @@ loop:
>  	INIT_LIST_HEAD(&cur_trans->pending_snapshots);
>  	INIT_LIST_HEAD(&cur_trans->ordered_operations);
>  	INIT_LIST_HEAD(&cur_trans->pending_chunks);
> +	INIT_LIST_HEAD(&cur_trans->switch_commits);
>  	list_add_tail(&cur_trans->list, &fs_info->trans_list);
>  	extent_io_tree_init(&cur_trans->dirty_pages,
>  			     fs_info->btree_inode->i_mapping);
> @@ -925,9 +937,6 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
>  			return ret;
>  	}
>  
> -	if (root != root->fs_info->extent_root)
> -		switch_commit_root(root);
> -
>  	return 0;
>  }
>  
> @@ -983,15 +992,16 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
>  		list_del_init(next);
>  		root = list_entry(next, struct btrfs_root, dirty_list);
>  
> +		if (root != fs_info->extent_root)
> +			list_add_tail(&root->dirty_list,
> +				      &trans->transaction->switch_commits);
>  		ret = update_cowonly_root(trans, root);
>  		if (ret)
>  			return ret;
>  	}
>  
> -	down_write(&fs_info->extent_commit_sem);
> -	switch_commit_root(fs_info->extent_root);
> -	up_write(&fs_info->extent_commit_sem);
> -
> +	list_add_tail(&fs_info->extent_root->dirty_list,
> +		      &trans->transaction->switch_commits);
>  	btrfs_after_dev_replace_commit(fs_info);
>  
>  	return 0;
> @@ -1048,11 +1058,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
>  			smp_wmb();
>  
>  			if (root->commit_root != root->node) {
> -				mutex_lock(&root->fs_commit_mutex);
> -				switch_commit_root(root);
> -				btrfs_unpin_free_ino(root);
> -				mutex_unlock(&root->fs_commit_mutex);
> -
> +				list_add_tail(&root->dirty_list,
> +					&trans->transaction->switch_commits);
>  				btrfs_set_root_node(&root->root_item,
>  						    root->node);
>  			}
> @@ -1863,11 +1870,15 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
>  
>  	btrfs_set_root_node(&root->fs_info->tree_root->root_item,
>  			    root->fs_info->tree_root->node);
> -	switch_commit_root(root->fs_info->tree_root);
> +	list_add_tail(&root->fs_info->tree_root->dirty_list,
> +		      &cur_trans->switch_commits);
>  
>  	btrfs_set_root_node(&root->fs_info->chunk_root->root_item,
>  			    root->fs_info->chunk_root->node);
> -	switch_commit_root(root->fs_info->chunk_root);
> +	list_add_tail(&root->fs_info->chunk_root->dirty_list,
> +		      &cur_trans->switch_commits);
> +
> +	switch_commit_roots(cur_trans, root->fs_info);
>  
>  	assert_qgroups_uptodate(trans);
>  	update_super_roots(root);
> diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
> index 6ac037e..aa014fe 100644
> --- a/fs/btrfs/transaction.h
> +++ b/fs/btrfs/transaction.h
> @@ -57,6 +57,7 @@ struct btrfs_transaction {
>  	struct list_head pending_snapshots;
>  	struct list_head ordered_operations;
>  	struct list_head pending_chunks;
> +	struct list_head switch_commits;
>  	struct btrfs_delayed_ref_root delayed_refs;
>  	int aborted;
>  };

-- 
=== Hugo Mills: hugo@... carfax.org.uk | darksatanic.net | lug.org.uk ===
  PGP key: 65E74AC0 from wwwkeys.eu.pgp.net or http://www.carfax.org.uk
   --- The enemy have elected for Death by Powerpoint. That's what ---   
                             they shall get.                             

Attachment: signature.asc
Description: Digital signature


[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