xfs
[Top] [All Lists]

[PATCH 43/58] xfs: map an inode's offset to an exact physical block

To: david@xxxxxxxxxxxxx, darrick.wong@xxxxxxxxxx
Subject: [PATCH 43/58] xfs: map an inode's offset to an exact physical block
From: "Darrick J. Wong" <darrick.wong@xxxxxxxxxx>
Date: Tue, 06 Oct 2015 21:59:52 -0700
Cc: linux-fsdevel@xxxxxxxxxxxxxxx, xfs@xxxxxxxxxxx
Delivered-to: xfs@xxxxxxxxxxx
In-reply-to: <20151007045443.30457.47038.stgit@xxxxxxxxxxxxxxxx>
References: <20151007045443.30457.47038.stgit@xxxxxxxxxxxxxxxx>
User-agent: StGit/0.17.1-dirty
Teach the bmap routine to know how to map a range of file blocks to a
specific range of physical blocks, instead of simply allocating fresh
blocks.  This enables reflink to map a file to blocks that are already
in use.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 fs/xfs/libxfs/xfs_bmap.c |   64 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_bmap.h |    9 ++++++
 2 files changed, 72 insertions(+), 1 deletion(-)


diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index f6812ba..5f457b4 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -4003,6 +4003,56 @@ xfs_bmap_btalloc(
 }
 
 /*
+ * For a remap operation, just "allocate" an extent at the address that the
+ * caller passed in, and ensure that the AGFL is the right size.  The caller
+ * will then map the "allocated" extent into the file somewhere.
+ */
+static int
+xfs_bmap_remap(
+       struct xfs_bmalloca     *ap)
+{
+       struct xfs_trans        *tp = ap->tp;
+       struct xfs_mount        *mp = tp->t_mountp;
+       xfs_agblock_t           bno;
+       struct xfs_alloc_arg    args;
+       int                     error;
+
+       /*
+        * validate that the block number is legal - the enables us to detect
+        * and handle a silent filesystem corruption rather than crashing.
+        */
+       memset(&args, 0, sizeof(struct xfs_alloc_arg));
+       args.tp = ap->tp;
+       args.mp = ap->tp->t_mountp;
+       bno = *ap->firstblock;
+       args.agno = XFS_FSB_TO_AGNO(mp, bno);
+       if (args.agno >= mp->m_sb.sb_agcount)
+               return -EFSCORRUPTED;
+
+       args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
+       if (args.agbno >= mp->m_sb.sb_agblocks)
+               return -EFSCORRUPTED;
+
+       /* "Allocate" the extent from the range we passed in. */
+       trace_xfs_reflink_relink_blocks(ap->ip, *ap->firstblock,
+                       ap->length);
+       ap->blkno = bno;
+       ap->ip->i_d.di_nblocks += ap->length;
+       xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
+
+       /* Fix the freelist, like a real allocator does. */
+
+       args.pag = xfs_perag_get(args.mp, args.agno);
+       ASSERT(args.pag);
+
+       error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
+       if (error)
+               goto error0;
+error0:
+       xfs_perag_put(args.pag);
+       return error;
+}
+/*
  * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
  * It figures out where to ask the underlying allocator to put the new extent.
  */
@@ -4010,6 +4060,8 @@ STATIC int
 xfs_bmap_alloc(
        struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
 {
+       if (ap->flags & XFS_BMAPI_REMAP)
+               return xfs_bmap_remap(ap);
        if (XFS_IS_REALTIME_INODE(ap->ip) && ap->userdata)
                return xfs_bmap_rtalloc(ap);
        return xfs_bmap_btalloc(ap);
@@ -4694,6 +4746,12 @@ xfs_bmapi_write(
        ASSERT(len > 0);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+       if (whichfork == XFS_ATTR_FORK)
+               ASSERT(!(flags & XFS_BMAPI_REMAP));
+       if (flags & XFS_BMAPI_REMAP) {
+               ASSERT(!(flags & XFS_BMAPI_PREALLOC));
+               ASSERT(!(flags & XFS_BMAPI_CONVERT));
+       }
 
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -4743,6 +4801,12 @@ xfs_bmapi_write(
                wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
 
                /*
+                * Make sure we only reflink into a hole.
+                */
+               if (flags & XFS_BMAPI_REMAP)
+                       ASSERT(inhole);
+
+               /*
                 * First, deal with the hole before the allocated space
                 * that we found, if any.
                 */
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index 59f26cf..78a56b69 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -110,6 +110,12 @@ typedef    struct xfs_bmap_free
  * from written to unwritten, otherwise convert from unwritten to written.
  */
 #define XFS_BMAPI_CONVERT      0x040
+/*
+ * Map the inode offset to the block given in ap->firstblock.  Primarily
+ * used for reflink.  The range must be in a hole, and this flag cannot be
+ * turned on with PREALLOC or CONVERT, and cannot be used on the attr fork.
+ */
+#define XFS_BMAPI_REMAP                0x100
 
 #define XFS_BMAPI_FLAGS \
        { XFS_BMAPI_ENTIRE,     "ENTIRE" }, \
@@ -118,7 +124,8 @@ typedef     struct xfs_bmap_free
        { XFS_BMAPI_PREALLOC,   "PREALLOC" }, \
        { XFS_BMAPI_IGSTATE,    "IGSTATE" }, \
        { XFS_BMAPI_CONTIG,     "CONTIG" }, \
-       { XFS_BMAPI_CONVERT,    "CONVERT" }
+       { XFS_BMAPI_CONVERT,    "CONVERT" }, \
+       { XFS_BMAPI_REMAP,      "REMAP" }
 
 
 static inline int xfs_bmapi_aflag(int w)

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