xfs
[Top] [All Lists]

[PATCH 40/71] xfs: report shared extents through the iomap interface

To: david@xxxxxxxxxxxxx, darrick.wong@xxxxxxxxxx
Subject: [PATCH 40/71] xfs: report shared extents through the iomap interface
From: "Darrick J. Wong" <darrick.wong@xxxxxxxxxx>
Date: Thu, 25 Aug 2016 16:36:17 -0700
Cc: linux-xfs@xxxxxxxxxxxxxxx, xfs@xxxxxxxxxxx
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <147216791538.867.12413509832420924168.stgit@xxxxxxxxxxxxxxxx>
References: <147216791538.867.12413509832420924168.stgit@xxxxxxxxxxxxxxxx>
User-agent: StGit/0.17.1-dirty
Report shared extents through the iomap interface so that FIEMAP
reports shared blocks accurately.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 fs/xfs/xfs_iomap.c   |   19 ++++++++++++---
 fs/xfs/xfs_iomap.h   |    2 +-
 fs/xfs/xfs_pnfs.c    |    2 +-
 fs/xfs/xfs_reflink.c |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/xfs_reflink.h |    3 ++
 5 files changed, 82 insertions(+), 6 deletions(-)


diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 6065bde..130010a 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -987,10 +987,13 @@ void
 xfs_bmbt_to_iomap(
        struct xfs_inode        *ip,
        struct iomap            *iomap,
-       struct xfs_bmbt_irec    *imap)
+       struct xfs_bmbt_irec    *imap,
+       bool                    is_shared)
 {
        struct xfs_mount        *mp = ip->i_mount;
 
+       if (is_shared)
+               iomap->flags |= IOMAP_F_SHARED;
        if (imap->br_startblock == HOLESTARTBLOCK) {
                iomap->blkno = IOMAP_NULL_BLOCK;
                iomap->type = IOMAP_HOLE;
@@ -1028,6 +1031,7 @@ xfs_file_iomap_begin(
        struct xfs_mount        *mp = ip->i_mount;
        struct xfs_bmbt_irec    imap;
        xfs_fileoff_t           offset_fsb, end_fsb;
+       bool                    shared, trimmed;
        int                     nimaps = 1, error = 0;
 
        if (XFS_FORCED_SHUTDOWN(mp))
@@ -1053,7 +1057,14 @@ xfs_file_iomap_begin(
        }
 
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
-                              &nimaps, XFS_BMAPI_ENTIRE);
+                              &nimaps, 0);
+       if (error) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               return error;
+       }
+
+       /* Trim the mapping to the nearest shared extent boundary. */
+       error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed);
        if (error) {
                xfs_iunlock(ip, XFS_ILOCK_EXCL);
                return error;
@@ -1095,7 +1106,7 @@ xfs_file_iomap_begin(
                trace_xfs_iomap_found(ip, offset, length, 0, &imap);
        }
 
-       xfs_bmbt_to_iomap(ip, iomap, &imap);
+       xfs_bmbt_to_iomap(ip, iomap, &imap, shared);
        return 0;
 }
 
@@ -1193,7 +1204,7 @@ out_unlock:
 
        if (!error) {
                ASSERT(nimaps);
-               xfs_bmbt_to_iomap(ip, iomap, &imap);
+               xfs_bmbt_to_iomap(ip, iomap, &imap, false);
        }
 
        return error;
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index 52f9c99..c04bf99 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -34,7 +34,7 @@ int xfs_iomap_cow_delay(struct xfs_inode *, xfs_off_t, size_t,
                        struct xfs_bmbt_irec *);
 
 void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
-               struct xfs_bmbt_irec *);
+               struct xfs_bmbt_irec *, bool);
 
 extern struct iomap_ops xfs_iomap_ops;
 extern struct iomap_ops xfs_xattr_iomap_ops;
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index 93a7aaf..5519e4b 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -188,7 +188,7 @@ xfs_fs_map_blocks(
        }
        xfs_iunlock(ip, XFS_IOLOCK_EXCL);
 
-       xfs_bmbt_to_iomap(ip, iomap, &imap);
+       xfs_bmbt_to_iomap(ip, iomap, &imap, false);
        *device_generation = mp->m_generation;
        return error;
 out_unlock:
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 82d66a4..885ec61 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -179,6 +179,68 @@ xfs_reflink_find_shared(
        return error;
 }
 
+/*
+ * Trim the mapping to the next block where there's a change in the
+ * shared/unshared status.  More specifically, this means that we
+ * find the lowest-numbered extent of shared blocks that coincides with
+ * the given block mapping.  If the shared extent overlaps the start of
+ * the mapping, trim the mapping to the end of the shared extent.  If
+ * the shared region intersects the mapping, trim the mapping to the
+ * start of the shared extent.  If there are no shared regions that
+ * overlap, just return the original extent.
+ */
+int
+xfs_reflink_trim_around_shared(
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *irec,
+       bool                    *shared,
+       bool                    *trimmed)
+{
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           fbno;
+       xfs_extlen_t            flen;
+       int                     error = 0;
+
+       /* Holes, unwritten, and delalloc extents cannot be shared */
+       if (!xfs_is_reflink_inode(ip) ||
+           ISUNWRITTEN(irec) ||
+           irec->br_startblock == HOLESTARTBLOCK ||
+           irec->br_startblock == DELAYSTARTBLOCK) {
+               *shared = false;
+               return 0;
+       }
+
+       trace_xfs_reflink_trim_around_shared(ip, irec);
+
+       agno = XFS_FSB_TO_AGNO(ip->i_mount, irec->br_startblock);
+       agbno = XFS_FSB_TO_AGBNO(ip->i_mount, irec->br_startblock);
+       aglen = irec->br_blockcount;
+
+       error = xfs_reflink_find_shared(ip->i_mount, agno, agbno,
+                       aglen, &fbno, &flen, true);
+       if (error)
+               return error;
+
+       *shared = *trimmed = false;
+       if (flen == 0) {
+               /* No shared blocks at all. */
+               return 0;
+       } else if (fbno == agbno) {
+               /* The start of this extent is shared. */
+               irec->br_blockcount = flen;
+               *shared = true;
+               *trimmed = true;
+               return 0;
+       } else {
+               /* There's a shared extent midway through this extent. */
+               irec->br_blockcount = fbno - agbno;
+               *trimmed = true;
+               return 0;
+       }
+}
+
 /* Find the shared ranges under an irec, and set up delalloc extents. */
 static int
 xfs_reflink_reserve_cow_extent(
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
index 0ffabfb..e0bad68 100644
--- a/fs/xfs/xfs_reflink.h
+++ b/fs/xfs/xfs_reflink.h
@@ -23,6 +23,9 @@
 extern int xfs_reflink_find_shared(struct xfs_mount *mp, xfs_agnumber_t agno,
                xfs_agblock_t agbno, xfs_extlen_t aglen, xfs_agblock_t *fbno,
                xfs_extlen_t *flen, bool find_maximal);
+extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip,
+               struct xfs_bmbt_irec *irec, bool *shared, bool *trimmed);
+
 extern int xfs_reflink_reserve_cow_range(struct xfs_inode *ip,
                xfs_fileoff_t offset_fsb, xfs_fileoff_t end_fsb);
 extern bool xfs_reflink_is_cow_pending(struct xfs_inode *ip, xfs_off_t offset);

<Prev in Thread] Current Thread [Next in Thread>