Hi, This version of the patch implements Dave Chinner's suggestion, which simplifies it (since GFS2 is the only direct caller of __generic_block_fiemap). Background: If you have a very big sparse file with huge holes, when those holes are encountered, function __generic_block_fiemap iterates for every block with "start_blk++;". This is extremely slow, inefficient and time consuming. A simple command like: dd if=/dev/zero of=/mnt/point/filler-P bs=1 count=1 seek=1P will cause some file systems to run continuously for days or weeks given a filefrag command, even though the file contains only a single byte. I encountered it with GFS2. Sure, I can (and did) easily implement a GFS2-specific block_fiemap that detects and skips holes. But this patch extends the capability to other file systems as well: They need only write their own get_hole_size() function and call __generic_block_fiemap with it (plus the appropriate inode mutex locking). This patch just adds a hook in function __generic_block_fiemap to call a fs-specific function to return a hole size. That way, the function doesn't have to do a block-by-block search when a hole is encountered. This, of course, would be followed up with a GFS2 patch to take advantage of the new hook. Regards, Bob Peterson Red Hat File Systems Signed-off-by: Bob Peterson <rpeterso@xxxxxxxxxx> --- fs: Add hooks for get_hole_size to __generic_block_fiemap This patch adds a hook in function __generic_block_fiemap to call a fs-specific function to return a hole size. That way, the function doesn't have to do a block-by-block search when a hole is encountered. --- diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index e62e594..e93a3bd 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1936,7 +1936,7 @@ static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ret = 0; } else { ret = __generic_block_fiemap(inode, fieinfo, start, len, - gfs2_block_map); + gfs2_block_map, NULL); } gfs2_glock_dq_uninit(&gh); diff --git a/fs/ioctl.c b/fs/ioctl.c index 8ac3fad..1c97425 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -234,6 +234,7 @@ static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) * @start: where to start mapping in the inode * @len: how much space to map * @get_block: the fs's get_block function + * @get_hole_size: the fs's get_hole_size function * * This does FIEMAP for block based inodes. Basically it will just loop * through get_block until we hit the number of extents we want to map, or we @@ -249,7 +250,8 @@ static inline loff_t blk_to_logical(struct inode *inode, sector_t blk) int __generic_block_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, loff_t start, - loff_t len, get_block_t *get_block) + loff_t len, get_block_t *get_block, + get_hole_size_t *get_hole_size) { struct buffer_head map_bh; sector_t start_blk, last_blk; @@ -258,6 +260,7 @@ int __generic_block_fiemap(struct inode *inode, u32 flags = FIEMAP_EXTENT_MERGED; bool past_eof = false, whole_file = false; int ret = 0; + u64 holesize = 1; ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC); if (ret) @@ -297,7 +300,12 @@ int __generic_block_fiemap(struct inode *inode, /* HOLE */ if (!buffer_mapped(&map_bh)) { - start_blk++; + if (get_hole_size) { + holesize = get_hole_size(inode, start_blk); + BUG_ON(!holesize); + } + + start_blk += holesize; /* * We want to handle the case where there is an @@ -407,7 +415,8 @@ int generic_block_fiemap(struct inode *inode, { int ret; mutex_lock(&inode->i_mutex); - ret = __generic_block_fiemap(inode, fieinfo, start, len, get_block); + ret = __generic_block_fiemap(inode, fieinfo, start, len, get_block, + NULL); mutex_unlock(&inode->i_mutex); return ret; } diff --git a/include/linux/fs.h b/include/linux/fs.h index e11d60c..eef1777 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -65,6 +65,7 @@ extern int sysctl_protected_hardlinks; struct buffer_head; typedef int (get_block_t)(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); +typedef u64 (get_hole_size_t)(struct inode *inode, sector_t lblock); typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, ssize_t bytes, void *private); @@ -2545,7 +2546,8 @@ extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, extern int __generic_block_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, loff_t start, loff_t len, - get_block_t *get_block); + get_block_t *get_block, + get_hole_size_t *get_hole_size); extern int generic_block_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len, get_block_t *get_block); -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html