diff options
Diffstat (limited to 'fs/btrfs/ctree.c')
| -rw-r--r-- | fs/btrfs/ctree.c | 39 | 
1 files changed, 36 insertions, 3 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index a99f1c2a710..fedf8b9f03a 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -1469,6 +1469,7 @@ read_block_for_search(struct btrfs_trans_handle *trans,  	u32 blocksize;  	struct extent_buffer *b = *eb_ret;  	struct extent_buffer *tmp; +	int ret;  	blocknr = btrfs_node_blockptr(b, slot);  	gen = btrfs_node_ptr_generation(b, slot); @@ -1476,6 +1477,10 @@ read_block_for_search(struct btrfs_trans_handle *trans,  	tmp = btrfs_find_tree_block(root, blocknr, blocksize);  	if (tmp && btrfs_buffer_uptodate(tmp, gen)) { +		/* +		 * we found an up to date block without sleeping, return +		 * right away +		 */  		*eb_ret = tmp;  		return 0;  	} @@ -1483,7 +1488,9 @@ read_block_for_search(struct btrfs_trans_handle *trans,  	/*  	 * reduce lock contention at high levels  	 * of the btree by dropping locks before -	 * we read. +	 * we read.  Don't release the lock on the current +	 * level because we need to walk this node to figure +	 * out which blocks to read.  	 */  	btrfs_unlock_up_safe(p, level + 1);  	btrfs_set_path_blocking(p); @@ -1494,10 +1501,21 @@ read_block_for_search(struct btrfs_trans_handle *trans,  		reada_for_search(root, p, level, slot, key->objectid);  	btrfs_release_path(NULL, p); + +	ret = -EAGAIN;  	tmp = read_tree_block(root, blocknr, blocksize, gen); -	if (tmp) +	if (tmp) { +		/* +		 * If the read above didn't mark this buffer up to date, +		 * it will never end up being up to date.  Set ret to EIO now +		 * and give up so that our caller doesn't loop forever +		 * on our EAGAINs. +		 */ +		if (!btrfs_buffer_uptodate(tmp, 0)) +			ret = -EIO;  		free_extent_buffer(tmp); -	return -EAGAIN; +	} +	return ret;  }  /* @@ -1696,6 +1714,9 @@ cow_done:  			if (ret == -EAGAIN)  				goto again; +			if (ret == -EIO) +				goto done; +  			if (!p->skip_locking) {  				int lret; @@ -1738,6 +1759,8 @@ done:  	 */  	if (!p->leave_spinning)  		btrfs_set_path_blocking(p); +	if (ret < 0) +		btrfs_release_path(root, p);  	return ret;  } @@ -4212,6 +4235,11 @@ again:  		if (ret == -EAGAIN)  			goto again; +		if (ret < 0) { +			btrfs_release_path(root, path); +			goto done; +		} +  		if (!path->skip_locking) {  			ret = btrfs_try_spin_lock(next);  			if (!ret) { @@ -4246,6 +4274,11 @@ again:  		if (ret == -EAGAIN)  			goto again; +		if (ret < 0) { +			btrfs_release_path(root, path); +			goto done; +		} +  		if (!path->skip_locking) {  			btrfs_assert_tree_locked(path->nodes[level]);  			ret = btrfs_try_spin_lock(next);  |