diff options
Diffstat (limited to 'fs/ext4/extents.c')
| -rw-r--r-- | fs/ext4/extents.c | 98 | 
1 files changed, 88 insertions, 10 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 92266a2da7d6..fb0f99dc8c22 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2315,6 +2315,52 @@ static int ext4_fill_fiemap_extents(struct inode *inode,  	return err;  } +static int ext4_fill_es_cache_info(struct inode *inode, +				   ext4_lblk_t block, ext4_lblk_t num, +				   struct fiemap_extent_info *fieinfo) +{ +	ext4_lblk_t next, end = block + num - 1; +	struct extent_status es; +	unsigned char blksize_bits = inode->i_sb->s_blocksize_bits; +	unsigned int flags; +	int err; + +	while (block <= end) { +		next = 0; +		flags = 0; +		if (!ext4_es_lookup_extent(inode, block, &next, &es)) +			break; +		if (ext4_es_is_unwritten(&es)) +			flags |= FIEMAP_EXTENT_UNWRITTEN; +		if (ext4_es_is_delayed(&es)) +			flags |= (FIEMAP_EXTENT_DELALLOC | +				  FIEMAP_EXTENT_UNKNOWN); +		if (ext4_es_is_hole(&es)) +			flags |= EXT4_FIEMAP_EXTENT_HOLE; +		if (next == 0) +			flags |= FIEMAP_EXTENT_LAST; +		if (flags & (FIEMAP_EXTENT_DELALLOC| +			     EXT4_FIEMAP_EXTENT_HOLE)) +			es.es_pblk = 0; +		else +			es.es_pblk = ext4_es_pblock(&es); +		err = fiemap_fill_next_extent(fieinfo, +				(__u64)es.es_lblk << blksize_bits, +				(__u64)es.es_pblk << blksize_bits, +				(__u64)es.es_len << blksize_bits, +				flags); +		if (next == 0) +			break; +		block = next; +		if (err < 0) +			return err; +		if (err == 1) +			return 0; +	} +	return 0; +} + +  /*   * ext4_ext_determine_hole - determine hole around given block   * @inode:	inode we lookup in @@ -3813,8 +3859,8 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,  	 * illegal.  	 */  	if (ee_block != map->m_lblk || ee_len > map->m_len) { -#ifdef EXT4_DEBUG -		ext4_warning("Inode (%ld) finished: extent logical block %llu," +#ifdef CONFIG_EXT4_DEBUG +		ext4_warning(inode->i_sb, "Inode (%ld) finished: extent logical block %llu,"  			     " len %u; IO logical block %llu, len %u",  			     inode->i_ino, (unsigned long long)ee_block, ee_len,  			     (unsigned long long)map->m_lblk, map->m_len); @@ -5017,8 +5063,6 @@ static int ext4_find_delayed_extent(struct inode *inode,  	return next_del;  } -/* fiemap flags we can handle specified here */ -#define EXT4_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)  static int ext4_xattr_fiemap(struct inode *inode,  				struct fiemap_extent_info *fieinfo) @@ -5055,10 +5099,16 @@ static int ext4_xattr_fiemap(struct inode *inode,  	return (error < 0 ? error : 0);  } -int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, -		__u64 start, __u64 len) +static int _ext4_fiemap(struct inode *inode, +			struct fiemap_extent_info *fieinfo, +			__u64 start, __u64 len, +			int (*fill)(struct inode *, ext4_lblk_t, +				    ext4_lblk_t, +				    struct fiemap_extent_info *))  {  	ext4_lblk_t start_blk; +	u32 ext4_fiemap_flags = FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR; +  	int error = 0;  	if (ext4_has_inline_data(inode)) { @@ -5075,14 +5125,18 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,  		error = ext4_ext_precache(inode);  		if (error)  			return error; +		fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE;  	}  	/* fallback to generic here if not in extents fmt */ -	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) +	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) && +	    fill == ext4_fill_fiemap_extents)  		return generic_block_fiemap(inode, fieinfo, start, len,  			ext4_get_block); -	if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS)) +	if (fill == ext4_fill_es_cache_info) +		ext4_fiemap_flags &= FIEMAP_FLAG_XATTR; +	if (fiemap_check_flags(fieinfo, ext4_fiemap_flags))  		return -EBADR;  	if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) { @@ -5101,12 +5155,36 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,  		 * Walk the extent tree gathering extent information  		 * and pushing extents back to the user.  		 */ -		error = ext4_fill_fiemap_extents(inode, start_blk, -						 len_blks, fieinfo); +		error = fill(inode, start_blk, len_blks, fieinfo);  	}  	return error;  } +int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, +		__u64 start, __u64 len) +{ +	return _ext4_fiemap(inode, fieinfo, start, len, +			    ext4_fill_fiemap_extents); +} + +int ext4_get_es_cache(struct inode *inode, struct fiemap_extent_info *fieinfo, +		      __u64 start, __u64 len) +{ +	if (ext4_has_inline_data(inode)) { +		int has_inline; + +		down_read(&EXT4_I(inode)->xattr_sem); +		has_inline = ext4_has_inline_data(inode); +		up_read(&EXT4_I(inode)->xattr_sem); +		if (has_inline) +			return 0; +	} + +	return _ext4_fiemap(inode, fieinfo, start, len, +			    ext4_fill_es_cache_info); +} + +  /*   * ext4_access_path:   * Function to access the path buffer for marking it dirty.  |