diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 61 | 
1 files changed, 53 insertions, 8 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a4f02501da4..a7d1921ac76 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -987,7 +987,7 @@ static noinline void async_cow_start(struct btrfs_work *work)  			    async_cow->start, async_cow->end, async_cow,  			    &num_added);  	if (num_added == 0) { -		iput(async_cow->inode); +		btrfs_add_delayed_iput(async_cow->inode);  		async_cow->inode = NULL;  	}  } @@ -1023,7 +1023,7 @@ static noinline void async_cow_free(struct btrfs_work *work)  	struct async_cow *async_cow;  	async_cow = container_of(work, struct async_cow, work);  	if (async_cow->inode) -		iput(async_cow->inode); +		btrfs_add_delayed_iput(async_cow->inode);  	kfree(async_cow);  } @@ -3754,7 +3754,7 @@ void btrfs_evict_inode(struct inode *inode)  	btrfs_wait_ordered_range(inode, 0, (u64)-1);  	if (root->fs_info->log_root_recovering) { -		BUG_ON(!test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM, +		BUG_ON(test_bit(BTRFS_INODE_HAS_ORPHAN_ITEM,  				 &BTRFS_I(inode)->runtime_flags));  		goto no_delete;  	} @@ -5876,8 +5876,17 @@ map:  	bh_result->b_size = len;  	bh_result->b_bdev = em->bdev;  	set_buffer_mapped(bh_result); -	if (create && !test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) -		set_buffer_new(bh_result); +	if (create) { +		if (!test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) +			set_buffer_new(bh_result); + +		/* +		 * Need to update the i_size under the extent lock so buffered +		 * readers will get the updated i_size when we unlock. +		 */ +		if (start + len > i_size_read(inode)) +			i_size_write(inode, start + len); +	}  	free_extent_map(em); @@ -6360,12 +6369,48 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,  		 */  		ordered = btrfs_lookup_ordered_range(inode, lockstart,  						     lockend - lockstart + 1); -		if (!ordered) + +		/* +		 * We need to make sure there are no buffered pages in this +		 * range either, we could have raced between the invalidate in +		 * generic_file_direct_write and locking the extent.  The +		 * invalidate needs to happen so that reads after a write do not +		 * get stale data. +		 */ +		if (!ordered && (!writing || +		    !test_range_bit(&BTRFS_I(inode)->io_tree, +				    lockstart, lockend, EXTENT_UPTODATE, 0, +				    cached_state)))  			break; +  		unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,  				     &cached_state, GFP_NOFS); -		btrfs_start_ordered_extent(inode, ordered, 1); -		btrfs_put_ordered_extent(ordered); + +		if (ordered) { +			btrfs_start_ordered_extent(inode, ordered, 1); +			btrfs_put_ordered_extent(ordered); +		} else { +			/* Screw you mmap */ +			ret = filemap_write_and_wait_range(file->f_mapping, +							   lockstart, +							   lockend); +			if (ret) +				goto out; + +			/* +			 * If we found a page that couldn't be invalidated just +			 * fall back to buffered. +			 */ +			ret = invalidate_inode_pages2_range(file->f_mapping, +					lockstart >> PAGE_CACHE_SHIFT, +					lockend >> PAGE_CACHE_SHIFT); +			if (ret) { +				if (ret == -EBUSY) +					ret = 0; +				goto out; +			} +		} +  		cond_resched();  	}  |