]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_bmap.c
xfs: remove unneeded parameter from XFS_TEST_ERROR
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_bmap.c
index b65f15c733c882804d2a1e1b6af3e59a27a36ae7..d44a4e9e720d95a2409f4c8e14e54e4cc3c8e5a3 100644 (file)
@@ -754,7 +754,6 @@ xfs_bmap_extents_to_btree(
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
        } else if (dfops->dop_low) {
-try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = *firstblock;
        } else {
@@ -770,28 +769,16 @@ try_another_ag:
                return error;
        }
 
-       /*
-        * During a CoW operation, the allocation and bmbt updates occur in
-        * different transactions.  The mapping code tries to put new bmbt
-        * blocks near extents being mapped, but the only way to guarantee this
-        * is if the alloc and the mapping happen in a single transaction that
-        * has a block reservation.  That isn't the case here, so if we run out
-        * of space we'll try again with another AG.
-        */
-       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
-           args.fsbno == NULLFSBLOCK &&
-           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
-               dfops->dop_low = true;
-               goto try_another_ag;
+       if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) {
+               xfs_iroot_realloc(ip, -1, whichfork);
+               xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+               return -ENOSPC;
        }
        /*
         * Allocation can't fail, the space was reserved.
         */
-       ASSERT(args.fsbno != NULLFSBLOCK);
        ASSERT(*firstblock == NULLFSBLOCK ||
-              args.agno == XFS_FSB_TO_AGNO(mp, *firstblock) ||
-              (dfops->dop_low &&
-               args.agno > XFS_FSB_TO_AGNO(mp, *firstblock)));
+              args.agno >= XFS_FSB_TO_AGNO(mp, *firstblock));
        *firstblock = cur->bc_private.b.firstblock = args.fsbno;
        cur->bc_private.b.allocated++;
        ip->i_d.di_nblocks++;
@@ -914,7 +901,6 @@ xfs_bmap_local_to_extents(
         * file currently fits in an inode.
         */
        if (*firstblock == NULLFSBLOCK) {
-try_another_ag:
                args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
                args.type = XFS_ALLOCTYPE_START_BNO;
        } else {
@@ -927,19 +913,6 @@ try_another_ag:
        if (error)
                goto done;
 
-       /*
-        * During a CoW operation, the allocation and bmbt updates occur in
-        * different transactions.  The mapping code tries to put new bmbt
-        * blocks near extents being mapped, but the only way to guarantee this
-        * is if the alloc and the mapping happen in a single transaction that
-        * has a block reservation.  That isn't the case here, so if we run out
-        * of space we'll try again with another AG.
-        */
-       if (xfs_sb_version_hasreflink(&ip->i_mount->m_sb) &&
-           args.fsbno == NULLFSBLOCK &&
-           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
-               goto try_another_ag;
-       }
        /* Can't fail, the space was reserved. */
        ASSERT(args.fsbno != NULLFSBLOCK);
        ASSERT(args.len == 1);
@@ -1249,7 +1222,6 @@ xfs_bmap_read_extents(
        xfs_fsblock_t           bno;    /* block # of "block" */
        xfs_buf_t               *bp;    /* buffer for "block" */
        int                     error;  /* error return value */
-       xfs_exntfmt_t           exntf;  /* XFS_EXTFMT_NOSTATE, if checking */
        xfs_extnum_t            i, j;   /* index into the extents list */
        xfs_ifork_t             *ifp;   /* fork structure */
        int                     level;  /* btree level, for checking */
@@ -1260,8 +1232,6 @@ xfs_bmap_read_extents(
 
        mp = ip->i_mount;
        ifp = XFS_IFORK_PTR(ip, whichfork);
-       exntf = (whichfork != XFS_DATA_FORK) ? XFS_EXTFMT_NOSTATE :
-                                       XFS_EXTFMT_INODE(ip);
        block = ifp->if_broot;
        /*
         * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
@@ -1301,7 +1271,6 @@ xfs_bmap_read_extents(
                xfs_bmbt_rec_t  *frp;
                xfs_fsblock_t   nextbno;
                xfs_extnum_t    num_recs;
-               xfs_extnum_t    start;
 
                num_recs = xfs_btree_get_numrecs(block);
                if (unlikely(i + num_recs > room)) {
@@ -1324,23 +1293,13 @@ xfs_bmap_read_extents(
                 * Copy records into the extent records.
                 */
                frp = XFS_BMBT_REC_ADDR(mp, block, 1);
-               start = i;
                for (j = 0; j < num_recs; j++, i++, frp++) {
                        xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
                        trp->l0 = be64_to_cpu(frp->l0);
                        trp->l1 = be64_to_cpu(frp->l1);
-               }
-               if (exntf == XFS_EXTFMT_NOSTATE) {
-                       /*
-                        * Check all attribute bmap btree records and
-                        * any "older" data bmap btree records for a
-                        * set bit in the "extent flag" position.
-                        */
-                       if (unlikely(xfs_check_nostate_extents(ifp,
-                                       start, num_recs))) {
+                       if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
                                XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
-                                                XFS_ERRLEVEL_LOW,
-                                                ip->i_mount);
+                                                XFS_ERRLEVEL_LOW, mp);
                                goto error0;
                        }
                }
@@ -2095,8 +2054,10 @@ xfs_bmap_add_extent_delay_real(
                }
                temp = xfs_bmap_worst_indlen(bma->ip, temp);
                temp2 = xfs_bmap_worst_indlen(bma->ip, temp2);
-               diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) -
-                       (bma->cur ? bma->cur->bc_private.b.allocated : 0));
+               diff = (int)(temp + temp2 -
+                            (startblockval(PREV.br_startblock) -
+                             (bma->cur ?
+                              bma->cur->bc_private.b.allocated : 0)));
                if (diff > 0) {
                        error = xfs_mod_fdblocks(bma->ip->i_mount,
                                                 -((int64_t)diff), false);
@@ -2153,7 +2114,6 @@ xfs_bmap_add_extent_delay_real(
                temp = da_new;
                if (bma->cur)
                        temp += bma->cur->bc_private.b.allocated;
-               ASSERT(temp <= da_old);
                if (temp < da_old)
                        xfs_mod_fdblocks(bma->ip->i_mount,
                                        (int64_t)(da_old - temp), false);
@@ -2868,27 +2828,30 @@ xfs_bmap_add_extent_hole_delay(
  */
 STATIC int                             /* error */
 xfs_bmap_add_extent_hole_real(
-       struct xfs_bmalloca     *bma,
-       int                     whichfork)
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       int                     whichfork,
+       xfs_extnum_t            *idx,
+       struct xfs_btree_cur    **curp,
+       struct xfs_bmbt_irec    *new,
+       xfs_fsblock_t           *first,
+       struct xfs_defer_ops    *dfops,
+       int                     *logflagsp)
 {
-       struct xfs_bmbt_irec    *new = &bma->got;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_btree_cur    *cur = *curp;
        int                     error;  /* error return value */
        int                     i;      /* temp state */
-       xfs_ifork_t             *ifp;   /* inode fork pointer */
        xfs_bmbt_irec_t         left;   /* left neighbor extent entry */
        xfs_bmbt_irec_t         right;  /* right neighbor extent entry */
        int                     rval=0; /* return value (logging flags) */
        int                     state;  /* state bits, accessed thru macros */
-       struct xfs_mount        *mp;
-
-       mp = bma->ip->i_mount;
-       ifp = XFS_IFORK_PTR(bma->ip, whichfork);
 
-       ASSERT(bma->idx >= 0);
-       ASSERT(bma->idx <= xfs_iext_count(ifp));
+       ASSERT(*idx >= 0);
+       ASSERT(*idx <= xfs_iext_count(ifp));
        ASSERT(!isnullstartblock(new->br_startblock));
-       ASSERT(!bma->cur ||
-              !(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
+       ASSERT(!cur || !(cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
 
        XFS_STATS_INC(mp, xs_add_exlist);
 
@@ -2901,9 +2864,9 @@ xfs_bmap_add_extent_hole_real(
        /*
         * Check and set flags if this segment has a left neighbor.
         */
-       if (bma->idx > 0) {
+       if (*idx > 0) {
                state |= BMAP_LEFT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx - 1), &left);
+               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &left);
                if (isnullstartblock(left.br_startblock))
                        state |= BMAP_LEFT_DELAY;
        }
@@ -2912,9 +2875,9 @@ xfs_bmap_add_extent_hole_real(
         * Check and set flags if this segment has a current value.
         * Not true if we're inserting into the "hole" at eof.
         */
-       if (bma->idx < xfs_iext_count(ifp)) {
+       if (*idx < xfs_iext_count(ifp)) {
                state |= BMAP_RIGHT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx), &right);
+               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
                if (isnullstartblock(right.br_startblock))
                        state |= BMAP_RIGHT_DELAY;
        }
@@ -2951,36 +2914,36 @@ xfs_bmap_add_extent_hole_real(
                 * left and on the right.
                 * Merge all three into a single extent record.
                 */
-               --bma->idx;
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, bma->idx),
+               --*idx;
+               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
                        left.br_blockcount + new->br_blockcount +
                        right.br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
 
-               xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
+               xfs_iext_remove(ip, *idx + 1, 1, state);
 
-               XFS_IFORK_NEXT_SET(bma->ip, whichfork,
-                       XFS_IFORK_NEXTENTS(bma->ip, whichfork) - 1);
-               if (bma->cur == NULL) {
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                       XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
+               if (cur == NULL) {
                        rval = XFS_ILOG_CORE | xfs_ilog_fext(whichfork);
                } else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur, right.br_startoff,
+                       error = xfs_bmbt_lookup_eq(cur, right.br_startoff,
                                        right.br_startblock, right.br_blockcount,
                                        &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_btree_delete(bma->cur, &i);
+                       error = xfs_btree_delete(cur, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_btree_decrement(bma->cur, 0, &i);
+                       error = xfs_btree_decrement(cur, 0, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, left.br_startoff,
+                       error = xfs_bmbt_update(cur, left.br_startoff,
                                        left.br_startblock,
                                        left.br_blockcount +
                                                new->br_blockcount +
@@ -2997,23 +2960,23 @@ xfs_bmap_add_extent_hole_real(
                 * on the left.
                 * Merge the new allocation with the left neighbor.
                 */
-               --bma->idx;
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, bma->idx),
+               --*idx;
+               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
                        left.br_blockcount + new->br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
 
-               if (bma->cur == NULL) {
+               if (cur == NULL) {
                        rval = xfs_ilog_fext(whichfork);
                } else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur, left.br_startoff,
+                       error = xfs_bmbt_lookup_eq(cur, left.br_startoff,
                                        left.br_startblock, left.br_blockcount,
                                        &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, left.br_startoff,
+                       error = xfs_bmbt_update(cur, left.br_startoff,
                                        left.br_startblock,
                                        left.br_blockcount +
                                                new->br_blockcount,
@@ -3029,25 +2992,25 @@ xfs_bmap_add_extent_hole_real(
                 * on the right.
                 * Merge the new allocation with the right neighbor.
                 */
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, bma->idx),
+               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
+               xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
                        new->br_startoff, new->br_startblock,
                        new->br_blockcount + right.br_blockcount,
                        right.br_state);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
 
-               if (bma->cur == NULL) {
+               if (cur == NULL) {
                        rval = xfs_ilog_fext(whichfork);
                } else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur,
+                       error = xfs_bmbt_lookup_eq(cur,
                                        right.br_startoff,
                                        right.br_startblock,
                                        right.br_blockcount, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, new->br_startoff,
+                       error = xfs_bmbt_update(cur, new->br_startoff,
                                        new->br_startblock,
                                        new->br_blockcount +
                                                right.br_blockcount,
@@ -3063,22 +3026,22 @@ xfs_bmap_add_extent_hole_real(
                 * real allocation.
                 * Insert a new entry.
                 */
-               xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
-               XFS_IFORK_NEXT_SET(bma->ip, whichfork,
-                       XFS_IFORK_NEXTENTS(bma->ip, whichfork) + 1);
-               if (bma->cur == NULL) {
+               xfs_iext_insert(ip, *idx, 1, new, state);
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                       XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
+               if (cur == NULL) {
                        rval = XFS_ILOG_CORE | xfs_ilog_fext(whichfork);
                } else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur,
+                       error = xfs_bmbt_lookup_eq(cur,
                                        new->br_startoff,
                                        new->br_startblock,
                                        new->br_blockcount, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
-                       bma->cur->bc_rec.b.br_state = new->br_state;
-                       error = xfs_btree_insert(bma->cur, &i);
+                       cur->bc_rec.b.br_state = new->br_state;
+                       error = xfs_btree_insert(cur, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
@@ -3087,30 +3050,30 @@ xfs_bmap_add_extent_hole_real(
        }
 
        /* add reverse mapping */
-       error = xfs_rmap_map_extent(mp, bma->dfops, bma->ip, whichfork, new);
+       error = xfs_rmap_map_extent(mp, dfops, ip, whichfork, new);
        if (error)
                goto done;
 
        /* convert to a btree if necessary */
-       if (xfs_bmap_needs_btree(bma->ip, whichfork)) {
+       if (xfs_bmap_needs_btree(ip, whichfork)) {
                int     tmp_logflags;   /* partial log flag return val */
 
-               ASSERT(bma->cur == NULL);
-               error = xfs_bmap_extents_to_btree(bma->tp, bma->ip,
-                               bma->firstblock, bma->dfops, &bma->cur,
+               ASSERT(cur == NULL);
+               error = xfs_bmap_extents_to_btree(tp, ip, first, dfops, curp,
                                0, &tmp_logflags, whichfork);
-               bma->logflags |= tmp_logflags;
+               *logflagsp |= tmp_logflags;
+               cur = *curp;
                if (error)
                        goto done;
        }
 
        /* clear out the allocated field, done with it now in any case. */
-       if (bma->cur)
-               bma->cur->bc_private.b.allocated = 0;
+       if (cur)
+               cur->bc_private.b.allocated = 0;
 
-       xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
+       xfs_bmap_check_leaf_extents(cur, ip, whichfork);
 done:
-       bma->logflags |= rval;
+       *logflagsp |= rval;
        return error;
 }
 
@@ -3813,17 +3776,13 @@ xfs_bmap_btalloc(
                 * the first block that was allocated.
                 */
                ASSERT(*ap->firstblock == NULLFSBLOCK ||
-                      XFS_FSB_TO_AGNO(mp, *ap->firstblock) ==
-                      XFS_FSB_TO_AGNO(mp, args.fsbno) ||
-                      (ap->dfops->dop_low &&
-                       XFS_FSB_TO_AGNO(mp, *ap->firstblock) <
-                       XFS_FSB_TO_AGNO(mp, args.fsbno)));
+                      XFS_FSB_TO_AGNO(mp, *ap->firstblock) <=
+                      XFS_FSB_TO_AGNO(mp, args.fsbno));
 
                ap->blkno = args.fsbno;
                if (*ap->firstblock == NULLFSBLOCK)
                        *ap->firstblock = args.fsbno;
-               ASSERT(nullfb || fb_agno == args.agno ||
-                      (ap->dfops->dop_low && fb_agno < args.agno));
+               ASSERT(nullfb || fb_agno <= args.agno);
                ap->length = args.len;
                if (!(ap->flags & XFS_BMAPI_COWFORK))
                        ap->ip->i_d.di_nblocks += args.len;
@@ -3845,60 +3804,6 @@ xfs_bmap_btalloc(
        return 0;
 }
 
-/*
- * 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_alloc(
-       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);
-       args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
-       if (args.agno >= mp->m_sb.sb_agcount ||
-           args.agbno >= mp->m_sb.sb_agblocks)
-               return -EFSCORRUPTED;
-
-       /* "Allocate" the extent from the range we passed in. */
-       trace_xfs_bmap_remap_alloc(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.datatype = ap->datatype;
-       args.pag = xfs_perag_get(args.mp, args.agno);
-       ASSERT(args.pag);
-
-       /*
-        * The freelist fixing code will decline the allocation if
-        * the size and shape of the free space doesn't allow for
-        * allocating the extent and updating all the metadata that
-        * happens during an allocation.  We're remapping, not
-        * allocating, so skip that check by pretending to be freeing.
-        */
-       error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
-       xfs_perag_put(args.pag);
-       if (error)
-               trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
-       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.
@@ -3907,8 +3812,6 @@ STATIC int
 xfs_bmap_alloc(
        struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
 {
-       if (ap->flags & XFS_BMAPI_REMAP)
-               return xfs_bmap_remap_alloc(ap);
        if (XFS_IS_REALTIME_INODE(ap->ip) &&
            xfs_alloc_is_userdata(ap->datatype))
                return xfs_bmap_rtalloc(ap);
@@ -4080,7 +3983,7 @@ xfs_bmapi_read(
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
             XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
-            mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
+            mp, XFS_ERRTAG_BMAPIFORMAT))) {
                XFS_ERROR_REPORT("xfs_bmapi_read", XFS_ERRLEVEL_LOW, mp);
                return -EFSCORRUPTED;
        }
@@ -4147,6 +4050,19 @@ xfs_bmapi_read(
        return 0;
 }
 
+/*
+ * Add a delayed allocation extent to an inode. Blocks are reserved from the
+ * global pool and the extent inserted into the inode in-core extent tree.
+ *
+ * On entry, got refers to the first extent beyond the offset of the extent to
+ * allocate or eof is specified if no such extent exists. On return, got refers
+ * to the extent record that was inserted to the inode fork.
+ *
+ * Note that the allocated extent may have been merged with contiguous extents
+ * during insertion into the inode fork. Thus, got does not reflect the current
+ * state of the inode fork on return. If necessary, the caller can use lastx to
+ * look up the updated record in the inode fork.
+ */
 int
 xfs_bmapi_reserve_delalloc(
        struct xfs_inode        *ip,
@@ -4233,13 +4149,8 @@ xfs_bmapi_reserve_delalloc(
        got->br_startblock = nullstartblock(indlen);
        got->br_blockcount = alen;
        got->br_state = XFS_EXT_NORM;
-       xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
 
-       /*
-        * Update our extent pointer, given that xfs_bmap_add_extent_hole_delay
-        * might have merged it into one of the neighbouring ones.
-        */
-       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *lastx), got);
+       xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
 
        /*
         * Tag the inode if blocks were preallocated. Note that COW fork
@@ -4251,10 +4162,6 @@ xfs_bmapi_reserve_delalloc(
        if (whichfork == XFS_COW_FORK && (prealloc || aoff < off || alen > len))
                xfs_inode_set_cowblocks_tag(ip);
 
-       ASSERT(got->br_startoff <= aoff);
-       ASSERT(got->br_startoff + got->br_blockcount >= aoff + alen);
-       ASSERT(isnullstartblock(got->br_startblock));
-       ASSERT(got->br_state == XFS_EXT_NORM);
        return 0;
 
 out_unreserve_blocks:
@@ -4375,7 +4282,9 @@ xfs_bmapi_allocate(
        if (bma->wasdel)
                error = xfs_bmap_add_extent_delay_real(bma, whichfork);
        else
-               error = xfs_bmap_add_extent_hole_real(bma, whichfork);
+               error = xfs_bmap_add_extent_hole_real(bma->tp, bma->ip,
+                               whichfork, &bma->idx, &bma->cur, &bma->got,
+                               bma->firstblock, bma->dfops, &bma->logflags);
 
        bma->logflags |= tmp_logflags;
        if (error)
@@ -4538,9 +4447,7 @@ xfs_bmapi_write(
        ASSERT(len > 0);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-       ASSERT(!(flags & XFS_BMAPI_REMAP) || whichfork == XFS_DATA_FORK);
-       ASSERT(!(flags & XFS_BMAPI_PREALLOC) || !(flags & XFS_BMAPI_REMAP));
-       ASSERT(!(flags & XFS_BMAPI_CONVERT) || !(flags & XFS_BMAPI_REMAP));
+       ASSERT(!(flags & XFS_BMAPI_REMAP));
 
        /* zeroing is for currently only for data extents, not metadata */
        ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
@@ -4557,7 +4464,7 @@ xfs_bmapi_write(
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
             XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
-            mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
+            mp, XFS_ERRTAG_BMAPIFORMAT))) {
                XFS_ERROR_REPORT("xfs_bmapi_write", XFS_ERRLEVEL_LOW, mp);
                return -EFSCORRUPTED;
        }
@@ -4624,13 +4531,8 @@ xfs_bmapi_write(
                        } else {
                                need_alloc = true;
                        }
-               } else {
-                       /*
-                        * Make sure we only reflink into a hole.
-                        */
-                       ASSERT(!(flags & XFS_BMAPI_REMAP));
-                       if (isnullstartblock(bma.got.br_startblock))
-                               wasdelay = true;
+               } else if (isnullstartblock(bma.got.br_startblock)) {
+                       wasdelay = true;
                }
 
                /*
@@ -4745,13 +4647,9 @@ error0:
        if (bma.cur) {
                if (!error) {
                        ASSERT(*firstblock == NULLFSBLOCK ||
-                              XFS_FSB_TO_AGNO(mp, *firstblock) ==
+                              XFS_FSB_TO_AGNO(mp, *firstblock) <=
                               XFS_FSB_TO_AGNO(mp,
-                                      bma.cur->bc_private.b.firstblock) ||
-                              (dfops->dop_low &&
-                               XFS_FSB_TO_AGNO(mp, *firstblock) <
-                               XFS_FSB_TO_AGNO(mp,
-                                       bma.cur->bc_private.b.firstblock)));
+                                      bma.cur->bc_private.b.firstblock));
                        *firstblock = bma.cur->bc_private.b.firstblock;
                }
                xfs_btree_del_cursor(bma.cur,
@@ -4763,6 +4661,93 @@ error0:
        return error;
 }
 
+static int
+xfs_bmapi_remap(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           bno,
+       xfs_filblks_t           len,
+       xfs_fsblock_t           startblock,
+       struct xfs_defer_ops    *dfops)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       struct xfs_btree_cur    *cur = NULL;
+       xfs_fsblock_t           firstblock = NULLFSBLOCK;
+       struct xfs_bmbt_irec    got;
+       xfs_extnum_t            idx;
+       int                     logflags = 0, error;
+
+       ASSERT(len > 0);
+       ASSERT(len <= (xfs_filblks_t)MAXEXTLEN);
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+
+       if (unlikely(XFS_TEST_ERROR(
+           (XFS_IFORK_FORMAT(ip, XFS_DATA_FORK) != XFS_DINODE_FMT_EXTENTS &&
+            XFS_IFORK_FORMAT(ip, XFS_DATA_FORK) != XFS_DINODE_FMT_BTREE),
+            mp, XFS_ERRTAG_BMAPIFORMAT))) {
+               XFS_ERROR_REPORT("xfs_bmapi_remap", XFS_ERRLEVEL_LOW, mp);
+               return -EFSCORRUPTED;
+       }
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+       if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+               error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+               if (error)
+                       return error;
+       }
+
+       if (xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got)) {
+               /* make sure we only reflink into a hole. */
+               ASSERT(got.br_startoff > bno);
+               ASSERT(got.br_startoff - bno >= len);
+       }
+
+       ip->i_d.di_nblocks += len;
+       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+       if (ifp->if_flags & XFS_IFBROOT) {
+               cur = xfs_bmbt_init_cursor(mp, tp, ip, XFS_DATA_FORK);
+               cur->bc_private.b.firstblock = firstblock;
+               cur->bc_private.b.dfops = dfops;
+               cur->bc_private.b.flags = 0;
+       }
+
+       got.br_startoff = bno;
+       got.br_startblock = startblock;
+       got.br_blockcount = len;
+       got.br_state = XFS_EXT_NORM;
+
+       error = xfs_bmap_add_extent_hole_real(tp, ip, XFS_DATA_FORK, &idx, &cur,
+                       &got, &firstblock, dfops, &logflags);
+       if (error)
+               goto error0;
+
+       if (xfs_bmap_wants_extents(ip, XFS_DATA_FORK)) {
+               int             tmp_logflags = 0;
+
+               error = xfs_bmap_btree_to_extents(tp, ip, cur,
+                       &tmp_logflags, XFS_DATA_FORK);
+               logflags |= tmp_logflags;
+       }
+
+error0:
+       if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS)
+               logflags &= ~XFS_ILOG_DEXT;
+       else if (ip->i_d.di_format != XFS_DINODE_FMT_BTREE)
+               logflags &= ~XFS_ILOG_DBROOT;
+
+       if (logflags)
+               xfs_trans_log_inode(tp, ip, logflags);
+       if (cur) {
+               xfs_btree_del_cursor(cur,
+                               error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       }
+       return error;
+}
+
 /*
  * When a delalloc extent is split (e.g., due to a hole punch), the original
  * indlen reservation must be shared across the two new extents that are left
@@ -4880,7 +4865,7 @@ xfs_bmap_del_extent_delay(
        ASSERT(got_endoff >= del_endoff);
 
        if (isrt) {
-               int64_t rtexts = XFS_FSB_TO_B(mp, del->br_blockcount);
+               uint64_t rtexts = XFS_FSB_TO_B(mp, del->br_blockcount);
 
                do_div(rtexts, mp->m_sb.sb_rextsize);
                xfs_mod_frextents(mp, rtexts);
@@ -5440,6 +5425,7 @@ __xfs_bunmapi(
        int                     whichfork;      /* data or attribute fork */
        xfs_fsblock_t           sum;
        xfs_filblks_t           len = *rlen;    /* length to unmap in file */
+       xfs_fileoff_t           max_len;
 
        trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
 
@@ -5461,6 +5447,16 @@ __xfs_bunmapi(
        ASSERT(len > 0);
        ASSERT(nexts >= 0);
 
+       /*
+        * Guesstimate how many blocks we can unmap without running the risk of
+        * blowing out the transaction with a mix of EFIs and reflink
+        * adjustments.
+        */
+       if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
+               max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));
+       else
+               max_len = len;
+
        if (!(ifp->if_flags & XFS_IFEXTENTS) &&
            (error = xfs_iread_extents(tp, ip, whichfork)))
                return error;
@@ -5505,7 +5501,7 @@ __xfs_bunmapi(
 
        extno = 0;
        while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 &&
-              (nexts == 0 || extno < nexts)) {
+              (nexts == 0 || extno < nexts) && max_len > 0) {
                /*
                 * Is the found extent after a hole in which bno lives?
                 * Just back up to the previous extent, if so.
@@ -5537,6 +5533,15 @@ __xfs_bunmapi(
                }
                if (del.br_startoff + del.br_blockcount > bno + 1)
                        del.br_blockcount = bno + 1 - del.br_startoff;
+
+               /* How much can we safely unmap? */
+               if (max_len < del.br_blockcount) {
+                       del.br_startoff += del.br_blockcount - max_len;
+                       if (!wasdel)
+                               del.br_startblock += del.br_blockcount - max_len;
+                       del.br_blockcount = max_len;
+               }
+
                sum = del.br_startblock + del.br_blockcount;
                if (isrt &&
                    (mod = do_mod(sum, mp->m_sb.sb_rextsize))) {
@@ -5713,6 +5718,7 @@ __xfs_bunmapi(
                if (!isrt && wasdel)
                        xfs_mod_fdblocks(mp, (int64_t)del.br_blockcount, false);
 
+               max_len -= del.br_blockcount;
                bno = del.br_startoff - 1;
 nodelete:
                /*
@@ -6083,7 +6089,7 @@ xfs_bmap_shift_extents(
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
             XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
-            mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
+            mp, XFS_ERRTAG_BMAPIFORMAT))) {
                XFS_ERROR_REPORT("xfs_bmap_shift_extents",
                                 XFS_ERRLEVEL_LOW, mp);
                return -EFSCORRUPTED;
@@ -6235,7 +6241,7 @@ xfs_bmap_split_extent_at(
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
             XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
-            mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
+            mp, XFS_ERRTAG_BMAPIFORMAT))) {
                XFS_ERROR_REPORT("xfs_bmap_split_extent_at",
                                 XFS_ERRLEVEL_LOW, mp);
                return -EFSCORRUPTED;
@@ -6478,49 +6484,33 @@ xfs_bmap_finish_one(
        int                             whichfork,
        xfs_fileoff_t                   startoff,
        xfs_fsblock_t                   startblock,
-       xfs_filblks_t                   blockcount,
+       xfs_filblks_t                   *blockcount,
        xfs_exntst_t                    state)
 {
-       struct xfs_bmbt_irec            bmap;
-       int                             nimaps = 1;
        xfs_fsblock_t                   firstfsb;
-       int                             flags = XFS_BMAPI_REMAP;
-       int                             done;
        int                             error = 0;
 
-       bmap.br_startblock = startblock;
-       bmap.br_startoff = startoff;
-       bmap.br_blockcount = blockcount;
-       bmap.br_state = state;
-
        trace_xfs_bmap_deferred(tp->t_mountp,
                        XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
                        XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
-                       ip->i_ino, whichfork, startoff, blockcount, state);
+                       ip->i_ino, whichfork, startoff, *blockcount, state);
 
-       if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
+       if (WARN_ON_ONCE(whichfork != XFS_DATA_FORK))
                return -EFSCORRUPTED;
-       if (whichfork == XFS_ATTR_FORK)
-               flags |= XFS_BMAPI_ATTRFORK;
 
        if (XFS_TEST_ERROR(false, tp->t_mountp,
-                       XFS_ERRTAG_BMAP_FINISH_ONE,
-                       XFS_RANDOM_BMAP_FINISH_ONE))
+                       XFS_ERRTAG_BMAP_FINISH_ONE))
                return -EIO;
 
        switch (type) {
        case XFS_BMAP_MAP:
-               firstfsb = bmap.br_startblock;
-               error = xfs_bmapi_write(tp, ip, bmap.br_startoff,
-                                       bmap.br_blockcount, flags, &firstfsb,
-                                       bmap.br_blockcount, &bmap, &nimaps,
-                                       dfops);
+               error = xfs_bmapi_remap(tp, ip, startoff, *blockcount,
+                               startblock, dfops);
+               *blockcount = 0;
                break;
        case XFS_BMAP_UNMAP:
-               error = xfs_bunmapi(tp, ip, bmap.br_startoff,
-                               bmap.br_blockcount, flags, 1, &firstfsb,
-                               dfops, &done);
-               ASSERT(done);
+               error = __xfs_bunmapi(tp, ip, startoff, blockcount,
+                               XFS_BMAPI_REMAP, 1, &firstfsb, dfops);
                break;
        default:
                ASSERT(0);