Hi,

This patch detects the new holesize bit in block_map requests. If a
hole is found during fiemap, it figures out the size of the hole
based on the current metapath information. Since the metapath only
represents a section of the file, it can only extrapolate to a
certain size based on the current metapath buffers. Therefore,
fiemap may call blockmap several times to get the hole size.
The hole size is determined by a new function.

Regards,

Bob Peterson
Red Hat File Systems

Signed-off-by: Bob Peterson <rpete...@redhat.com> 
---
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index f0b945a..edd7ed6 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -587,6 +587,62 @@ static int gfs2_bmap_alloc(struct inode *inode, const 
sector_t lblock,
 }
 
 /**
+ * hole_size - figure out the size of a hole
+ * @ip: The inode
+ * @lblock: The logical starting block number
+ * @mp: The metapath
+ *
+ * Returns: The hole size in bytes
+ *
+ */
+static u64 hole_size(struct inode *inode, sector_t lblock,
+                    struct metapath *mp)
+{
+       struct gfs2_inode *ip = GFS2_I(inode);
+       struct gfs2_sbd *sdp = GFS2_SB(inode);
+       unsigned int end_of_metadata = ip->i_height - 1;
+       u64 factor = 1;
+       int hgt = end_of_metadata;
+       u64 holesz = 0, holestep;
+       const __be64 *first, *end, *ptr;
+       const struct buffer_head *bh;
+       u64 isize = i_size_read(inode);
+       int zeroptrs;
+       struct metapath mp_eof;
+
+       /* Get a metapath to the very last byte */
+       find_metapath(sdp, (isize - 1) >> inode->i_blkbits, &mp_eof,
+                     ip->i_height);
+       for (hgt = end_of_metadata; hgt >= 0; hgt--) {
+               bh = mp->mp_bh[hgt];
+               if (bh) {
+                       zeroptrs = 0;
+                       first = metapointer(hgt, mp);
+                       end = (const __be64 *)(bh->b_data + bh->b_size);
+
+                       for (ptr = first; ptr < end; ptr++) {
+                               if (*ptr)
+                                       break;
+                               else
+                                       zeroptrs++;
+                       }
+               } else {
+                       zeroptrs = sdp->sd_inptrs;
+               }
+               holestep = min(factor * zeroptrs,
+                              isize - (lblock + (zeroptrs * holesz)));
+               holesz += holestep;
+               if (lblock + holesz >= isize)
+                       return holesz << inode->i_blkbits;
+
+               factor *= sdp->sd_inptrs;
+               if (hgt && (mp->mp_list[hgt - 1] < mp_eof.mp_list[hgt - 1]))
+                       (mp->mp_list[hgt - 1])++;
+       }
+       return holesz << inode->i_blkbits;
+}
+
+/**
  * gfs2_block_map - Map a block from an inode to a disk block
  * @inode: The inode
  * @lblock: The logical block number
@@ -645,11 +701,17 @@ int gfs2_block_map(struct inode *inode, sector_t lblock,
        ret = lookup_metapath(ip, &mp);
        if (ret < 0)
                goto out;
-       if (ret != ip->i_height)
+       if (ret != ip->i_height) {
+               if (test_clear_buffer_holesize(bh_map))
+                       bh_map->b_size = hole_size(inode, lblock, &mp);
                goto do_alloc;
+       }
        ptr = metapointer(ip->i_height - 1, &mp);
-       if (*ptr == 0)
+       if (*ptr == 0) {
+               if (test_clear_buffer_holesize(bh_map))
+                       bh_map->b_size = hole_size(inode, lblock, &mp);
                goto do_alloc;
+       }
        map_bh(bh_map, inode->i_sb, be64_to_cpu(*ptr));
        bh = mp.mp_bh[ip->i_height - 1];
        len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob);

Reply via email to