]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_alloc.c
xfs: validity check agbnos on the AGFL
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_alloc.c
index 7d304160d3543b7c5c0a9b84d07bcac7273dd7d7..40a36efa76d8acfc1657e7d64e2138743e1503ac 100644 (file)
@@ -23,7 +23,7 @@
 #include "xfs_ag_resv.h"
 #include "xfs_bmap.h"
 
-extern kmem_zone_t     *xfs_bmap_free_item_zone;
+struct kmem_cache      *xfs_extfree_item_cache;
 
 struct workqueue_struct *xfs_alloc_wq;
 
@@ -32,10 +32,6 @@ struct workqueue_struct *xfs_alloc_wq;
 #define        XFSA_FIXUP_BNO_OK       1
 #define        XFSA_FIXUP_CNT_OK       2
 
-STATIC int xfs_alloc_ag_vextent_exact(xfs_alloc_arg_t *);
-STATIC int xfs_alloc_ag_vextent_near(xfs_alloc_arg_t *);
-STATIC int xfs_alloc_ag_vextent_size(xfs_alloc_arg_t *);
-
 /*
  * Size of the AGFL.  For CRC-enabled filesystes we steal a couple of slots in
  * the beginning of the block for a proper header with the location information
@@ -78,6 +74,24 @@ xfs_prealloc_blocks(
 }
 
 /*
+ * The number of blocks per AG that we withhold from xfs_mod_fdblocks to
+ * guarantee that we can refill the AGFL prior to allocating space in a nearly
+ * full AG.  Although the space described by the free space btrees, the
+ * blocks used by the freesp btrees themselves, and the blocks owned by the
+ * AGFL are counted in the ondisk fdblocks, it's a mistake to let the ondisk
+ * free space in the AG drop so low that the free space btrees cannot refill an
+ * empty AGFL up to the minimum level.  Rather than grind through empty AGs
+ * until the fs goes down, we subtract this many AG blocks from the incore
+ * fdblocks to ensure user allocation does not overcommit the space the
+ * filesystem needs for the AGFLs.  The rmap btree uses a per-AG reservation to
+ * withhold space from xfs_mod_fdblocks, so we do not account for that here.
+ */
+#define XFS_ALLOCBT_AGFL_RESERVE       4
+
+/*
+ * Compute the number of blocks that we set aside to guarantee the ability to
+ * refill the AGFL and handle a full bmap btree split.
+ *
  * In order to avoid ENOSPC-related deadlock caused by out-of-order locking of
  * AGF buffer (PV 947395), we place constraints on the relationship among
  * actual allocations for data blocks, freelist blocks, and potential file data
@@ -89,14 +103,14 @@ xfs_prealloc_blocks(
  * extents need to be actually allocated. To get around this, we explicitly set
  * aside a few blocks which will not be reserved in delayed allocation.
  *
- * We need to reserve 4 fsbs _per AG_ for the freelist and 4 more to handle a
- * potential split of the file's bmap btree.
+ * For each AG, we need to reserve enough blocks to replenish a totally empty
+ * AGFL and 4 more to handle a potential split of the file's bmap btree.
  */
 unsigned int
 xfs_alloc_set_aside(
        struct xfs_mount        *mp)
 {
-       return mp->m_sb.sb_agcount * (XFS_ALLOC_AGFL_RESERVE + 4);
+       return mp->m_sb.sb_agcount * (XFS_ALLOCBT_AGFL_RESERVE + 4);
 }
 
 /*
@@ -120,12 +134,12 @@ xfs_alloc_ag_max_usable(
        unsigned int            blocks;
 
        blocks = XFS_BB_TO_FSB(mp, XFS_FSS_TO_BB(mp, 4)); /* ag headers */
-       blocks += XFS_ALLOC_AGFL_RESERVE;
+       blocks += XFS_ALLOCBT_AGFL_RESERVE;
        blocks += 3;                    /* AGF, AGI btree root blocks */
        if (xfs_has_finobt(mp))
                blocks++;               /* finobt root block */
        if (xfs_has_rmapbt(mp))
-               blocks++;               /* rmap root block */
+               blocks++;               /* rmap root block */
        if (xfs_has_reflink(mp))
                blocks++;               /* refcount root block */
 
@@ -215,6 +229,52 @@ xfs_alloc_update(
        return xfs_btree_update(cur, &rec);
 }
 
+/* Convert the ondisk btree record to its incore representation. */
+void
+xfs_alloc_btrec_to_irec(
+       const union xfs_btree_rec       *rec,
+       struct xfs_alloc_rec_incore     *irec)
+{
+       irec->ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
+       irec->ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
+}
+
+/* Simple checks for free space records. */
+xfs_failaddr_t
+xfs_alloc_check_irec(
+       struct xfs_btree_cur            *cur,
+       const struct xfs_alloc_rec_incore *irec)
+{
+       struct xfs_perag                *pag = cur->bc_ag.pag;
+
+       if (irec->ar_blockcount == 0)
+               return __this_address;
+
+       /* check for valid extent range, including overflow */
+       if (!xfs_verify_agbext(pag, irec->ar_startblock, irec->ar_blockcount))
+               return __this_address;
+
+       return NULL;
+}
+
+static inline int
+xfs_alloc_complain_bad_rec(
+       struct xfs_btree_cur            *cur,
+       xfs_failaddr_t                  fa,
+       const struct xfs_alloc_rec_incore *irec)
+{
+       struct xfs_mount                *mp = cur->bc_mp;
+
+       xfs_warn(mp,
+               "%s Freespace BTree record corruption in AG %d detected at %pS!",
+               cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
+               cur->bc_ag.pag->pag_agno, fa);
+       xfs_warn(mp,
+               "start block 0x%x block count 0x%x", irec->ar_startblock,
+               irec->ar_blockcount);
+       return -EFSCORRUPTED;
+}
+
 /*
  * Get the data from the pointed-to record.
  */
@@ -225,38 +285,23 @@ xfs_alloc_get_rec(
        xfs_extlen_t            *len,   /* output: length of extent */
        int                     *stat)  /* output: success/failure */
 {
-       struct xfs_mount        *mp = cur->bc_mp;
-       xfs_agnumber_t          agno = cur->bc_ag.pag->pag_agno;
+       struct xfs_alloc_rec_incore irec;
        union xfs_btree_rec     *rec;
+       xfs_failaddr_t          fa;
        int                     error;
 
        error = xfs_btree_get_rec(cur, &rec, stat);
        if (error || !(*stat))
                return error;
 
-       *bno = be32_to_cpu(rec->alloc.ar_startblock);
-       *len = be32_to_cpu(rec->alloc.ar_blockcount);
-
-       if (*len == 0)
-               goto out_bad_rec;
-
-       /* check for valid extent range, including overflow */
-       if (!xfs_verify_agbno(mp, agno, *bno))
-               goto out_bad_rec;
-       if (*bno > *bno + *len)
-               goto out_bad_rec;
-       if (!xfs_verify_agbno(mp, agno, *bno + *len - 1))
-               goto out_bad_rec;
+       xfs_alloc_btrec_to_irec(rec, &irec);
+       fa = xfs_alloc_check_irec(cur, &irec);
+       if (fa)
+               return xfs_alloc_complain_bad_rec(cur, fa, &irec);
 
+       *bno = irec.ar_startblock;
+       *len = irec.ar_blockcount;
        return 0;
-
-out_bad_rec:
-       xfs_warn(mp,
-               "%s Freespace BTree record corruption in AG %d detected!",
-               cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size", agno);
-       xfs_warn(mp,
-               "start block 0x%x block count 0x%x", *bno, *len);
-       return -EFSCORRUPTED;
 }
 
 /*
@@ -579,6 +624,25 @@ xfs_alloc_fixup_trees(
        return 0;
 }
 
+/*
+ * We do not verify the AGFL contents against AGF-based index counters here,
+ * even though we may have access to the perag that contains shadow copies. We
+ * don't know if the AGF based counters have been checked, and if they have they
+ * still may be inconsistent because they haven't yet been reset on the first
+ * allocation after the AGF has been read in.
+ *
+ * This means we can only check that all agfl entries contain valid or null
+ * values because we can't reliably determine the active range to exclude
+ * NULLAGBNO as a valid value.
+ *
+ * However, we can't even do that for v4 format filesystems because there are
+ * old versions of mkfs out there that does not initialise the AGFL to known,
+ * verifiable values. HEnce we can't tell the difference between a AGFL block
+ * allocated by mkfs and a corrupted AGFL block here on v4 filesystems.
+ *
+ * As a result, we can only fully validate AGFL block numbers when we pull them
+ * from the freelist in xfs_alloc_get_freelist().
+ */
 static xfs_failaddr_t
 xfs_agfl_verify(
        struct xfs_buf  *bp)
@@ -588,12 +652,6 @@ xfs_agfl_verify(
        __be32          *agfl_bno = xfs_buf_to_agfl_bno(bp);
        int             i;
 
-       /*
-        * There is no verification of non-crc AGFLs because mkfs does not
-        * initialise the AGFL to zero or NULL. Hence the only valid part of the
-        * AGFL is what the AGF says is active. We can't get to the AGF, so we
-        * can't verify just those entries are valid.
-        */
        if (!xfs_has_crc(mp))
                return NULL;
 
@@ -681,20 +739,19 @@ const struct xfs_buf_ops xfs_agfl_buf_ops = {
 /*
  * Read in the allocation group free block array.
  */
-int                                    /* error */
+int
 xfs_alloc_read_agfl(
-       xfs_mount_t     *mp,            /* mount point structure */
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_agnumber_t  agno,           /* allocation group number */
-       struct xfs_buf  **bpp)          /* buffer for the ag free block array */
+       struct xfs_perag        *pag,
+       struct xfs_trans        *tp,
+       struct xfs_buf          **bpp)
 {
-       struct xfs_buf  *bp;            /* return value */
-       int             error;
+       struct xfs_mount        *mp = pag->pag_mount;
+       struct xfs_buf          *bp;
+       int                     error;
 
-       ASSERT(agno != NULLAGNUMBER);
        error = xfs_trans_read_buf(
                        mp, tp, mp->m_ddev_targp,
-                       XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
+                       XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGFL_DADDR(mp)),
                        XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_agfl_buf_ops);
        if (error)
                return error;
@@ -754,8 +811,6 @@ xfs_alloc_cur_setup(
        int                     error;
        int                     i;
 
-       ASSERT(args->alignment == 1 || args->type != XFS_ALLOCTYPE_THIS_BNO);
-
        acur->cur_len = args->maxlen;
        acur->rec_bno = 0;
        acur->rec_len = 0;
@@ -869,7 +924,6 @@ xfs_alloc_cur_check(
         * We have an aligned record that satisfies minlen and beats or matches
         * the candidate extent size. Compare locality for near allocation mode.
         */
-       ASSERT(args->type == XFS_ALLOCTYPE_NEAR_BNO);
        diff = xfs_alloc_compute_diff(args->agbno, args->len,
                                      args->alignment, args->datatype,
                                      bnoa, lena, &bnew);
@@ -1053,7 +1107,8 @@ xfs_alloc_ag_vextent_small(
            be32_to_cpu(agf->agf_flcount) <= args->minleft)
                goto out;
 
-       error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno, 0);
+       error = xfs_alloc_get_freelist(args->pag, args->tp, args->agbp,
+                       &fbno, 0);
        if (error)
                goto error;
        if (fbno == NULLAGBLOCK)
@@ -1113,78 +1168,6 @@ error:
        return error;
 }
 
-/*
- * Allocate a variable extent in the allocation group agno.
- * Type and bno are used to determine where in the allocation group the
- * extent will start.
- * Extent's length (returned in *len) will be between minlen and maxlen,
- * and of the form k * prod + mod unless there's nothing that large.
- * Return the starting a.g. block, or NULLAGBLOCK if we can't do it.
- */
-STATIC int                     /* error */
-xfs_alloc_ag_vextent(
-       xfs_alloc_arg_t *args)  /* argument structure for allocation */
-{
-       int             error=0;
-
-       ASSERT(args->minlen > 0);
-       ASSERT(args->maxlen > 0);
-       ASSERT(args->minlen <= args->maxlen);
-       ASSERT(args->mod < args->prod);
-       ASSERT(args->alignment > 0);
-
-       /*
-        * Branch to correct routine based on the type.
-        */
-       args->wasfromfl = 0;
-       switch (args->type) {
-       case XFS_ALLOCTYPE_THIS_AG:
-               error = xfs_alloc_ag_vextent_size(args);
-               break;
-       case XFS_ALLOCTYPE_NEAR_BNO:
-               error = xfs_alloc_ag_vextent_near(args);
-               break;
-       case XFS_ALLOCTYPE_THIS_BNO:
-               error = xfs_alloc_ag_vextent_exact(args);
-               break;
-       default:
-               ASSERT(0);
-               /* NOTREACHED */
-       }
-
-       if (error || args->agbno == NULLAGBLOCK)
-               return error;
-
-       ASSERT(args->len >= args->minlen);
-       ASSERT(args->len <= args->maxlen);
-       ASSERT(!args->wasfromfl || args->resv != XFS_AG_RESV_AGFL);
-       ASSERT(args->agbno % args->alignment == 0);
-
-       /* if not file data, insert new block into the reverse map btree */
-       if (!xfs_rmap_should_skip_owner_update(&args->oinfo)) {
-               error = xfs_rmap_alloc(args->tp, args->agbp, args->pag,
-                                      args->agbno, args->len, &args->oinfo);
-               if (error)
-                       return error;
-       }
-
-       if (!args->wasfromfl) {
-               error = xfs_alloc_update_counters(args->tp, args->agbp,
-                                                 -((long)(args->len)));
-               if (error)
-                       return error;
-
-               ASSERT(!xfs_extent_busy_search(args->mp, args->pag,
-                                             args->agbno, args->len));
-       }
-
-       xfs_ag_resv_alloc_extent(args->pag, args->resv, args);
-
-       XFS_STATS_INC(args->mp, xs_allocx);
-       XFS_STATS_ADD(args->mp, xs_allocb, args->len);
-       return error;
-}
-
 /*
  * Allocate a variable extent at exactly agno/bno.
  * Extent's length (returned in *len) will be between minlen and maxlen,
@@ -1370,7 +1353,6 @@ xfs_alloc_ag_vextent_locality(
        bool                    fbinc;
 
        ASSERT(acur->len == 0);
-       ASSERT(args->type == XFS_ALLOCTYPE_NEAR_BNO);
 
        *stat = 0;
 
@@ -1497,7 +1479,7 @@ xfs_alloc_ag_vextent_lastblock(
 
 #ifdef DEBUG
        /* Randomly don't execute the first algorithm. */
-       if (prandom_u32() & 1)
+       if (get_random_u32_below(2))
                return 0;
 #endif
 
@@ -2348,12 +2330,16 @@ xfs_free_agfl_block(
 }
 
 /*
- * Check the agfl fields of the agf for inconsistency or corruption. The purpose
- * is to detect an agfl header padding mismatch between current and early v5
- * kernels. This problem manifests as a 1-slot size difference between the
- * on-disk flcount and the active [first, last] range of a wrapped agfl. This
- * may also catch variants of agfl count corruption unrelated to padding. Either
- * way, we'll reset the agfl and warn the user.
+ * Check the agfl fields of the agf for inconsistency or corruption.
+ *
+ * The original purpose was to detect an agfl header padding mismatch between
+ * current and early v5 kernels. This problem manifests as a 1-slot size
+ * difference between the on-disk flcount and the active [first, last] range of
+ * a wrapped agfl.
+ *
+ * However, we need to use these same checks to catch agfl count corruptions
+ * unrelated to padding. This could occur on any v4 or v5 filesystem, so either
+ * way, we need to reset the agfl and warn the user.
  *
  * Return true if a reset is required before the agfl can be used, false
  * otherwise.
@@ -2369,10 +2355,6 @@ xfs_agfl_needs_reset(
        int                     agfl_size = xfs_agfl_size(mp);
        int                     active;
 
-       /* no agfl header on v4 supers */
-       if (!xfs_has_crc(mp))
-               return false;
-
        /*
         * The agf read verifier catches severe corruption of these fields.
         * Repeat some sanity checks to cover a packed -> unpacked mismatch if
@@ -2416,7 +2398,7 @@ xfs_agfl_reset(
        struct xfs_mount        *mp = tp->t_mountp;
        struct xfs_agf          *agf = agbp->b_addr;
 
-       ASSERT(pag->pagf_agflreset);
+       ASSERT(xfs_perag_agfl_needs_reset(pag));
        trace_xfs_agfl_reset(mp, agf, 0, _RET_IP_);
 
        xfs_warn(mp,
@@ -2431,12 +2413,12 @@ xfs_agfl_reset(
                                    XFS_AGF_FLCOUNT);
 
        pag->pagf_flcount = 0;
-       pag->pagf_agflreset = false;
+       clear_bit(XFS_AGSTATE_AGFL_NEEDS_RESET, &pag->pag_opstate);
 }
 
 /*
  * Defer an AGFL block free. This is effectively equivalent to
- * xfs_bmap_add_free() with some special handling particular to AGFL blocks.
+ * xfs_free_extent_later() with some special handling particular to AGFL blocks.
  *
  * Deferring AGFL frees helps prevent log reservation overruns due to too many
  * allocation operations in a transaction. AGFL frees are prone to this problem
@@ -2453,21 +2435,77 @@ xfs_defer_agfl_block(
        struct xfs_owner_info           *oinfo)
 {
        struct xfs_mount                *mp = tp->t_mountp;
-       struct xfs_extent_free_item     *new;           /* new element */
+       struct xfs_extent_free_item     *xefi;
 
-       ASSERT(xfs_bmap_free_item_zone != NULL);
+       ASSERT(xfs_extfree_item_cache != NULL);
        ASSERT(oinfo != NULL);
 
-       new = kmem_cache_alloc(xfs_bmap_free_item_zone,
+       xefi = kmem_cache_zalloc(xfs_extfree_item_cache,
                               GFP_KERNEL | __GFP_NOFAIL);
-       new->xefi_startblock = XFS_AGB_TO_FSB(mp, agno, agbno);
-       new->xefi_blockcount = 1;
-       new->xefi_oinfo = *oinfo;
-       new->xefi_skip_discard = false;
+       xefi->xefi_startblock = XFS_AGB_TO_FSB(mp, agno, agbno);
+       xefi->xefi_blockcount = 1;
+       xefi->xefi_owner = oinfo->oi_owner;
 
        trace_xfs_agfl_free_defer(mp, agno, 0, agbno, 1);
 
-       xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_AGFL_FREE, &new->xefi_list);
+       xfs_extent_free_get_group(mp, xefi);
+       xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_AGFL_FREE, &xefi->xefi_list);
+}
+
+/*
+ * Add the extent to the list of extents to be free at transaction end.
+ * The list is maintained sorted (by block number).
+ */
+void
+__xfs_free_extent_later(
+       struct xfs_trans                *tp,
+       xfs_fsblock_t                   bno,
+       xfs_filblks_t                   len,
+       const struct xfs_owner_info     *oinfo,
+       bool                            skip_discard)
+{
+       struct xfs_extent_free_item     *xefi;
+       struct xfs_mount                *mp = tp->t_mountp;
+#ifdef DEBUG
+       xfs_agnumber_t                  agno;
+       xfs_agblock_t                   agbno;
+
+       ASSERT(bno != NULLFSBLOCK);
+       ASSERT(len > 0);
+       ASSERT(len <= XFS_MAX_BMBT_EXTLEN);
+       ASSERT(!isnullstartblock(bno));
+       agno = XFS_FSB_TO_AGNO(mp, bno);
+       agbno = XFS_FSB_TO_AGBNO(mp, bno);
+       ASSERT(agno < mp->m_sb.sb_agcount);
+       ASSERT(agbno < mp->m_sb.sb_agblocks);
+       ASSERT(len < mp->m_sb.sb_agblocks);
+       ASSERT(agbno + len <= mp->m_sb.sb_agblocks);
+#endif
+       ASSERT(xfs_extfree_item_cache != NULL);
+
+       xefi = kmem_cache_zalloc(xfs_extfree_item_cache,
+                              GFP_KERNEL | __GFP_NOFAIL);
+       xefi->xefi_startblock = bno;
+       xefi->xefi_blockcount = (xfs_extlen_t)len;
+       if (skip_discard)
+               xefi->xefi_flags |= XFS_EFI_SKIP_DISCARD;
+       if (oinfo) {
+               ASSERT(oinfo->oi_offset == 0);
+
+               if (oinfo->oi_flags & XFS_OWNER_INFO_ATTR_FORK)
+                       xefi->xefi_flags |= XFS_EFI_ATTR_FORK;
+               if (oinfo->oi_flags & XFS_OWNER_INFO_BMBT_BLOCK)
+                       xefi->xefi_flags |= XFS_EFI_BMBT_BLOCK;
+               xefi->xefi_owner = oinfo->oi_owner;
+       } else {
+               xefi->xefi_owner = XFS_RMAP_OWN_NULL;
+       }
+       trace_xfs_bmap_free_defer(mp,
+                       XFS_FSB_TO_AGNO(tp->t_mountp, bno), 0,
+                       XFS_FSB_TO_AGBNO(tp->t_mountp, bno), len);
+
+       xfs_extent_free_get_group(mp, xefi);
+       xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_FREE, &xefi->xefi_list);
 }
 
 #ifdef DEBUG
@@ -2533,8 +2571,8 @@ xfs_alloc_fix_freelist(
        /* deferred ops (AGFL block frees) require permanent transactions */
        ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
 
-       if (!pag->pagf_init) {
-               error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp);
+       if (!xfs_perag_initialised_agf(pag)) {
+               error = xfs_alloc_read_agf(pag, tp, flags, &agbp);
                if (error) {
                        /* Couldn't lock the AGF so skip this AG. */
                        if (error == -EAGAIN)
@@ -2548,7 +2586,8 @@ xfs_alloc_fix_freelist(
         * somewhere else if we are not being asked to try harder at this
         * point
         */
-       if (pag->pagf_metadata && (args->datatype & XFS_ALLOC_USERDATA) &&
+       if (xfs_perag_prefers_metadata(pag) &&
+           (args->datatype & XFS_ALLOC_USERDATA) &&
            (flags & XFS_ALLOC_FLAG_TRYLOCK)) {
                ASSERT(!(flags & XFS_ALLOC_FLAG_FREEING));
                goto out_agbp_relse;
@@ -2564,7 +2603,7 @@ xfs_alloc_fix_freelist(
         * Can fail if we're not blocking on locks, and it's held.
         */
        if (!agbp) {
-               error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp);
+               error = xfs_alloc_read_agf(pag, tp, flags, &agbp);
                if (error) {
                        /* Couldn't lock the AGF so skip this AG. */
                        if (error == -EAGAIN)
@@ -2574,7 +2613,7 @@ xfs_alloc_fix_freelist(
        }
 
        /* reset a padding mismatched agfl before final free space check */
-       if (pag->pagf_agflreset)
+       if (xfs_perag_agfl_needs_reset(pag))
                xfs_agfl_reset(tp, agbp, pag);
 
        /* If there isn't enough total space or single-extent, reject it. */
@@ -2622,7 +2661,7 @@ xfs_alloc_fix_freelist(
        else
                targs.oinfo = XFS_RMAP_OINFO_AG;
        while (!(flags & XFS_ALLOC_FLAG_NOSHRINK) && pag->pagf_flcount > need) {
-               error = xfs_alloc_get_freelist(tp, agbp, &bno, 0);
+               error = xfs_alloc_get_freelist(pag, tp, agbp, &bno, 0);
                if (error)
                        goto out_agbp_relse;
 
@@ -2635,9 +2674,8 @@ xfs_alloc_fix_freelist(
        targs.agbp = agbp;
        targs.agno = args->agno;
        targs.alignment = targs.minlen = targs.prod = 1;
-       targs.type = XFS_ALLOCTYPE_THIS_AG;
        targs.pag = pag;
-       error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp);
+       error = xfs_alloc_read_agfl(pag, tp, &agflbp);
        if (error)
                goto out_agbp_relse;
 
@@ -2648,7 +2686,7 @@ xfs_alloc_fix_freelist(
                targs.resv = XFS_AG_RESV_AGFL;
 
                /* Allocate as many blocks as possible at once. */
-               error = xfs_alloc_ag_vextent(&targs);
+               error = xfs_alloc_ag_vextent_size(&targs);
                if (error)
                        goto out_agflbp_relse;
 
@@ -2662,11 +2700,23 @@ xfs_alloc_fix_freelist(
                                break;
                        goto out_agflbp_relse;
                }
+
+               if (!xfs_rmap_should_skip_owner_update(&targs.oinfo)) {
+                       error = xfs_rmap_alloc(tp, agbp, pag,
+                                      targs.agbno, targs.len, &targs.oinfo);
+                       if (error)
+                               goto out_agflbp_relse;
+               }
+               error = xfs_alloc_update_counters(tp, agbp,
+                                                 -((long)(targs.len)));
+               if (error)
+                       goto out_agflbp_relse;
+
                /*
                 * Put each allocated block on the list.
                 */
                for (bno = targs.agbno; bno < targs.agbno + targs.len; bno++) {
-                       error = xfs_alloc_put_freelist(tp, agbp,
+                       error = xfs_alloc_put_freelist(pag, tp, agbp,
                                                        agflbp, bno, 0);
                        if (error)
                                goto out_agflbp_relse;
@@ -2692,6 +2742,7 @@ out_no_agbp:
  */
 int
 xfs_alloc_get_freelist(
+       struct xfs_perag        *pag,
        struct xfs_trans        *tp,
        struct xfs_buf          *agbp,
        xfs_agblock_t           *bnop,
@@ -2702,9 +2753,8 @@ xfs_alloc_get_freelist(
        xfs_agblock_t           bno;
        __be32                  *agfl_bno;
        int                     error;
-       int                     logflags;
+       uint32_t                logflags;
        struct xfs_mount        *mp = tp->t_mountp;
-       struct xfs_perag        *pag;
 
        /*
         * Freelist is empty, give up.
@@ -2716,8 +2766,7 @@ xfs_alloc_get_freelist(
        /*
         * Read the array of free blocks.
         */
-       error = xfs_alloc_read_agfl(mp, tp, be32_to_cpu(agf->agf_seqno),
-                                   &agflbp);
+       error = xfs_alloc_read_agfl(pag, tp, &agflbp);
        if (error)
                return error;
 
@@ -2727,13 +2776,15 @@ xfs_alloc_get_freelist(
         */
        agfl_bno = xfs_buf_to_agfl_bno(agflbp);
        bno = be32_to_cpu(agfl_bno[be32_to_cpu(agf->agf_flfirst)]);
+       if (XFS_IS_CORRUPT(tp->t_mountp, !xfs_verify_agbno(pag, bno)))
+               return -EFSCORRUPTED;
+
        be32_add_cpu(&agf->agf_flfirst, 1);
        xfs_trans_brelse(tp, agflbp);
        if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp))
                agf->agf_flfirst = 0;
 
-       pag = agbp->b_pag;
-       ASSERT(!pag->pagf_agflreset);
+       ASSERT(!xfs_perag_agfl_needs_reset(pag));
        be32_add_cpu(&agf->agf_flcount, -1);
        pag->pagf_flcount--;
 
@@ -2755,9 +2806,9 @@ xfs_alloc_get_freelist(
  */
 void
 xfs_alloc_log_agf(
-       xfs_trans_t     *tp,    /* transaction pointer */
-       struct xfs_buf  *bp,    /* buffer for a.g. freelist header */
-       int             fields) /* mask of fields to be logged (XFS_AGF_...) */
+       struct xfs_trans        *tp,
+       struct xfs_buf          *bp,
+       uint32_t                fields)
 {
        int     first;          /* first byte offset */
        int     last;           /* last byte offset */
@@ -2792,30 +2843,12 @@ xfs_alloc_log_agf(
        xfs_trans_log_buf(tp, bp, (uint)first, (uint)last);
 }
 
-/*
- * Interface for inode allocation to force the pag data to be initialized.
- */
-int                                    /* error */
-xfs_alloc_pagf_init(
-       xfs_mount_t             *mp,    /* file system mount structure */
-       xfs_trans_t             *tp,    /* transaction pointer */
-       xfs_agnumber_t          agno,   /* allocation group number */
-       int                     flags)  /* XFS_ALLOC_FLAGS_... */
-{
-       struct xfs_buf          *bp;
-       int                     error;
-
-       error = xfs_alloc_read_agf(mp, tp, agno, flags, &bp);
-       if (!error)
-               xfs_trans_brelse(tp, bp);
-       return error;
-}
-
 /*
  * Put the block on the freelist for the allocation group.
  */
 int
 xfs_alloc_put_freelist(
+       struct xfs_perag        *pag,
        struct xfs_trans        *tp,
        struct xfs_buf          *agbp,
        struct xfs_buf          *agflbp,
@@ -2824,22 +2857,23 @@ xfs_alloc_put_freelist(
 {
        struct xfs_mount        *mp = tp->t_mountp;
        struct xfs_agf          *agf = agbp->b_addr;
-       struct xfs_perag        *pag;
        __be32                  *blockp;
        int                     error;
-       int                     logflags;
+       uint32_t                logflags;
        __be32                  *agfl_bno;
        int                     startoff;
 
-       if (!agflbp && (error = xfs_alloc_read_agfl(mp, tp,
-                       be32_to_cpu(agf->agf_seqno), &agflbp)))
-               return error;
+       if (!agflbp) {
+               error = xfs_alloc_read_agfl(pag, tp, &agflbp);
+               if (error)
+                       return error;
+       }
+
        be32_add_cpu(&agf->agf_fllast, 1);
        if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp))
                agf->agf_fllast = 0;
 
-       pag = agbp->b_pag;
-       ASSERT(!pag->pagf_agflreset);
+       ASSERT(!xfs_perag_agfl_needs_reset(pag));
        be32_add_cpu(&agf->agf_flcount, 1);
        pag->pagf_flcount++;
 
@@ -2867,6 +2901,19 @@ xfs_alloc_put_freelist(
        return 0;
 }
 
+/*
+ * Verify the AGF is consistent.
+ *
+ * We do not verify the AGFL indexes in the AGF are fully consistent here
+ * because of issues with variable on-disk structure sizes. Instead, we check
+ * the agfl indexes for consistency when we initialise the perag from the AGF
+ * information after a read completes.
+ *
+ * If the index is inconsistent, then we mark the perag as needing an AGFL
+ * reset. The first AGFL update performed then resets the AGFL indexes and
+ * refills the AGFL with known good free blocks, allowing the filesystem to
+ * continue operating normally at the cost of a few leaked free space blocks.
+ */
 static xfs_failaddr_t
 xfs_agf_verify(
        struct xfs_buf          *bp)
@@ -2940,7 +2987,6 @@ xfs_agf_verify(
                return __this_address;
 
        return NULL;
-
 }
 
 static void
@@ -2995,62 +3041,58 @@ const struct xfs_buf_ops xfs_agf_buf_ops = {
 /*
  * Read in the allocation group header (free/alloc section).
  */
-int                                    /* error */
+int
 xfs_read_agf(
-       struct xfs_mount        *mp,    /* mount point structure */
-       struct xfs_trans        *tp,    /* transaction pointer */
-       xfs_agnumber_t          agno,   /* allocation group number */
-       int                     flags,  /* XFS_BUF_ */
-       struct xfs_buf          **bpp)  /* buffer for the ag freelist header */
+       struct xfs_perag        *pag,
+       struct xfs_trans        *tp,
+       int                     flags,
+       struct xfs_buf          **agfbpp)
 {
-       int             error;
+       struct xfs_mount        *mp = pag->pag_mount;
+       int                     error;
 
-       trace_xfs_read_agf(mp, agno);
+       trace_xfs_read_agf(pag->pag_mount, pag->pag_agno);
 
-       ASSERT(agno != NULLAGNUMBER);
        error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
-                       XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
-                       XFS_FSS_TO_BB(mp, 1), flags, bpp, &xfs_agf_buf_ops);
+                       XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGF_DADDR(mp)),
+                       XFS_FSS_TO_BB(mp, 1), flags, agfbpp, &xfs_agf_buf_ops);
        if (error)
                return error;
 
-       ASSERT(!(*bpp)->b_error);
-       xfs_buf_set_ref(*bpp, XFS_AGF_REF);
+       xfs_buf_set_ref(*agfbpp, XFS_AGF_REF);
        return 0;
 }
 
 /*
- * Read in the allocation group header (free/alloc section).
+ * Read in the allocation group header (free/alloc section) and initialise the
+ * perag structure if necessary. If the caller provides @agfbpp, then return the
+ * locked buffer to the caller, otherwise free it.
  */
-int                                    /* error */
+int
 xfs_alloc_read_agf(
-       struct xfs_mount        *mp,    /* mount point structure */
-       struct xfs_trans        *tp,    /* transaction pointer */
-       xfs_agnumber_t          agno,   /* allocation group number */
-       int                     flags,  /* XFS_ALLOC_FLAG_... */
-       struct xfs_buf          **bpp)  /* buffer for the ag freelist header */
-{
-       struct xfs_agf          *agf;           /* ag freelist header */
-       struct xfs_perag        *pag;           /* per allocation group data */
+       struct xfs_perag        *pag,
+       struct xfs_trans        *tp,
+       int                     flags,
+       struct xfs_buf          **agfbpp)
+{
+       struct xfs_buf          *agfbp;
+       struct xfs_agf          *agf;
        int                     error;
        int                     allocbt_blks;
 
-       trace_xfs_alloc_read_agf(mp, agno);
+       trace_xfs_alloc_read_agf(pag->pag_mount, pag->pag_agno);
 
        /* We don't support trylock when freeing. */
        ASSERT((flags & (XFS_ALLOC_FLAG_FREEING | XFS_ALLOC_FLAG_TRYLOCK)) !=
                        (XFS_ALLOC_FLAG_FREEING | XFS_ALLOC_FLAG_TRYLOCK));
-       ASSERT(agno != NULLAGNUMBER);
-       error = xfs_read_agf(mp, tp, agno,
+       error = xfs_read_agf(pag, tp,
                        (flags & XFS_ALLOC_FLAG_TRYLOCK) ? XBF_TRYLOCK : 0,
-                       bpp);
+                       &agfbp);
        if (error)
                return error;
-       ASSERT(!(*bpp)->b_error);
 
-       agf = (*bpp)->b_addr;
-       pag = (*bpp)->b_pag;
-       if (!pag->pagf_init) {
+       agf = agfbp->b_addr;
+       if (!xfs_perag_initialised_agf(pag)) {
                pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks);
                pag->pagf_btreeblks = be32_to_cpu(agf->agf_btreeblks);
                pag->pagf_flcount = be32_to_cpu(agf->agf_flcount);
@@ -3062,8 +3104,10 @@ xfs_alloc_read_agf(
                pag->pagf_levels[XFS_BTNUM_RMAPi] =
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]);
                pag->pagf_refcount_level = be32_to_cpu(agf->agf_refcount_level);
-               pag->pagf_init = 1;
-               pag->pagf_agflreset = xfs_agfl_needs_reset(mp, agf);
+               if (xfs_agfl_needs_reset(pag->pag_mount, agf))
+                       set_bit(XFS_AGSTATE_AGFL_NEEDS_RESET, &pag->pag_opstate);
+               else
+                       clear_bit(XFS_AGSTATE_AGFL_NEEDS_RESET, &pag->pag_opstate);
 
                /*
                 * Update the in-core allocbt counter. Filter out the rmapbt
@@ -3073,13 +3117,16 @@ xfs_alloc_read_agf(
                 * counter only tracks non-root blocks.
                 */
                allocbt_blks = pag->pagf_btreeblks;
-               if (xfs_has_rmapbt(mp))
+               if (xfs_has_rmapbt(pag->pag_mount))
                        allocbt_blks -= be32_to_cpu(agf->agf_rmap_blocks) - 1;
                if (allocbt_blks > 0)
-                       atomic64_add(allocbt_blks, &mp->m_allocbt_blks);
+                       atomic64_add(allocbt_blks,
+                                       &pag->pag_mount->m_allocbt_blks);
+
+               set_bit(XFS_AGSTATE_AGF_INIT, &pag->pag_opstate);
        }
 #ifdef DEBUG
-       else if (!xfs_is_shutdown(mp)) {
+       else if (!xfs_is_shutdown(pag->pag_mount)) {
                ASSERT(pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks));
                ASSERT(pag->pagf_btreeblks == be32_to_cpu(agf->agf_btreeblks));
                ASSERT(pag->pagf_flcount == be32_to_cpu(agf->agf_flcount));
@@ -3090,30 +3137,33 @@ xfs_alloc_read_agf(
                       be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]));
        }
 #endif
+       if (agfbpp)
+               *agfbpp = agfbp;
+       else
+               xfs_trans_brelse(tp, agfbp);
        return 0;
 }
 
 /*
- * Allocate an extent (variable-size).
- * Depending on the allocation type, we either look in a single allocation
- * group or loop over the allocation groups to find the result.
+ * Pre-proces allocation arguments to set initial state that we don't require
+ * callers to set up correctly, as well as bounds check the allocation args
+ * that are set up.
  */
-int                            /* error */
-xfs_alloc_vextent(
-       struct xfs_alloc_arg    *args)  /* allocation argument structure */
+static int
+xfs_alloc_vextent_check_args(
+       struct xfs_alloc_arg    *args,
+       xfs_fsblock_t           target,
+       xfs_agnumber_t          *minimum_agno)
 {
-       xfs_agblock_t           agsize; /* allocation group size */
-       int                     error;
-       int                     flags;  /* XFS_ALLOC_FLAG_... locking flags */
-       struct xfs_mount        *mp;    /* mount structure pointer */
-       xfs_agnumber_t          sagno;  /* starting allocation group number */
-       xfs_alloctype_t         type;   /* input allocation type */
-       int                     bump_rotor = 0;
-       xfs_agnumber_t          rotorstep = xfs_rotorstep; /* inode32 agf stepper */
-
-       mp = args->mp;
-       type = args->otype = args->type;
-       args->agbno = NULLAGBLOCK;
+       struct xfs_mount        *mp = args->mp;
+       xfs_agblock_t           agsize;
+
+       args->fsbno = NULLFSBLOCK;
+
+       *minimum_agno = 0;
+       if (args->tp->t_highest_agno != NULLAGNUMBER)
+               *minimum_agno = args->tp->t_highest_agno;
+
        /*
         * Just fix this up, for the case where the last a.g. is shorter
         * (or there's only one a.g.) and the caller couldn't easily figure
@@ -3124,168 +3174,449 @@ xfs_alloc_vextent(
                args->maxlen = agsize;
        if (args->alignment == 0)
                args->alignment = 1;
-       ASSERT(XFS_FSB_TO_AGNO(mp, args->fsbno) < mp->m_sb.sb_agcount);
-       ASSERT(XFS_FSB_TO_AGBNO(mp, args->fsbno) < agsize);
+
+       ASSERT(args->minlen > 0);
+       ASSERT(args->maxlen > 0);
+       ASSERT(args->alignment > 0);
+       ASSERT(args->resv != XFS_AG_RESV_AGFL);
+
+       ASSERT(XFS_FSB_TO_AGNO(mp, target) < mp->m_sb.sb_agcount);
+       ASSERT(XFS_FSB_TO_AGBNO(mp, target) < agsize);
        ASSERT(args->minlen <= args->maxlen);
        ASSERT(args->minlen <= agsize);
        ASSERT(args->mod < args->prod);
-       if (XFS_FSB_TO_AGNO(mp, args->fsbno) >= mp->m_sb.sb_agcount ||
-           XFS_FSB_TO_AGBNO(mp, args->fsbno) >= agsize ||
+
+       if (XFS_FSB_TO_AGNO(mp, target) >= mp->m_sb.sb_agcount ||
+           XFS_FSB_TO_AGBNO(mp, target) >= agsize ||
            args->minlen > args->maxlen || args->minlen > agsize ||
            args->mod >= args->prod) {
-               args->fsbno = NULLFSBLOCK;
                trace_xfs_alloc_vextent_badargs(args);
+               return -ENOSPC;
+       }
+
+       if (args->agno != NULLAGNUMBER && *minimum_agno > args->agno) {
+               trace_xfs_alloc_vextent_skip_deadlock(args);
+               return -ENOSPC;
+       }
+       return 0;
+
+}
+
+/*
+ * Prepare an AG for allocation. If the AG is not prepared to accept the
+ * allocation, return failure.
+ *
+ * XXX(dgc): The complexity of "need_pag" will go away as all caller paths are
+ * modified to hold their own perag references.
+ */
+static int
+xfs_alloc_vextent_prepare_ag(
+       struct xfs_alloc_arg    *args,
+       uint32_t                flags)
+{
+       bool                    need_pag = !args->pag;
+       int                     error;
+
+       if (need_pag)
+               args->pag = xfs_perag_get(args->mp, args->agno);
+
+       args->agbp = NULL;
+       error = xfs_alloc_fix_freelist(args, flags);
+       if (error) {
+               trace_xfs_alloc_vextent_nofix(args);
+               if (need_pag)
+                       xfs_perag_put(args->pag);
+               args->agbno = NULLAGBLOCK;
+               return error;
+       }
+       if (!args->agbp) {
+               /* cannot allocate in this AG at all */
+               trace_xfs_alloc_vextent_noagbp(args);
+               args->agbno = NULLAGBLOCK;
                return 0;
        }
+       args->wasfromfl = 0;
+       return 0;
+}
 
-       switch (type) {
-       case XFS_ALLOCTYPE_THIS_AG:
-       case XFS_ALLOCTYPE_NEAR_BNO:
-       case XFS_ALLOCTYPE_THIS_BNO:
-               /*
-                * These three force us into a single a.g.
-                */
-               args->agno = XFS_FSB_TO_AGNO(mp, args->fsbno);
-               args->pag = xfs_perag_get(mp, args->agno);
-               error = xfs_alloc_fix_freelist(args, 0);
-               if (error) {
-                       trace_xfs_alloc_vextent_nofix(args);
-                       goto error0;
-               }
-               if (!args->agbp) {
-                       trace_xfs_alloc_vextent_noagbp(args);
+/*
+ * Post-process allocation results to account for the allocation if it succeed
+ * and set the allocated block number correctly for the caller.
+ *
+ * XXX: we should really be returning ENOSPC for ENOSPC, not
+ * hiding it behind a "successful" NULLFSBLOCK allocation.
+ */
+static int
+xfs_alloc_vextent_finish(
+       struct xfs_alloc_arg    *args,
+       xfs_agnumber_t          minimum_agno,
+       int                     alloc_error,
+       bool                    drop_perag)
+{
+       struct xfs_mount        *mp = args->mp;
+       int                     error = 0;
+
+       /*
+        * We can end up here with a locked AGF. If we failed, the caller is
+        * likely going to try to allocate again with different parameters, and
+        * that can widen the AGs that are searched for free space. If we have
+        * to do BMBT block allocation, we have to do a new allocation.
+        *
+        * Hence leaving this function with the AGF locked opens up potential
+        * ABBA AGF deadlocks because a future allocation attempt in this
+        * transaction may attempt to lock a lower number AGF.
+        *
+        * We can't release the AGF until the transaction is commited, so at
+        * this point we must update the "first allocation" tracker to point at
+        * this AG if the tracker is empty or points to a lower AG. This allows
+        * the next allocation attempt to be modified appropriately to avoid
+        * deadlocks.
+        */
+       if (args->agbp &&
+           (args->tp->t_highest_agno == NULLAGNUMBER ||
+            args->agno > minimum_agno))
+               args->tp->t_highest_agno = args->agno;
+
+       /*
+        * If the allocation failed with an error or we had an ENOSPC result,
+        * preserve the returned error whilst also marking the allocation result
+        * as "no extent allocated". This ensures that callers that fail to
+        * capture the error will still treat it as a failed allocation.
+        */
+       if (alloc_error || args->agbno == NULLAGBLOCK) {
+               args->fsbno = NULLFSBLOCK;
+               error = alloc_error;
+               goto out_drop_perag;
+       }
+
+       args->fsbno = XFS_AGB_TO_FSB(mp, args->agno, args->agbno);
+
+       ASSERT(args->len >= args->minlen);
+       ASSERT(args->len <= args->maxlen);
+       ASSERT(args->agbno % args->alignment == 0);
+       XFS_AG_CHECK_DADDR(mp, XFS_FSB_TO_DADDR(mp, args->fsbno), args->len);
+
+       /* if not file data, insert new block into the reverse map btree */
+       if (!xfs_rmap_should_skip_owner_update(&args->oinfo)) {
+               error = xfs_rmap_alloc(args->tp, args->agbp, args->pag,
+                                      args->agbno, args->len, &args->oinfo);
+               if (error)
+                       goto out_drop_perag;
+       }
+
+       if (!args->wasfromfl) {
+               error = xfs_alloc_update_counters(args->tp, args->agbp,
+                                                 -((long)(args->len)));
+               if (error)
+                       goto out_drop_perag;
+
+               ASSERT(!xfs_extent_busy_search(mp, args->pag, args->agbno,
+                               args->len));
+       }
+
+       xfs_ag_resv_alloc_extent(args->pag, args->resv, args);
+
+       XFS_STATS_INC(mp, xs_allocx);
+       XFS_STATS_ADD(mp, xs_allocb, args->len);
+
+       trace_xfs_alloc_vextent_finish(args);
+
+out_drop_perag:
+       if (drop_perag && args->pag) {
+               xfs_perag_rele(args->pag);
+               args->pag = NULL;
+       }
+       return error;
+}
+
+/*
+ * Allocate within a single AG only. This uses a best-fit length algorithm so if
+ * you need an exact sized allocation without locality constraints, this is the
+ * fastest way to do it.
+ *
+ * Caller is expected to hold a perag reference in args->pag.
+ */
+int
+xfs_alloc_vextent_this_ag(
+       struct xfs_alloc_arg    *args,
+       xfs_agnumber_t          agno)
+{
+       struct xfs_mount        *mp = args->mp;
+       xfs_agnumber_t          minimum_agno;
+       int                     error;
+
+       ASSERT(args->pag != NULL);
+       ASSERT(args->pag->pag_agno == agno);
+
+       args->agno = agno;
+       args->agbno = 0;
+
+       trace_xfs_alloc_vextent_this_ag(args);
+
+       error = xfs_alloc_vextent_check_args(args, XFS_AGB_TO_FSB(mp, agno, 0),
+                       &minimum_agno);
+       if (error) {
+               if (error == -ENOSPC)
+                       return 0;
+               return error;
+       }
+
+       error = xfs_alloc_vextent_prepare_ag(args, 0);
+       if (!error && args->agbp)
+               error = xfs_alloc_ag_vextent_size(args);
+
+       return xfs_alloc_vextent_finish(args, minimum_agno, error, false);
+}
+
+/*
+ * Iterate all AGs trying to allocate an extent starting from @start_ag.
+ *
+ * If the incoming allocation type is XFS_ALLOCTYPE_NEAR_BNO, it means the
+ * allocation attempts in @start_agno have locality information. If we fail to
+ * allocate in that AG, then we revert to anywhere-in-AG for all the other AGs
+ * we attempt to allocation in as there is no locality optimisation possible for
+ * those allocations.
+ *
+ * On return, args->pag may be left referenced if we finish before the "all
+ * failed" return point. The allocation finish still needs the perag, and
+ * so the caller will release it once they've finished the allocation.
+ *
+ * When we wrap the AG iteration at the end of the filesystem, we have to be
+ * careful not to wrap into AGs below ones we already have locked in the
+ * transaction if we are doing a blocking iteration. This will result in an
+ * out-of-order locking of AGFs and hence can cause deadlocks.
+ */
+static int
+xfs_alloc_vextent_iterate_ags(
+       struct xfs_alloc_arg    *args,
+       xfs_agnumber_t          minimum_agno,
+       xfs_agnumber_t          start_agno,
+       xfs_agblock_t           target_agbno,
+       uint32_t                flags)
+{
+       struct xfs_mount        *mp = args->mp;
+       xfs_agnumber_t          restart_agno = minimum_agno;
+       xfs_agnumber_t          agno;
+       int                     error = 0;
+
+       if (flags & XFS_ALLOC_FLAG_TRYLOCK)
+               restart_agno = 0;
+restart:
+       for_each_perag_wrap_range(mp, start_agno, restart_agno,
+                       mp->m_sb.sb_agcount, agno, args->pag) {
+               args->agno = agno;
+               error = xfs_alloc_vextent_prepare_ag(args, flags);
+               if (error)
                        break;
+               if (!args->agbp) {
+                       trace_xfs_alloc_vextent_loopfailed(args);
+                       continue;
                }
-               args->agbno = XFS_FSB_TO_AGBNO(mp, args->fsbno);
-               if ((error = xfs_alloc_ag_vextent(args)))
-                       goto error0;
-               break;
-       case XFS_ALLOCTYPE_START_BNO:
-               /*
-                * Try near allocation first, then anywhere-in-ag after
-                * the first a.g. fails.
-                */
-               if ((args->datatype & XFS_ALLOC_INITIAL_USER_DATA) &&
-                   xfs_is_inode32(mp)) {
-                       args->fsbno = XFS_AGB_TO_FSB(mp,
-                                       ((mp->m_agfrotor / rotorstep) %
-                                       mp->m_sb.sb_agcount), 0);
-                       bump_rotor = 1;
-               }
-               args->agbno = XFS_FSB_TO_AGBNO(mp, args->fsbno);
-               args->type = XFS_ALLOCTYPE_NEAR_BNO;
-               fallthrough;
-       case XFS_ALLOCTYPE_FIRST_AG:
+
                /*
-                * Rotate through the allocation groups looking for a winner.
+                * Allocation is supposed to succeed now, so break out of the
+                * loop regardless of whether we succeed or not.
                 */
-               if (type == XFS_ALLOCTYPE_FIRST_AG) {
-                       /*
-                        * Start with allocation group given by bno.
-                        */
-                       args->agno = XFS_FSB_TO_AGNO(mp, args->fsbno);
-                       args->type = XFS_ALLOCTYPE_THIS_AG;
-                       sagno = 0;
-                       flags = 0;
+               if (args->agno == start_agno && target_agbno) {
+                       args->agbno = target_agbno;
+                       error = xfs_alloc_ag_vextent_near(args);
                } else {
-                       /*
-                        * Start with the given allocation group.
-                        */
-                       args->agno = sagno = XFS_FSB_TO_AGNO(mp, args->fsbno);
-                       flags = XFS_ALLOC_FLAG_TRYLOCK;
-               }
-               /*
-                * Loop over allocation groups twice; first time with
-                * trylock set, second time without.
-                */
-               for (;;) {
-                       args->pag = xfs_perag_get(mp, args->agno);
-                       error = xfs_alloc_fix_freelist(args, flags);
-                       if (error) {
-                               trace_xfs_alloc_vextent_nofix(args);
-                               goto error0;
-                       }
-                       /*
-                        * If we get a buffer back then the allocation will fly.
-                        */
-                       if (args->agbp) {
-                               if ((error = xfs_alloc_ag_vextent(args)))
-                                       goto error0;
-                               break;
-                       }
-
-                       trace_xfs_alloc_vextent_loopfailed(args);
-
-                       /*
-                        * Didn't work, figure out the next iteration.
-                        */
-                       if (args->agno == sagno &&
-                           type == XFS_ALLOCTYPE_START_BNO)
-                               args->type = XFS_ALLOCTYPE_THIS_AG;
-                       /*
-                       * For the first allocation, we can try any AG to get
-                       * space.  However, if we already have allocated a
-                       * block, we don't want to try AGs whose number is below
-                       * sagno. Otherwise, we may end up with out-of-order
-                       * locking of AGF, which might cause deadlock.
-                       */
-                       if (++(args->agno) == mp->m_sb.sb_agcount) {
-                               if (args->tp->t_firstblock != NULLFSBLOCK)
-                                       args->agno = sagno;
-                               else
-                                       args->agno = 0;
-                       }
-                       /*
-                        * Reached the starting a.g., must either be done
-                        * or switch to non-trylock mode.
-                        */
-                       if (args->agno == sagno) {
-                               if (flags == 0) {
-                                       args->agbno = NULLAGBLOCK;
-                                       trace_xfs_alloc_vextent_allfailed(args);
-                                       break;
-                               }
-
-                               flags = 0;
-                               if (type == XFS_ALLOCTYPE_START_BNO) {
-                                       args->agbno = XFS_FSB_TO_AGBNO(mp,
-                                               args->fsbno);
-                                       args->type = XFS_ALLOCTYPE_NEAR_BNO;
-                               }
-                       }
-                       xfs_perag_put(args->pag);
-               }
-               if (bump_rotor) {
-                       if (args->agno == sagno)
-                               mp->m_agfrotor = (mp->m_agfrotor + 1) %
-                                       (mp->m_sb.sb_agcount * rotorstep);
-                       else
-                               mp->m_agfrotor = (args->agno * rotorstep + 1) %
-                                       (mp->m_sb.sb_agcount * rotorstep);
+                       args->agbno = 0;
+                       error = xfs_alloc_ag_vextent_size(args);
                }
                break;
-       default:
-               ASSERT(0);
-               /* NOTREACHED */
        }
-       if (args->agbno == NULLAGBLOCK)
-               args->fsbno = NULLFSBLOCK;
-       else {
-               args->fsbno = XFS_AGB_TO_FSB(mp, args->agno, args->agbno);
-#ifdef DEBUG
-               ASSERT(args->len >= args->minlen);
-               ASSERT(args->len <= args->maxlen);
-               ASSERT(args->agbno % args->alignment == 0);
-               XFS_AG_CHECK_DADDR(mp, XFS_FSB_TO_DADDR(mp, args->fsbno),
-                       args->len);
-#endif
+       if (error) {
+               xfs_perag_rele(args->pag);
+               args->pag = NULL;
+               return error;
+       }
+       if (args->agbp)
+               return 0;
 
+       /*
+        * We didn't find an AG we can alloation from. If we were given
+        * constraining flags by the caller, drop them and retry the allocation
+        * without any constraints being set.
+        */
+       if (flags) {
+               flags = 0;
+               restart_agno = minimum_agno;
+               goto restart;
        }
-       xfs_perag_put(args->pag);
+
+       ASSERT(args->pag == NULL);
+       trace_xfs_alloc_vextent_allfailed(args);
        return 0;
-error0:
-       xfs_perag_put(args->pag);
-       return error;
+}
+
+/*
+ * Iterate from the AGs from the start AG to the end of the filesystem, trying
+ * to allocate blocks. It starts with a near allocation attempt in the initial
+ * AG, then falls back to anywhere-in-ag after the first AG fails. It will wrap
+ * back to zero if allowed by previous allocations in this transaction,
+ * otherwise will wrap back to the start AG and run a second blocking pass to
+ * the end of the filesystem.
+ */
+int
+xfs_alloc_vextent_start_ag(
+       struct xfs_alloc_arg    *args,
+       xfs_fsblock_t           target)
+{
+       struct xfs_mount        *mp = args->mp;
+       xfs_agnumber_t          minimum_agno;
+       xfs_agnumber_t          start_agno;
+       xfs_agnumber_t          rotorstep = xfs_rotorstep;
+       bool                    bump_rotor = false;
+       int                     error;
+
+       ASSERT(args->pag == NULL);
+
+       args->agno = NULLAGNUMBER;
+       args->agbno = NULLAGBLOCK;
+
+       trace_xfs_alloc_vextent_start_ag(args);
+
+       error = xfs_alloc_vextent_check_args(args, target, &minimum_agno);
+       if (error) {
+               if (error == -ENOSPC)
+                       return 0;
+               return error;
+       }
+
+       if ((args->datatype & XFS_ALLOC_INITIAL_USER_DATA) &&
+           xfs_is_inode32(mp)) {
+               target = XFS_AGB_TO_FSB(mp,
+                               ((mp->m_agfrotor / rotorstep) %
+                               mp->m_sb.sb_agcount), 0);
+               bump_rotor = 1;
+       }
+
+       start_agno = max(minimum_agno, XFS_FSB_TO_AGNO(mp, target));
+       error = xfs_alloc_vextent_iterate_ags(args, minimum_agno, start_agno,
+                       XFS_FSB_TO_AGBNO(mp, target), XFS_ALLOC_FLAG_TRYLOCK);
+
+       if (bump_rotor) {
+               if (args->agno == start_agno)
+                       mp->m_agfrotor = (mp->m_agfrotor + 1) %
+                               (mp->m_sb.sb_agcount * rotorstep);
+               else
+                       mp->m_agfrotor = (args->agno * rotorstep + 1) %
+                               (mp->m_sb.sb_agcount * rotorstep);
+       }
+
+       return xfs_alloc_vextent_finish(args, minimum_agno, error, true);
+}
+
+/*
+ * Iterate from the agno indicated via @target through to the end of the
+ * filesystem attempting blocking allocation. This does not wrap or try a second
+ * pass, so will not recurse into AGs lower than indicated by the target.
+ */
+int
+xfs_alloc_vextent_first_ag(
+       struct xfs_alloc_arg    *args,
+       xfs_fsblock_t           target)
+ {
+       struct xfs_mount        *mp = args->mp;
+       xfs_agnumber_t          minimum_agno;
+       xfs_agnumber_t          start_agno;
+       int                     error;
+
+       ASSERT(args->pag == NULL);
+
+       args->agno = NULLAGNUMBER;
+       args->agbno = NULLAGBLOCK;
+
+       trace_xfs_alloc_vextent_first_ag(args);
+
+       error = xfs_alloc_vextent_check_args(args, target, &minimum_agno);
+       if (error) {
+               if (error == -ENOSPC)
+                       return 0;
+               return error;
+       }
+
+       start_agno = max(minimum_agno, XFS_FSB_TO_AGNO(mp, target));
+       error = xfs_alloc_vextent_iterate_ags(args, minimum_agno, start_agno,
+                       XFS_FSB_TO_AGBNO(mp, target), 0);
+       return xfs_alloc_vextent_finish(args, minimum_agno, error, true);
+}
+
+/*
+ * Allocate at the exact block target or fail. Caller is expected to hold a
+ * perag reference in args->pag.
+ */
+int
+xfs_alloc_vextent_exact_bno(
+       struct xfs_alloc_arg    *args,
+       xfs_fsblock_t           target)
+{
+       struct xfs_mount        *mp = args->mp;
+       xfs_agnumber_t          minimum_agno;
+       int                     error;
+
+       ASSERT(args->pag != NULL);
+       ASSERT(args->pag->pag_agno == XFS_FSB_TO_AGNO(mp, target));
+
+       args->agno = XFS_FSB_TO_AGNO(mp, target);
+       args->agbno = XFS_FSB_TO_AGBNO(mp, target);
+
+       trace_xfs_alloc_vextent_exact_bno(args);
+
+       error = xfs_alloc_vextent_check_args(args, target, &minimum_agno);
+       if (error) {
+               if (error == -ENOSPC)
+                       return 0;
+               return error;
+       }
+
+       error = xfs_alloc_vextent_prepare_ag(args, 0);
+       if (!error && args->agbp)
+               error = xfs_alloc_ag_vextent_exact(args);
+
+       return xfs_alloc_vextent_finish(args, minimum_agno, error, false);
+}
+
+/*
+ * Allocate an extent as close to the target as possible. If there are not
+ * viable candidates in the AG, then fail the allocation.
+ *
+ * Caller may or may not have a per-ag reference in args->pag.
+ */
+int
+xfs_alloc_vextent_near_bno(
+       struct xfs_alloc_arg    *args,
+       xfs_fsblock_t           target)
+{
+       struct xfs_mount        *mp = args->mp;
+       xfs_agnumber_t          minimum_agno;
+       bool                    needs_perag = args->pag == NULL;
+       int                     error;
+
+       if (!needs_perag)
+               ASSERT(args->pag->pag_agno == XFS_FSB_TO_AGNO(mp, target));
+
+       args->agno = XFS_FSB_TO_AGNO(mp, target);
+       args->agbno = XFS_FSB_TO_AGBNO(mp, target);
+
+       trace_xfs_alloc_vextent_near_bno(args);
+
+       error = xfs_alloc_vextent_check_args(args, target, &minimum_agno);
+       if (error) {
+               if (error == -ENOSPC)
+                       return 0;
+               return error;
+       }
+
+       if (needs_perag)
+               args->pag = xfs_perag_grab(mp, args->agno);
+
+       error = xfs_alloc_vextent_prepare_ag(args, 0);
+       if (!error && args->agbp)
+               error = xfs_alloc_ag_vextent_near(args);
+
+       return xfs_alloc_vextent_finish(args, minimum_agno, error, needs_perag);
 }
 
 /* Ensure that the freelist is at full capacity. */
@@ -3327,7 +3658,8 @@ xfs_free_extent_fix_freelist(
 int
 __xfs_free_extent(
        struct xfs_trans                *tp,
-       xfs_fsblock_t                   bno,
+       struct xfs_perag                *pag,
+       xfs_agblock_t                   agbno,
        xfs_extlen_t                    len,
        const struct xfs_owner_info     *oinfo,
        enum xfs_ag_resv_type           type,
@@ -3335,12 +3667,9 @@ __xfs_free_extent(
 {
        struct xfs_mount                *mp = tp->t_mountp;
        struct xfs_buf                  *agbp;
-       xfs_agnumber_t                  agno = XFS_FSB_TO_AGNO(mp, bno);
-       xfs_agblock_t                   agbno = XFS_FSB_TO_AGBNO(mp, bno);
        struct xfs_agf                  *agf;
        int                             error;
        unsigned int                    busy_flags = 0;
-       struct xfs_perag                *pag;
 
        ASSERT(len != 0);
        ASSERT(type != XFS_AG_RESV_AGFL);
@@ -3349,10 +3678,9 @@ __xfs_free_extent(
                        XFS_ERRTAG_FREE_EXTENT))
                return -EIO;
 
-       pag = xfs_perag_get(mp, agno);
        error = xfs_free_extent_fix_freelist(tp, pag, &agbp);
        if (error)
-               goto err;
+               return error;
        agf = agbp->b_addr;
 
        if (XFS_IS_CORRUPT(mp, agbno >= mp->m_sb.sb_agblocks)) {
@@ -3366,20 +3694,18 @@ __xfs_free_extent(
                goto err_release;
        }
 
-       error = xfs_free_ag_extent(tp, agbp, agno, agbno, len, oinfo, type);
+       error = xfs_free_ag_extent(tp, agbp, pag->pag_agno, agbno, len, oinfo,
+                       type);
        if (error)
                goto err_release;
 
        if (skip_discard)
                busy_flags |= XFS_EXTENT_BUSY_SKIP_DISCARD;
        xfs_extent_busy_insert(tp, pag, agbno, len, busy_flags);
-       xfs_perag_put(pag);
        return 0;
 
 err_release:
        xfs_trans_brelse(tp, agbp);
-err:
-       xfs_perag_put(pag);
        return error;
 }
 
@@ -3397,9 +3723,13 @@ xfs_alloc_query_range_helper(
 {
        struct xfs_alloc_query_range_info       *query = priv;
        struct xfs_alloc_rec_incore             irec;
+       xfs_failaddr_t                          fa;
+
+       xfs_alloc_btrec_to_irec(rec, &irec);
+       fa = xfs_alloc_check_irec(cur, &irec);
+       if (fa)
+               return xfs_alloc_complain_bad_rec(cur, fa, &irec);
 
-       irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
-       irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
        return query->fn(cur, &irec, query->priv);
 }
 
@@ -3440,13 +3770,16 @@ xfs_alloc_query_all(
        return xfs_btree_query_all(cur, xfs_alloc_query_range_helper, &query);
 }
 
-/* Is there a record covering a given extent? */
+/*
+ * Scan part of the keyspace of the free space and tell us if the area has no
+ * records, is fully mapped by records, or is partially filled.
+ */
 int
-xfs_alloc_has_record(
+xfs_alloc_has_records(
        struct xfs_btree_cur    *cur,
        xfs_agblock_t           bno,
        xfs_extlen_t            len,
-       bool                    *exists)
+       enum xbtree_recpacking  *outcome)
 {
        union xfs_btree_irec    low;
        union xfs_btree_irec    high;
@@ -3456,7 +3789,7 @@ xfs_alloc_has_record(
        memset(&high, 0xFF, sizeof(high));
        high.a.ar_startblock = bno + len - 1;
 
-       return xfs_btree_has_record(cur, &low, &high, exists);
+       return xfs_btree_has_records(cur, &low, &high, NULL, outcome);
 }
 
 /*
@@ -3495,3 +3828,20 @@ xfs_agfl_walk(
 
        return 0;
 }
+
+int __init
+xfs_extfree_intent_init_cache(void)
+{
+       xfs_extfree_item_cache = kmem_cache_create("xfs_extfree_intent",
+                       sizeof(struct xfs_extent_free_item),
+                       0, 0, NULL);
+
+       return xfs_extfree_item_cache != NULL ? 0 : -ENOMEM;
+}
+
+void
+xfs_extfree_intent_destroy_cache(void)
+{
+       kmem_cache_destroy(xfs_extfree_item_cache);
+       xfs_extfree_item_cache = NULL;
+}