]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: Introduce error injection to allocate only minlen size extents for files
authorChandan Babu R <chandanrlinux@gmail.com>
Mon, 5 Apr 2021 22:36:56 +0000 (18:36 -0400)
committerEric Sandeen <sandeen@sandeen.net>
Mon, 5 Apr 2021 22:36:56 +0000 (18:36 -0400)
Source kernel commit: 301519674699aa9b80a15b2b2165e08532b176e6

This commit adds XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT error tag which
helps userspace test programs to get xfs_bmap_btalloc() to always
allocate minlen sized extents.

This is required for test programs which need a guarantee that minlen
extents allocated for a file do not get merged with their existing
neighbours in the inode's BMBT. "Inode fork extent overflow check" for
Directories, Xattrs and extension of realtime inodes need this since the
file offset at which the extents are being allocated cannot be
explicitly controlled from userspace.

One way to use this error tag is to,
1. Consume all of the free space by sequentially writing to a file.
2. Punch alternate blocks of the file. This causes CNTBT to contain
sufficient number of one block sized extent records.
3. Inject XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT error tag.
After step 3, xfs_bmap_btalloc() will issue space allocation
requests for minlen sized extents only.

ENOSPC error code is returned to userspace when there aren't any "one
block sized" extents left in any of the AGs.

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Chandan Babu R <chandanrlinux@gmail.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
io/inject.c
libxfs/xfs_alloc.c
libxfs/xfs_alloc.h
libxfs/xfs_bmap.c
libxfs/xfs_errortag.h

index 9e3d5ad49d3f8fc107ff90744ed5ccf6f4164c24..9a401a1e0096f3b090c29a18731bbf8f04357fad 100644 (file)
@@ -56,6 +56,7 @@ error_tag(char *name)
                { XFS_ERRTAG_IUNLINK_FALLBACK,          "iunlink_fallback" },
                { XFS_ERRTAG_BUF_IOERROR,               "buf_ioerror" },
                { XFS_ERRTAG_REDUCE_MAX_IEXTENTS,       "reduce_max_iextents" },
+               { XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT,  "bmap_alloc_minlen_extent" },
                { XFS_ERRTAG_MAX,                       NULL }
        };
        int     count;
index 0bef9f0d8b4aff44a810436b240d2f59764f66f7..63e15bb9ea33e8d21f27efb16557b57bd97761b2 100644 (file)
@@ -2470,6 +2470,47 @@ xfs_defer_agfl_block(
        xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_AGFL_FREE, &new->xefi_list);
 }
 
+#ifdef DEBUG
+/*
+ * Check if an AGF has a free extent record whose length is equal to
+ * args->minlen.
+ */
+STATIC int
+xfs_exact_minlen_extent_available(
+       struct xfs_alloc_arg    *args,
+       struct xfs_buf          *agbp,
+       int                     *stat)
+{
+       struct xfs_btree_cur    *cnt_cur;
+       xfs_agblock_t           fbno;
+       xfs_extlen_t            flen;
+       int                     error = 0;
+
+       cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, agbp,
+                       args->agno, XFS_BTNUM_CNT);
+       error = xfs_alloc_lookup_ge(cnt_cur, 0, args->minlen, stat);
+       if (error)
+               goto out;
+
+       if (*stat == 0) {
+               error = -EFSCORRUPTED;
+               goto out;
+       }
+
+       error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen, stat);
+       if (error)
+               goto out;
+
+       if (*stat == 1 && flen != args->minlen)
+               *stat = 0;
+
+out:
+       xfs_btree_del_cursor(cnt_cur, error);
+
+       return error;
+}
+#endif
+
 /*
  * Decide whether to use this allocation group for this allocation.
  * If so, fix up the btree freelist's size.
@@ -2541,6 +2582,15 @@ xfs_alloc_fix_freelist(
        if (!xfs_alloc_space_available(args, need, flags))
                goto out_agbp_relse;
 
+#ifdef DEBUG
+       if (args->alloc_minlen_only) {
+               int stat;
+
+               error = xfs_exact_minlen_extent_available(args, agbp, &stat);
+               if (error || !stat)
+                       goto out_agbp_relse;
+       }
+#endif
        /*
         * Make the freelist shorter if it's too long.
         *
index 6c22b12176b8b65153c6405c906d1a70876c68b4..a4427c5775c252e2672dc36200bf359b06dbb325 100644 (file)
@@ -75,6 +75,9 @@ typedef struct xfs_alloc_arg {
        char            wasfromfl;      /* set if allocation is from freelist */
        struct xfs_owner_info   oinfo;  /* owner of blocks being allocated */
        enum xfs_ag_resv_type   resv;   /* block reservation to use */
+#ifdef DEBUG
+       bool            alloc_minlen_only; /* allocate exact minlen extent */
+#endif
 } xfs_alloc_arg_t;
 
 /*
index 6a9485a64e1af85cbdbfc193c706a9af16d84fc9..9209e4ff9631b61681ac157bbd6beb302301313d 100644 (file)
@@ -3545,34 +3545,101 @@ xfs_bmap_process_allocated_extent(
        xfs_bmap_btalloc_accounting(ap, args);
 }
 
-STATIC int
-xfs_bmap_btalloc(
-       struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
+#ifdef DEBUG
+static int
+xfs_bmap_exact_minlen_extent_alloc(
+       struct xfs_bmalloca     *ap)
 {
-       xfs_mount_t     *mp;            /* mount point structure */
-       xfs_alloctype_t atype = 0;      /* type for allocation routines */
-       xfs_agnumber_t  fb_agno;        /* ag number of ap->firstblock */
-       xfs_agnumber_t  ag;
-       xfs_alloc_arg_t args;
-       xfs_fileoff_t   orig_offset;
-       xfs_extlen_t    orig_length;
-       xfs_extlen_t    blen;
-       xfs_extlen_t    nextminlen = 0;
-       int             nullfb;         /* true if ap->firstblock isn't set */
-       int             isaligned;
-       int             tryagain;
-       int             error;
-       int             stripe_align;
+       struct xfs_mount        *mp = ap->ip->i_mount;
+       struct xfs_alloc_arg    args = { .tp = ap->tp, .mp = mp };
+       xfs_fileoff_t           orig_offset;
+       xfs_extlen_t            orig_length;
+       int                     error;
 
        ASSERT(ap->length);
+
+       if (ap->minlen != 1) {
+               ap->blkno = NULLFSBLOCK;
+               ap->length = 0;
+               return 0;
+       }
+
        orig_offset = ap->offset;
        orig_length = ap->length;
 
-       mp = ap->ip->i_mount;
+       args.alloc_minlen_only = 1;
 
-       memset(&args, 0, sizeof(args));
-       args.tp = ap->tp;
-       args.mp = mp;
+       xfs_bmap_compute_alignments(ap, &args);
+
+       if (ap->tp->t_firstblock == NULLFSBLOCK) {
+               /*
+                * Unlike the longest extent available in an AG, we don't track
+                * the length of an AG's shortest extent.
+                * XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT is a debug only knob and
+                * hence we can afford to start traversing from the 0th AG since
+                * we need not be concerned about a drop in performance in
+                * "debug only" code paths.
+                */
+               ap->blkno = XFS_AGB_TO_FSB(mp, 0, 0);
+       } else {
+               ap->blkno = ap->tp->t_firstblock;
+       }
+
+       args.fsbno = ap->blkno;
+       args.oinfo = XFS_RMAP_OINFO_SKIP_UPDATE;
+       args.type = XFS_ALLOCTYPE_FIRST_AG;
+       args.total = args.minlen = args.maxlen = ap->minlen;
+
+       args.alignment = 1;
+       args.minalignslop = 0;
+
+       args.minleft = ap->minleft;
+       args.wasdel = ap->wasdel;
+       args.resv = XFS_AG_RESV_NONE;
+       args.datatype = ap->datatype;
+
+       error = xfs_alloc_vextent(&args);
+       if (error)
+               return error;
+
+       if (args.fsbno != NULLFSBLOCK) {
+               xfs_bmap_process_allocated_extent(ap, &args, orig_offset,
+                       orig_length);
+       } else {
+               ap->blkno = NULLFSBLOCK;
+               ap->length = 0;
+       }
+
+       return 0;
+}
+#else
+
+#define xfs_bmap_exact_minlen_extent_alloc(bma) (-EFSCORRUPTED)
+
+#endif
+
+STATIC int
+xfs_bmap_btalloc(
+       struct xfs_bmalloca     *ap)
+{
+       struct xfs_mount        *mp = ap->ip->i_mount;
+       struct xfs_alloc_arg    args = { .tp = ap->tp, .mp = mp };
+       xfs_alloctype_t         atype = 0;
+       xfs_agnumber_t          fb_agno;        /* ag number of ap->firstblock */
+       xfs_agnumber_t          ag;
+       xfs_fileoff_t           orig_offset;
+       xfs_extlen_t            orig_length;
+       xfs_extlen_t            blen;
+       xfs_extlen_t            nextminlen = 0;
+       int                     nullfb; /* true if ap->firstblock isn't set */
+       int                     isaligned;
+       int                     tryagain;
+       int                     error;
+       int                     stripe_align;
+
+       ASSERT(ap->length);
+       orig_offset = ap->offset;
+       orig_length = ap->length;
 
        stripe_align = xfs_bmap_compute_alignments(ap, &args);
 
@@ -4106,6 +4173,10 @@ xfs_bmap_alloc_userdata(
                        return xfs_bmap_rtalloc(bma);
        }
 
+       if (unlikely(XFS_TEST_ERROR(false, mp,
+                       XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT)))
+               return xfs_bmap_exact_minlen_extent_alloc(bma);
+
        return xfs_bmap_btalloc(bma);
 }
 
@@ -4142,10 +4213,15 @@ xfs_bmapi_allocate(
        else
                bma->minlen = 1;
 
-       if (bma->flags & XFS_BMAPI_METADATA)
-               error = xfs_bmap_btalloc(bma);
-       else
+       if (bma->flags & XFS_BMAPI_METADATA) {
+               if (unlikely(XFS_TEST_ERROR(false, mp,
+                               XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT)))
+                       error = xfs_bmap_exact_minlen_extent_alloc(bma);
+               else
+                       error = xfs_bmap_btalloc(bma);
+       } else {
                error = xfs_bmap_alloc_userdata(bma);
+       }
        if (error || bma->blkno == NULLFSBLOCK)
                return error;
 
index 1c56fcceeea65ba97b0edac3bd80e5d3b99e1983..6ca9084b6934effa27a5ad1d7d7ccf42aad42193 100644 (file)
@@ -57,7 +57,8 @@
 #define XFS_ERRTAG_IUNLINK_FALLBACK                    34
 #define XFS_ERRTAG_BUF_IOERROR                         35
 #define XFS_ERRTAG_REDUCE_MAX_IEXTENTS                 36
-#define XFS_ERRTAG_MAX                                 37
+#define XFS_ERRTAG_BMAP_ALLOC_MINLEN_EXTENT            37
+#define XFS_ERRTAG_MAX                                 38
 
 /*
  * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
 #define XFS_RANDOM_IUNLINK_FALLBACK                    (XFS_RANDOM_DEFAULT/10)
 #define XFS_RANDOM_BUF_IOERROR                         XFS_RANDOM_DEFAULT
 #define XFS_RANDOM_REDUCE_MAX_IEXTENTS                 1
+#define XFS_RANDOM_BMAP_ALLOC_MINLEN_EXTENT            1
 
 #endif /* __XFS_ERRORTAG_H_ */