]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: introduce BMAPI_ZERO for allocating zeroed extents
authorDave Chinner <dchinner@redhat.com>
Wed, 17 Feb 2016 05:44:53 +0000 (16:44 +1100)
committerDave Chinner <david@fromorbit.com>
Wed, 17 Feb 2016 05:44:53 +0000 (16:44 +1100)
Source kernel commit 3fbbbea34bac049c0b5938dc065f7d8ee1ef7e67

To enable DAX to do atomic allocation of zeroed extents, we need to
drive the block zeroing deep into the allocator. Because
xfs_bmapi_write() can return merged extents on allocation that were
only partially allocated (i.e. requested range spans allocated and
hole regions, allocation into the hole was contiguous), we cannot
zero the extent returned from xfs_bmapi_write() as that can
overwrite existing data with zeros.

Hence we have to drive the extent zeroing into the allocation code,
prior to where we merge the extents into the BMBT and return the
resultant map. This means we need to propagate this need down to
the xfs_alloc_vextent() and issue the block zeroing at this point.

While this functionality is being introduced for DAX, there is no
reason why it is specific to DAX - we can per-zero blocks during the
allocation transaction on any type of device. It's just slow (and
usually slower than unwritten allocation and conversion) on
traditional block devices so doesn't tend to get used. We can,
however, hook hardware zeroing optimisations via sb_issue_zeroout()
to this operation, so it may be useful in future and hence the
"allocate zeroed blocks" API needs to be implementation neutral.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
include/libxfs.h
libxfs/libxfs_api_defs.h
libxfs/libxfs_io.h
libxfs/libxfs_priv.h
libxfs/rdwr.c
libxfs/util.c
libxfs/xfs_alloc.c
libxfs/xfs_alloc.h
libxfs/xfs_bmap.c
libxfs/xfs_bmap.h

index bd51df0d7418e9a2b27913605779393f1751f072..5e8f3d4fa5be5c575789b50fc1c9eac6fde97642 100644 (file)
@@ -139,7 +139,6 @@ extern int  libxfs_init (libxfs_init_t *);
 extern void    libxfs_destroy (void);
 extern int     libxfs_device_to_fd (dev_t);
 extern dev_t   libxfs_device_open (char *, int, int, int);
-extern void    libxfs_device_zero(struct xfs_buftarg *, xfs_daddr_t, uint);
 extern void    libxfs_device_close (dev_t);
 extern int     libxfs_device_alignment (void);
 extern void    libxfs_report(FILE *);
index e9fd9c82f5fa26c8fcd939f0d19db7975b638008..3a649e3e253cc5405701316e2f03f55f8d0c3a55 100644 (file)
@@ -73,6 +73,7 @@
 #define xfs_bunmapi                    libxfs_bunmapi
 #define xfs_bmbt_get_all               libxfs_bmbt_get_all
 #define xfs_rtfree_extent              libxfs_rtfree_extent
+#define xfs_zero_extent                        libxfs_zero_extent
 
 #define xfs_da_brelse                  libxfs_da_brelse
 #define xfs_da_hashname                        libxfs_da_hashname
index 86b18a0c598591eab25bfce41601fa69ec9a9e91..29ab5835a244ab451200a4ab803ff7eb9f7f075c 100644 (file)
@@ -212,6 +212,8 @@ extern int  libxfs_writebufr(struct xfs_buf *);
 extern int     libxfs_readbufr(struct xfs_buftarg *, xfs_daddr_t, xfs_buf_t *, int, int);
 extern int     libxfs_readbufr_map(struct xfs_buftarg *, struct xfs_buf *, int);
 
+extern int     libxfs_device_zero(struct xfs_buftarg *, xfs_daddr_t, uint);
+
 extern int libxfs_bhash_size;
 
 #define LIBXFS_BREAD   0x1
index e2884a28fa4dba8109f71264f2f6d847979142e8..2c5aba0c879c8384b455a5331261ed7f260b5ae3 100644 (file)
@@ -520,6 +520,9 @@ void xfs_verifier_error(struct xfs_buf *bp);
 /* xfs_rtalloc.c */
 int libxfs_rtfree_extent(struct xfs_trans *, xfs_rtblock_t, xfs_extlen_t);
 
+int libxfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb,
+                        xfs_off_t count_fsb);
+
 bool xfs_log_check_lsn(struct xfs_mount *, xfs_lsn_t);
 
 #endif /* __LIBXFS_INTERNAL_XFS_H__ */
index 2e298c2c73f00a4302c46c66b95252431fc1dc80..3522c261f41f4a715ef84965d98f7072729b1d1d 100644 (file)
@@ -67,7 +67,8 @@
 
 #define IO_BCOMPARE_CHECK
 
-void
+/* XXX: (dgc) Propagate errors, only exit if fail-on-error flag set */
+int
 libxfs_device_zero(struct xfs_buftarg *btp, xfs_daddr_t start, uint len)
 {
        xfs_off_t       start_offset, end_offset, offset;
@@ -109,6 +110,7 @@ libxfs_device_zero(struct xfs_buftarg *btp, xfs_daddr_t start, uint len)
                offset += bytes;
        }
        free(z);
+       return 0;
 }
 
 static void unmount_record(void *p)
index 90031fd74dedc3ff26ec73976cc1b84588a4fdcb..8882b2ab374fb7ff970da8f8354d0c549409318d 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include "libxfs_priv.h"
+#include "libxfs_io.h"
 #include "init.h"
 #include "xfs_fs.h"
 #include "xfs_shared.h"
@@ -33,6 +34,7 @@
 #include "xfs_trans_space.h"
 #include "xfs_ialloc.h"
 #include "xfs_alloc.h"
+#include "xfs_bit.h"
 
 /*
  * Calculate the worst case log unit reservation for a given superblock
@@ -770,3 +772,34 @@ xfs_log_check_lsn(
 
        return true;
 }
+
+static struct xfs_buftarg *
+xfs_find_bdev_for_inode(
+       struct xfs_inode        *ip)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+
+       if (XFS_IS_REALTIME_INODE(ip))
+               return mp->m_rtdev_targp;
+       return mp->m_ddev_targp;
+}
+
+static xfs_daddr_t
+xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb)
+{
+       if (XFS_IS_REALTIME_INODE(ip))
+                return XFS_FSB_TO_BB(ip->i_mount, fsb);
+       return XFS_FSB_TO_DADDR(ip->i_mount, (fsb));
+}
+
+int
+libxfs_zero_extent(
+       struct xfs_inode *ip,
+       xfs_fsblock_t   start_fsb,
+       xfs_off_t       count_fsb)
+{
+       xfs_daddr_t     sector = xfs_fsb_to_db(ip, start_fsb);
+       ssize_t         size = XFS_FSB_TO_BB(ip->i_mount, count_fsb);
+
+       return libxfs_device_zero(xfs_find_bdev_for_inode(ip), sector, size);
+}
index 12d59dfd9963b998d81e7f3a19d52b45eaeb1b6e..af40270af8b79a4d8fa0d5a1543c6529595bc251 100644 (file)
@@ -2508,7 +2508,7 @@ xfs_alloc_vextent(
                 * Try near allocation first, then anywhere-in-ag after
                 * the first a.g. fails.
                 */
-               if ((args->userdata  == XFS_ALLOC_INITIAL_USER_DATA) &&
+               if ((args->userdata & XFS_ALLOC_INITIAL_USER_DATA) &&
                    (mp->m_flags & XFS_MOUNT_32BITINODES)) {
                        args->fsbno = XFS_AGB_TO_FSB(mp,
                                        ((mp->m_agfrotor / rotorstep) %
@@ -2639,6 +2639,14 @@ xfs_alloc_vextent(
                XFS_AG_CHECK_DADDR(mp, XFS_FSB_TO_DADDR(mp, args->fsbno),
                        args->len);
 #endif
+
+               /* Zero the extent if we were asked to do so */
+               if (args->userdata & XFS_ALLOC_USERDATA_ZERO) {
+                       error = xfs_zero_extent(args->ip, args->fsbno, args->len);
+                       if (error)
+                               goto error0;
+               }
+
        }
        xfs_perag_put(args->pag);
        return 0;
index 071b28b814c110be0e2c63ca43df40d22eb8ccca..135eb3d24db7166bcafb659ab192d25f5ad6a104 100644 (file)
@@ -101,6 +101,7 @@ typedef struct xfs_alloc_arg {
        struct xfs_mount *mp;           /* file system mount point */
        struct xfs_buf  *agbp;          /* buffer for a.g. freelist header */
        struct xfs_perag *pag;          /* per-ag struct for this agno */
+       struct xfs_inode *ip;           /* for userdata zeroing method */
        xfs_fsblock_t   fsbno;          /* file system block number */
        xfs_agnumber_t  agno;           /* allocation group number */
        xfs_agblock_t   agbno;          /* allocation group-relative block # */
@@ -120,15 +121,16 @@ typedef struct xfs_alloc_arg {
        char            wasdel;         /* set if allocation was prev delayed */
        char            wasfromfl;      /* set if allocation is from freelist */
        char            isfl;           /* set if is freelist blocks - !acctg */
-       char            userdata;       /* set if this is user data */
+       char            userdata;       /* mask defining userdata treatment */
        xfs_fsblock_t   firstblock;     /* io first block allocated */
 } xfs_alloc_arg_t;
 
 /*
  * Defines for userdata
  */
-#define XFS_ALLOC_USERDATA             1       /* allocation is for user data*/
-#define XFS_ALLOC_INITIAL_USER_DATA    2       /* special case start of file */
+#define XFS_ALLOC_USERDATA             (1 << 0)/* allocation is for user data*/
+#define XFS_ALLOC_INITIAL_USER_DATA    (1 << 1)/* special case start of file */
+#define XFS_ALLOC_USERDATA_ZERO                (1 << 2)/* zero extent on allocation */
 
 xfs_extlen_t xfs_alloc_longest_free_extent(struct xfs_mount *mp,
                struct xfs_perag *pag, xfs_extlen_t need);
index 8e19b500a46b6250c2a32de26a7adcfd3933626b..a38549cbd41567505da493d6e33f0bea704984ef 100644 (file)
@@ -3795,8 +3795,13 @@ xfs_bmap_btalloc(
        args.wasdel = ap->wasdel;
        args.isfl = 0;
        args.userdata = ap->userdata;
-       if ((error = xfs_alloc_vextent(&args)))
+       if (ap->userdata & XFS_ALLOC_USERDATA_ZERO)
+               args.ip = ap->ip;
+
+       error = xfs_alloc_vextent(&args);
+       if (error)
                return error;
+
        if (tryagain && args.fsbno == NULLFSBLOCK) {
                /*
                 * Exact allocation failed. Now try with alignment
@@ -4295,11 +4300,14 @@ xfs_bmapi_allocate(
 
        /*
         * Indicate if this is the first user data in the file, or just any
-        * user data.
+        * user data. And if it is userdata, indicate whether it needs to
+        * be initialised to zero during allocation.
         */
        if (!(bma->flags & XFS_BMAPI_METADATA)) {
                bma->userdata = (bma->offset == 0) ?
                        XFS_ALLOC_INITIAL_USER_DATA : XFS_ALLOC_USERDATA;
+               if (bma->flags & XFS_BMAPI_ZERO)
+                       bma->userdata |= XFS_ALLOC_USERDATA_ZERO;
        }
 
        bma->minlen = (bma->flags & XFS_BMAPI_CONTIG) ? bma->length : 1;
@@ -4414,6 +4422,17 @@ xfs_bmapi_convert_unwritten(
        mval->br_state = (mval->br_state == XFS_EXT_UNWRITTEN)
                                ? XFS_EXT_NORM : XFS_EXT_UNWRITTEN;
 
+       /*
+        * Before insertion into the bmbt, zero the range being converted
+        * if required.
+        */
+       if (flags & XFS_BMAPI_ZERO) {
+               error = xfs_zero_extent(bma->ip, mval->br_startblock,
+                                       mval->br_blockcount);
+               if (error)
+                       return error;
+       }
+
        error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, &bma->idx,
                        &bma->cur, mval, bma->firstblock, bma->flist,
                        &tmp_logflags);
@@ -4507,6 +4526,18 @@ xfs_bmapi_write(
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 
+       /* zeroing is for currently only for data extents, not metadata */
+       ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
+                       (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO));
+       /*
+        * we can allocate unwritten extents or pre-zero allocated blocks,
+        * but it makes no sense to do both at once. This would result in
+        * zeroing the unwritten extent twice, but it still being an
+        * unwritten extent....
+        */
+       ASSERT((flags & (XFS_BMAPI_PREALLOC | XFS_BMAPI_ZERO)) !=
+                       (XFS_BMAPI_PREALLOC | XFS_BMAPI_ZERO));
+
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
             XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
index d3daf6d017af9bf14a928a19a54780f40f504172..baec27d1d1ccfa922d2a6b091a4a9ca89d0e1af2 100644 (file)
@@ -52,9 +52,9 @@ struct xfs_bmalloca {
        xfs_extlen_t            minleft; /* amount must be left after alloc */
        bool                    eof;    /* set if allocating past last extent */
        bool                    wasdel; /* replacing a delayed allocation */
-       bool                    userdata;/* set if is user data */
        bool                    aeof;   /* allocated space at eof */
        bool                    conv;   /* overwriting unwritten extents */
+       char                    userdata;/* userdata mask */
        int                     flags;
 };
 
@@ -109,6 +109,14 @@ typedef    struct xfs_bmap_free
  */
 #define XFS_BMAPI_CONVERT      0x040
 
+/*
+ * allocate zeroed extents - this requires all newly allocated user data extents
+ * to be initialised to zero. It will be ignored if XFS_BMAPI_METADATA is set.
+ * Use in conjunction with XFS_BMAPI_CONVERT to convert unwritten extents found
+ * during the allocation range to zeroed written extents.
+ */
+#define XFS_BMAPI_ZERO         0x080
+
 #define XFS_BMAPI_FLAGS \
        { XFS_BMAPI_ENTIRE,     "ENTIRE" }, \
        { XFS_BMAPI_METADATA,   "METADATA" }, \
@@ -116,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_ZERO,       "ZERO" }
 
 
 static inline int xfs_bmapi_aflag(int w)