[PATCH][TRY #2] fs: Add hooks for get_hole_size to __generic_block_fiemap

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux