diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 126 | 
1 files changed, 124 insertions, 2 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index fb9bd7832b6..0efdb65953c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1913,7 +1913,7 @@ static int btrfs_clean_io_failures(struct inode *inode, u64 start)  	private = 0;  	if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private, -			     (u64)-1, 1, EXTENT_DIRTY)) { +			     (u64)-1, 1, EXTENT_DIRTY, 0)) {  		ret = get_state_private(&BTRFS_I(inode)->io_failure_tree,  					start, &private_failure);  		if (ret == 0) { @@ -5280,6 +5280,128 @@ out:  	return em;  } +struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page, +					   size_t pg_offset, u64 start, u64 len, +					   int create) +{ +	struct extent_map *em; +	struct extent_map *hole_em = NULL; +	u64 range_start = start; +	u64 end; +	u64 found; +	u64 found_end; +	int err = 0; + +	em = btrfs_get_extent(inode, page, pg_offset, start, len, create); +	if (IS_ERR(em)) +		return em; +	if (em) { +		/* +		 * if our em maps to a hole, there might +		 * actually be delalloc bytes behind it +		 */ +		if (em->block_start != EXTENT_MAP_HOLE) +			return em; +		else +			hole_em = em; +	} + +	/* check to see if we've wrapped (len == -1 or similar) */ +	end = start + len; +	if (end < start) +		end = (u64)-1; +	else +		end -= 1; + +	em = NULL; + +	/* ok, we didn't find anything, lets look for delalloc */ +	found = count_range_bits(&BTRFS_I(inode)->io_tree, &range_start, +				 end, len, EXTENT_DELALLOC, 1); +	found_end = range_start + found; +	if (found_end < range_start) +		found_end = (u64)-1; + +	/* +	 * we didn't find anything useful, return +	 * the original results from get_extent() +	 */ +	if (range_start > end || found_end <= start) { +		em = hole_em; +		hole_em = NULL; +		goto out; +	} + +	/* adjust the range_start to make sure it doesn't +	 * go backwards from the start they passed in +	 */ +	range_start = max(start,range_start); +	found = found_end - range_start; + +	if (found > 0) { +		u64 hole_start = start; +		u64 hole_len = len; + +		em = alloc_extent_map(GFP_NOFS); +		if (!em) { +			err = -ENOMEM; +			goto out; +		} +		/* +		 * when btrfs_get_extent can't find anything it +		 * returns one huge hole +		 * +		 * make sure what it found really fits our range, and +		 * adjust to make sure it is based on the start from +		 * the caller +		 */ +		if (hole_em) { +			u64 calc_end = extent_map_end(hole_em); + +			if (calc_end <= start || (hole_em->start > end)) { +				free_extent_map(hole_em); +				hole_em = NULL; +			} else { +				hole_start = max(hole_em->start, start); +				hole_len = calc_end - hole_start; +			} +		} +		em->bdev = NULL; +		if (hole_em && range_start > hole_start) { +			/* our hole starts before our delalloc, so we +			 * have to return just the parts of the hole +			 * that go until  the delalloc starts +			 */ +			em->len = min(hole_len, +				      range_start - hole_start); +			em->start = hole_start; +			em->orig_start = hole_start; +			/* +			 * don't adjust block start at all, +			 * it is fixed at EXTENT_MAP_HOLE +			 */ +			em->block_start = hole_em->block_start; +			em->block_len = hole_len; +		} else { +			em->start = range_start; +			em->len = found; +			em->orig_start = range_start; +			em->block_start = EXTENT_MAP_DELALLOC; +			em->block_len = found; +		} +	} else if (hole_em) { +		return hole_em; +	} +out: + +	free_extent_map(hole_em); +	if (err) { +		free_extent_map(em); +		return ERR_PTR(err); +	} +	return em; +} +  static struct extent_map *btrfs_new_extent_direct(struct inode *inode,  						  u64 start, u64 len)  { @@ -6102,7 +6224,7 @@ out:  static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,  		__u64 start, __u64 len)  { -	return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent); +	return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent_fiemap);  }  int btrfs_readpage(struct file *file, struct page *page)  |