diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 66 | 
1 files changed, 65 insertions, 1 deletions
| diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 936c3137c646..7e8d8169779d 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2253,11 +2253,69 @@ static int add_pending_csums(struct btrfs_trans_handle *trans,  	return 0;  } +static int btrfs_find_new_delalloc_bytes(struct btrfs_inode *inode, +					 const u64 start, +					 const u64 len, +					 struct extent_state **cached_state) +{ +	u64 search_start = start; +	const u64 end = start + len - 1; + +	while (search_start < end) { +		const u64 search_len = end - search_start + 1; +		struct extent_map *em; +		u64 em_len; +		int ret = 0; + +		em = btrfs_get_extent(inode, NULL, 0, search_start, search_len); +		if (IS_ERR(em)) +			return PTR_ERR(em); + +		if (em->block_start != EXTENT_MAP_HOLE) +			goto next; + +		em_len = em->len; +		if (em->start < search_start) +			em_len -= search_start - em->start; +		if (em_len > search_len) +			em_len = search_len; + +		ret = set_extent_bit(&inode->io_tree, search_start, +				     search_start + em_len - 1, +				     EXTENT_DELALLOC_NEW, +				     NULL, cached_state, GFP_NOFS); +next: +		search_start = extent_map_end(em); +		free_extent_map(em); +		if (ret) +			return ret; +	} +	return 0; +} +  int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,  			      unsigned int extra_bits,  			      struct extent_state **cached_state)  {  	WARN_ON(PAGE_ALIGNED(end)); + +	if (start >= i_size_read(&inode->vfs_inode) && +	    !(inode->flags & BTRFS_INODE_PREALLOC)) { +		/* +		 * There can't be any extents following eof in this case so just +		 * set the delalloc new bit for the range directly. +		 */ +		extra_bits |= EXTENT_DELALLOC_NEW; +	} else { +		int ret; + +		ret = btrfs_find_new_delalloc_bytes(inode, start, +						    end + 1 - start, +						    cached_state); +		if (ret) +			return ret; +	} +  	return set_extent_delalloc(&inode->io_tree, start, end, extra_bits,  				   cached_state);  } @@ -9672,10 +9730,16 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,  		 * clear_offset by our extent size.  		 */  		clear_offset += ins.offset; -		btrfs_dec_block_group_reservations(fs_info, ins.objectid);  		last_alloc = ins.offset;  		trans = insert_prealloc_file_extent(trans, inode, &ins, cur_offset); +		/* +		 * Now that we inserted the prealloc extent we can finally +		 * decrement the number of reservations in the block group. +		 * If we did it before, we could race with relocation and have +		 * relocation miss the reserved extent, making it fail later. +		 */ +		btrfs_dec_block_group_reservations(fs_info, ins.objectid);  		if (IS_ERR(trans)) {  			ret = PTR_ERR(trans);  			btrfs_free_reserved_extent(fs_info, ins.objectid, | 
