diff options
Diffstat (limited to 'fs/btrfs/extent_io.c')
| -rw-r--r-- | fs/btrfs/extent_io.c | 43 | 
1 files changed, 26 insertions, 17 deletions
| diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 68c96057ad2d..60278e52c37a 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1999,7 +1999,8 @@ static int __process_pages_contig(struct address_space *mapping,  				if (!PageDirty(pages[i]) ||  				    pages[i]->mapping != mapping) {  					unlock_page(pages[i]); -					put_page(pages[i]); +					for (; i < ret; i++) +						put_page(pages[i]);  					err = -EAGAIN;  					goto out;  				} @@ -5058,25 +5059,28 @@ struct extent_buffer *alloc_dummy_extent_buffer(struct btrfs_fs_info *fs_info,  static void check_buffer_tree_ref(struct extent_buffer *eb)  {  	int refs; -	/* the ref bit is tricky.  We have to make sure it is set -	 * if we have the buffer dirty.   Otherwise the -	 * code to free a buffer can end up dropping a dirty -	 * page +	/* +	 * The TREE_REF bit is first set when the extent_buffer is added +	 * to the radix tree. It is also reset, if unset, when a new reference +	 * is created by find_extent_buffer.  	 * -	 * Once the ref bit is set, it won't go away while the -	 * buffer is dirty or in writeback, and it also won't -	 * go away while we have the reference count on the -	 * eb bumped. +	 * It is only cleared in two cases: freeing the last non-tree +	 * reference to the extent_buffer when its STALE bit is set or +	 * calling releasepage when the tree reference is the only reference.  	 * -	 * We can't just set the ref bit without bumping the -	 * ref on the eb because free_extent_buffer might -	 * see the ref bit and try to clear it.  If this happens -	 * free_extent_buffer might end up dropping our original -	 * ref by mistake and freeing the page before we are able -	 * to add one more ref. +	 * In both cases, care is taken to ensure that the extent_buffer's +	 * pages are not under io. However, releasepage can be concurrently +	 * called with creating new references, which is prone to race +	 * conditions between the calls to check_buffer_tree_ref in those +	 * codepaths and clearing TREE_REF in try_release_extent_buffer.  	 * -	 * So bump the ref count first, then set the bit.  If someone -	 * beat us to it, drop the ref we added. +	 * The actual lifetime of the extent_buffer in the radix tree is +	 * adequately protected by the refcount, but the TREE_REF bit and +	 * its corresponding reference are not. To protect against this +	 * class of races, we call check_buffer_tree_ref from the codepaths +	 * which trigger io after they set eb->io_pages. Note that once io is +	 * initiated, TREE_REF can no longer be cleared, so that is the +	 * moment at which any such race is best fixed.  	 */  	refs = atomic_read(&eb->refs);  	if (refs >= 2 && test_bit(EXTENT_BUFFER_TREE_REF, &eb->bflags)) @@ -5527,6 +5531,11 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num)  	clear_bit(EXTENT_BUFFER_READ_ERR, &eb->bflags);  	eb->read_mirror = 0;  	atomic_set(&eb->io_pages, num_reads); +	/* +	 * It is possible for releasepage to clear the TREE_REF bit before we +	 * set io_pages. See check_buffer_tree_ref for a more detailed comment. +	 */ +	check_buffer_tree_ref(eb);  	for (i = 0; i < num_pages; i++) {  		page = eb->pages[i]; |