diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 73 | 
1 files changed, 58 insertions, 15 deletions
| diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f6ab6f5e635a..d8bb0dbc4941 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -830,7 +830,7 @@ static noinline int cow_file_range(struct inode *inode,  	if (IS_ERR(trans)) {  		extent_clear_unlock_delalloc(inode,  			     &BTRFS_I(inode)->io_tree, -			     start, end, NULL, +			     start, end, locked_page,  			     EXTENT_CLEAR_UNLOCK_PAGE |  			     EXTENT_CLEAR_UNLOCK |  			     EXTENT_CLEAR_DELALLOC | @@ -963,7 +963,7 @@ out:  out_unlock:  	extent_clear_unlock_delalloc(inode,  		     &BTRFS_I(inode)->io_tree, -		     start, end, NULL, +		     start, end, locked_page,  		     EXTENT_CLEAR_UNLOCK_PAGE |  		     EXTENT_CLEAR_UNLOCK |  		     EXTENT_CLEAR_DELALLOC | @@ -986,8 +986,10 @@ static noinline void async_cow_start(struct btrfs_work *work)  	compress_file_range(async_cow->inode, async_cow->locked_page,  			    async_cow->start, async_cow->end, async_cow,  			    &num_added); -	if (num_added == 0) +	if (num_added == 0) { +		btrfs_add_delayed_iput(async_cow->inode);  		async_cow->inode = NULL; +	}  }  /* @@ -1020,6 +1022,8 @@ 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) +		btrfs_add_delayed_iput(async_cow->inode);  	kfree(async_cow);  } @@ -1038,7 +1042,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,  	while (start < end) {  		async_cow = kmalloc(sizeof(*async_cow), GFP_NOFS);  		BUG_ON(!async_cow); /* -ENOMEM */ -		async_cow->inode = inode; +		async_cow->inode = igrab(inode);  		async_cow->root = root;  		async_cow->locked_page = locked_page;  		async_cow->start = start; @@ -1136,8 +1140,18 @@ static noinline int run_delalloc_nocow(struct inode *inode,  	u64 ino = btrfs_ino(inode);  	path = btrfs_alloc_path(); -	if (!path) +	if (!path) { +		extent_clear_unlock_delalloc(inode, +			     &BTRFS_I(inode)->io_tree, +			     start, end, locked_page, +			     EXTENT_CLEAR_UNLOCK_PAGE | +			     EXTENT_CLEAR_UNLOCK | +			     EXTENT_CLEAR_DELALLOC | +			     EXTENT_CLEAR_DIRTY | +			     EXTENT_SET_WRITEBACK | +			     EXTENT_END_WRITEBACK);  		return -ENOMEM; +	}  	nolock = btrfs_is_free_space_inode(root, inode); @@ -1147,6 +1161,15 @@ static noinline int run_delalloc_nocow(struct inode *inode,  		trans = btrfs_join_transaction(root);  	if (IS_ERR(trans)) { +		extent_clear_unlock_delalloc(inode, +			     &BTRFS_I(inode)->io_tree, +			     start, end, locked_page, +			     EXTENT_CLEAR_UNLOCK_PAGE | +			     EXTENT_CLEAR_UNLOCK | +			     EXTENT_CLEAR_DELALLOC | +			     EXTENT_CLEAR_DIRTY | +			     EXTENT_SET_WRITEBACK | +			     EXTENT_END_WRITEBACK);  		btrfs_free_path(path);  		return PTR_ERR(trans);  	} @@ -1327,8 +1350,11 @@ out_check:  	}  	btrfs_release_path(path); -	if (cur_offset <= end && cow_start == (u64)-1) +	if (cur_offset <= end && cow_start == (u64)-1) {  		cow_start = cur_offset; +		cur_offset = end; +	} +  	if (cow_start != (u64)-1) {  		ret = cow_file_range(inode, locked_page, cow_start, end,  				     page_started, nr_written, 1); @@ -1347,6 +1373,17 @@ error:  	if (!ret)  		ret = err; +	if (ret && cur_offset < end) +		extent_clear_unlock_delalloc(inode, +			     &BTRFS_I(inode)->io_tree, +			     cur_offset, end, locked_page, +			     EXTENT_CLEAR_UNLOCK_PAGE | +			     EXTENT_CLEAR_UNLOCK | +			     EXTENT_CLEAR_DELALLOC | +			     EXTENT_CLEAR_DIRTY | +			     EXTENT_SET_WRITEBACK | +			     EXTENT_END_WRITEBACK); +  	btrfs_free_path(path);  	return ret;  } @@ -1361,20 +1398,23 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,  	int ret;  	struct btrfs_root *root = BTRFS_I(inode)->root; -	if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) +	if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW) {  		ret = run_delalloc_nocow(inode, locked_page, start, end,  					 page_started, 1, nr_written); -	else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC) +	} else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC) {  		ret = run_delalloc_nocow(inode, locked_page, start, end,  					 page_started, 0, nr_written); -	else if (!btrfs_test_opt(root, COMPRESS) && -		 !(BTRFS_I(inode)->force_compress) && -		 !(BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS)) +	} else if (!btrfs_test_opt(root, COMPRESS) && +		   !(BTRFS_I(inode)->force_compress) && +		   !(BTRFS_I(inode)->flags & BTRFS_INODE_COMPRESS)) {  		ret = cow_file_range(inode, locked_page, start, end,  				      page_started, nr_written, 1); -	else +	} else { +		set_bit(BTRFS_INODE_HAS_ASYNC_EXTENT, +			&BTRFS_I(inode)->runtime_flags);  		ret = cow_file_range_async(inode, locked_page, start, end,  					   page_started, nr_written); +	}  	return ret;  } @@ -7054,10 +7094,13 @@ static void fixup_inode_flags(struct inode *dir, struct inode *inode)  	else  		b_inode->flags &= ~BTRFS_INODE_NODATACOW; -	if (b_dir->flags & BTRFS_INODE_COMPRESS) +	if (b_dir->flags & BTRFS_INODE_COMPRESS) {  		b_inode->flags |= BTRFS_INODE_COMPRESS; -	else -		b_inode->flags &= ~BTRFS_INODE_COMPRESS; +		b_inode->flags &= ~BTRFS_INODE_NOCOMPRESS; +	} else { +		b_inode->flags &= ~(BTRFS_INODE_COMPRESS | +				    BTRFS_INODE_NOCOMPRESS); +	}  }  static int btrfs_rename(struct inode *old_dir, struct dentry *old_dentry, | 
