xfs: handle zeroing in xfs_file_iomap_begin_delay
authorChristoph Hellwig <hch@lst.de>
Thu, 18 Oct 2018 06:19:26 +0000 (17:19 +1100)
committerDave Chinner <david@fromorbit.com>
Thu, 18 Oct 2018 06:19:26 +0000 (17:19 +1100)
We only need to allocate blocks for zeroing for reflink inodes,
and for we currently have a special case for reflink files in
the otherwise direct I/O path that I'd like to get rid of.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
fs/xfs/xfs_iomap.c

index 6320aca..9b572a1 100644 (file)
@@ -62,6 +62,21 @@ xfs_bmbt_to_iomap(
        iomap->dax_dev = xfs_find_daxdev_for_inode(VFS_I(ip));
 }
 
+static void
+xfs_hole_to_iomap(
+       struct xfs_inode        *ip,
+       struct iomap            *iomap,
+       xfs_fileoff_t           offset_fsb,
+       xfs_fileoff_t           end_fsb)
+{
+       iomap->addr = IOMAP_NULL_ADDR;
+       iomap->type = IOMAP_HOLE;
+       iomap->offset = XFS_FSB_TO_B(ip->i_mount, offset_fsb);
+       iomap->length = XFS_FSB_TO_B(ip->i_mount, end_fsb - offset_fsb);
+       iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
+       iomap->dax_dev = xfs_find_daxdev_for_inode(VFS_I(ip));
+}
+
 xfs_extlen_t
 xfs_eof_alignment(
        struct xfs_inode        *ip,
@@ -502,6 +517,7 @@ xfs_file_iomap_begin_delay(
        struct inode            *inode,
        loff_t                  offset,
        loff_t                  count,
+       unsigned                flags,
        struct iomap            *iomap)
 {
        struct xfs_inode        *ip = XFS_I(inode);
@@ -538,13 +554,23 @@ xfs_file_iomap_begin_delay(
                        goto out_unlock;
        }
 
+       end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
+
        eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &icur, &got);
-       if (!eof && got.br_startoff <= offset_fsb) {
-               if (xfs_is_reflink_inode(ip)) {
+       if (eof)
+               got.br_startoff = end_fsb; /* fake hole until the end */
+
+       if (got.br_startoff <= offset_fsb) {
+               /*
+                * For reflink files we may need a delalloc reservation when
+                * overwriting shared extents.   This includes zeroing of
+                * existing extents that contain data.
+                */
+               if (xfs_is_reflink_inode(ip) &&
+                   ((flags & IOMAP_WRITE) ||
+                    got.br_state != XFS_EXT_UNWRITTEN)) {
                        bool            shared;
 
-                       end_fsb = min(XFS_B_TO_FSB(mp, offset + count),
-                                       maxbytes_fsb);
                        xfs_trim_extent(&got, offset_fsb, end_fsb - offset_fsb);
                        error = xfs_reflink_reserve_cow(ip, &got, &shared);
                        if (error)
@@ -555,6 +581,11 @@ xfs_file_iomap_begin_delay(
                goto done;
        }
 
+       if (flags & IOMAP_ZERO) {
+               xfs_hole_to_iomap(ip, iomap, offset_fsb, got.br_startoff);
+               goto out_unlock;
+       }
+
        error = xfs_qm_dqattach_locked(ip, false);
        if (error)
                goto out_unlock;
@@ -1009,10 +1040,11 @@ xfs_file_iomap_begin(
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
-       if (((flags & (IOMAP_WRITE | IOMAP_DIRECT)) == IOMAP_WRITE) &&
+       if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && !(flags & IOMAP_DIRECT) &&
                        !IS_DAX(inode) && !xfs_get_extsz_hint(ip)) {
                /* Reserve delalloc blocks for regular writeback. */
-               return xfs_file_iomap_begin_delay(inode, offset, length, iomap);
+               return xfs_file_iomap_begin_delay(inode, offset, length, flags,
+                               iomap);
        }
 
        /*