diff options
| author | Yan, Zheng <zheng.yan@oracle.com> | 2009-11-12 09:34:21 +0000 | 
|---|---|---|
| committer | Chris Mason <chris.mason@oracle.com> | 2009-12-17 12:33:24 -0500 | 
| commit | c216775458a2ee345d9412a2770c2916acfb5d30 (patch) | |
| tree | 41a947a9d254aeeef40b7e42162d80646477f30a /fs/btrfs/ordered-data.c | |
| parent | 920bbbfb05c9fce22e088d20eb9dcb8f96342de9 (diff) | |
| download | olio-linux-3.10-c216775458a2ee345d9412a2770c2916acfb5d30.tar.xz olio-linux-3.10-c216775458a2ee345d9412a2770c2916acfb5d30.zip  | |
Btrfs: Fix disk_i_size update corner case
There are some cases file extents are inserted without involving
ordered struct. In these cases, we update disk_i_size directly,
without checking pending ordered extent and DELALLOC bit. This
patch extends btrfs_ordered_update_i_size() to handle these cases.
Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/ordered-data.c')
| -rw-r--r-- | fs/btrfs/ordered-data.c | 105 | 
1 files changed, 81 insertions, 24 deletions
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index 5799bc46a30..9b16073bb87 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -291,16 +291,16 @@ int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry)  /*   * remove an ordered extent from the tree.  No references are dropped - * but, anyone waiting on this extent is woken up. + * and you must wake_up entry->wait.  You must hold the tree mutex + * while you call this function.   */ -int btrfs_remove_ordered_extent(struct inode *inode, +static int __btrfs_remove_ordered_extent(struct inode *inode,  				struct btrfs_ordered_extent *entry)  {  	struct btrfs_ordered_inode_tree *tree;  	struct rb_node *node;  	tree = &BTRFS_I(inode)->ordered_tree; -	mutex_lock(&tree->mutex);  	node = &entry->rb_node;  	rb_erase(node, &tree->tree);  	tree->last = NULL; @@ -326,9 +326,26 @@ int btrfs_remove_ordered_extent(struct inode *inode,  	}  	spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock); +	return 0; +} + +/* + * remove an ordered extent from the tree.  No references are dropped + * but any waiters are woken. + */ +int btrfs_remove_ordered_extent(struct inode *inode, +				struct btrfs_ordered_extent *entry) +{ +	struct btrfs_ordered_inode_tree *tree; +	int ret; + +	tree = &BTRFS_I(inode)->ordered_tree; +	mutex_lock(&tree->mutex); +	ret = __btrfs_remove_ordered_extent(inode, entry);  	mutex_unlock(&tree->mutex);  	wake_up(&entry->wait); -	return 0; + +	return ret;  }  /* @@ -589,7 +606,7 @@ out:   * After an extent is done, call this to conditionally update the on disk   * i_size.  i_size is updated to cover any fully written part of the file.   */ -int btrfs_ordered_update_i_size(struct inode *inode, +int btrfs_ordered_update_i_size(struct inode *inode, u64 offset,  				struct btrfs_ordered_extent *ordered)  {  	struct btrfs_ordered_inode_tree *tree = &BTRFS_I(inode)->ordered_tree; @@ -597,18 +614,30 @@ int btrfs_ordered_update_i_size(struct inode *inode,  	u64 disk_i_size;  	u64 new_i_size;  	u64 i_size_test; +	u64 i_size = i_size_read(inode);  	struct rb_node *node; +	struct rb_node *prev = NULL;  	struct btrfs_ordered_extent *test; +	int ret = 1; + +	if (ordered) +		offset = entry_end(ordered);  	mutex_lock(&tree->mutex);  	disk_i_size = BTRFS_I(inode)->disk_i_size; +	/* truncate file */ +	if (disk_i_size > i_size) { +		BTRFS_I(inode)->disk_i_size = i_size; +		ret = 0; +		goto out; +	} +  	/*  	 * if the disk i_size is already at the inode->i_size, or  	 * this ordered extent is inside the disk i_size, we're done  	 */ -	if (disk_i_size >= inode->i_size || -	    ordered->file_offset + ordered->len <= disk_i_size) { +	if (disk_i_size == i_size || offset <= disk_i_size) {  		goto out;  	} @@ -616,8 +645,7 @@ int btrfs_ordered_update_i_size(struct inode *inode,  	 * we can't update the disk_isize if there are delalloc bytes  	 * between disk_i_size and  this ordered extent  	 */ -	if (test_range_bit(io_tree, disk_i_size, -			   ordered->file_offset + ordered->len - 1, +	if (test_range_bit(io_tree, disk_i_size, offset - 1,  			   EXTENT_DELALLOC, 0, NULL)) {  		goto out;  	} @@ -626,20 +654,32 @@ int btrfs_ordered_update_i_size(struct inode *inode,  	 * if we find an ordered extent then we can't update disk i_size  	 * yet  	 */ -	node = &ordered->rb_node; -	while (1) { -		node = rb_prev(node); -		if (!node) -			break; +	if (ordered) { +		node = rb_prev(&ordered->rb_node); +	} else { +		prev = tree_search(tree, offset); +		/* +		 * we insert file extents without involving ordered struct, +		 * so there should be no ordered struct cover this offset +		 */ +		if (prev) { +			test = rb_entry(prev, struct btrfs_ordered_extent, +					rb_node); +			BUG_ON(offset_in_entry(test, offset)); +		} +		node = prev; +	} +	while (node) {  		test = rb_entry(node, struct btrfs_ordered_extent, rb_node);  		if (test->file_offset + test->len <= disk_i_size)  			break; -		if (test->file_offset >= inode->i_size) +		if (test->file_offset >= i_size)  			break;  		if (test->file_offset >= disk_i_size)  			goto out; +		node = rb_prev(node);  	} -	new_i_size = min_t(u64, entry_end(ordered), i_size_read(inode)); +	new_i_size = min_t(u64, offset, i_size);  	/*  	 * at this point, we know we can safely update i_size to at least @@ -647,7 +687,14 @@ int btrfs_ordered_update_i_size(struct inode *inode,  	 * walk forward and see if ios from higher up in the file have  	 * finished.  	 */ -	node = rb_next(&ordered->rb_node); +	if (ordered) { +		node = rb_next(&ordered->rb_node); +	} else { +		if (prev) +			node = rb_next(prev); +		else +			node = rb_first(&tree->tree); +	}  	i_size_test = 0;  	if (node) {  		/* @@ -655,10 +702,10 @@ int btrfs_ordered_update_i_size(struct inode *inode,  		 * between our ordered extent and the next one.  		 */  		test = rb_entry(node, struct btrfs_ordered_extent, rb_node); -		if (test->file_offset > entry_end(ordered)) +		if (test->file_offset > offset)  			i_size_test = test->file_offset;  	} else { -		i_size_test = i_size_read(inode); +		i_size_test = i_size;  	}  	/* @@ -667,15 +714,25 @@ int btrfs_ordered_update_i_size(struct inode *inode,  	 * are no delalloc bytes in this area, it is safe to update  	 * disk_i_size to the end of the region.  	 */ -	if (i_size_test > entry_end(ordered) && -	    !test_range_bit(io_tree, entry_end(ordered), i_size_test - 1, -			   EXTENT_DELALLOC, 0, NULL)) { -		new_i_size = min_t(u64, i_size_test, i_size_read(inode)); +	if (i_size_test > offset && +	    !test_range_bit(io_tree, offset, i_size_test - 1, +			    EXTENT_DELALLOC, 0, NULL)) { +		new_i_size = min_t(u64, i_size_test, i_size);  	}  	BTRFS_I(inode)->disk_i_size = new_i_size; +	ret = 0;  out: +	/* +	 * we need to remove the ordered extent with the tree lock held +	 * so that other people calling this function don't find our fully +	 * processed ordered entry and skip updating the i_size +	 */ +	if (ordered) +		__btrfs_remove_ordered_extent(inode, ordered);  	mutex_unlock(&tree->mutex); -	return 0; +	if (ordered) +		wake_up(&ordered->wait); +	return ret;  }  /*  |