diff options
Diffstat (limited to 'fs/btrfs/file.c')
| -rw-r--r-- | fs/btrfs/file.c | 33 | 
1 files changed, 25 insertions, 8 deletions
| diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 2be00e873e92..15b925142793 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -531,6 +531,14 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages,  	end_of_last_block = start_pos + num_bytes - 1; +	/* +	 * The pages may have already been dirty, clear out old accounting so +	 * we can set things up properly +	 */ +	clear_extent_bit(&BTRFS_I(inode)->io_tree, start_pos, end_of_last_block, +			 EXTENT_DIRTY | EXTENT_DELALLOC | +			 EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 0, 0, cached); +  	if (!btrfs_is_free_space_inode(BTRFS_I(inode))) {  		if (start_pos >= isize &&  		    !(BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)) { @@ -1500,18 +1508,27 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct page **pages,  		}  		if (ordered)  			btrfs_put_ordered_extent(ordered); -		clear_extent_bit(&inode->io_tree, start_pos, last_pos, -				 EXTENT_DIRTY | EXTENT_DELALLOC | -				 EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, -				 0, 0, cached_state); +  		*lockstart = start_pos;  		*lockend = last_pos;  		ret = 1;  	} +	/* +	 * It's possible the pages are dirty right now, but we don't want +	 * to clean them yet because copy_from_user may catch a page fault +	 * and we might have to fall back to one page at a time.  If that +	 * happens, we'll unlock these pages and we'd have a window where +	 * reclaim could sneak in and drop the once-dirty page on the floor +	 * without writing it. +	 * +	 * We have the pages locked and the extent range locked, so there's +	 * no way someone can start IO on any dirty pages in this range. +	 * +	 * We'll call btrfs_dirty_pages() later on, and that will flip around +	 * delalloc bits and dirty the pages as required. +	 */  	for (i = 0; i < num_pages; i++) { -		if (clear_page_dirty_for_io(pages[i])) -			account_page_redirty(pages[i]);  		set_page_extent_mapped(pages[i]);  		WARN_ON(!PageLocked(pages[i]));  	} @@ -2544,7 +2561,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)  	}  	ret = btrfs_block_rsv_migrate(&fs_info->trans_block_rsv, rsv, -				      min_size, 0); +				      min_size, false);  	BUG_ON(ret);  	trans->block_rsv = rsv; @@ -2594,7 +2611,7 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)  		}  		ret = btrfs_block_rsv_migrate(&fs_info->trans_block_rsv, -					      rsv, min_size, 0); +					      rsv, min_size, false);  		BUG_ON(ret);	/* shouldn't happen */  		trans->block_rsv = rsv; |