]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_bmap.c
xfs: don't rely on extent indices in xfs_bmap_collapse_extents
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_bmap.c
index 907e4db876e1ace30c8fc154b2aa82fc5d2211d0..52bf4b4c30a5d522e8fb401692fcb93c41b5efae 100644 (file)
@@ -41,7 +41,6 @@
 #include "xfs_rmap.h"
 #include "xfs_ag_resv.h"
 #include "xfs_refcount.h"
-#include "xfs_rmap_btree.h"
 
 
 kmem_zone_t            *xfs_bmap_free_item_zone;
@@ -104,28 +103,21 @@ xfs_bmap_compute_maxlevels(
 STATIC int                             /* error */
 xfs_bmbt_lookup_eq(
        struct xfs_btree_cur    *cur,
-       xfs_fileoff_t           off,
-       xfs_fsblock_t           bno,
-       xfs_filblks_t           len,
+       struct xfs_bmbt_irec    *irec,
        int                     *stat)  /* success/failure */
 {
-       cur->bc_rec.b.br_startoff = off;
-       cur->bc_rec.b.br_startblock = bno;
-       cur->bc_rec.b.br_blockcount = len;
+       cur->bc_rec.b = *irec;
        return xfs_btree_lookup(cur, XFS_LOOKUP_EQ, stat);
 }
 
 STATIC int                             /* error */
-xfs_bmbt_lookup_ge(
+xfs_bmbt_lookup_first(
        struct xfs_btree_cur    *cur,
-       xfs_fileoff_t           off,
-       xfs_fsblock_t           bno,
-       xfs_filblks_t           len,
        int                     *stat)  /* success/failure */
 {
-       cur->bc_rec.b.br_startoff = off;
-       cur->bc_rec.b.br_startblock = bno;
-       cur->bc_rec.b.br_blockcount = len;
+       cur->bc_rec.b.br_startoff = 0;
+       cur->bc_rec.b.br_startblock = 0;
+       cur->bc_rec.b.br_blockcount = 0;
        return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
 }
 
@@ -152,21 +144,17 @@ static inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork)
 }
 
 /*
- * Update the record referred to by cur to the value given
- * by [off, bno, len, state].
+ * Update the record referred to by cur to the value given by irec
  * This either works (return 0) or gets an EFSCORRUPTED error.
  */
 STATIC int
 xfs_bmbt_update(
        struct xfs_btree_cur    *cur,
-       xfs_fileoff_t           off,
-       xfs_fsblock_t           bno,
-       xfs_filblks_t           len,
-       xfs_exntst_t            state)
+       struct xfs_bmbt_irec    *irec)
 {
        union xfs_btree_rec     rec;
 
-       xfs_bmbt_disk_set_allf(&rec.bmbt, off, bno, len, state);
+       xfs_bmbt_disk_set_all(&rec.bmbt, irec);
        return xfs_btree_update(cur, &rec);
 }
 
@@ -183,12 +171,8 @@ xfs_bmap_worst_indlen(
        int             maxrecs;        /* maximum record count at this level */
        xfs_mount_t     *mp;            /* mount structure */
        xfs_filblks_t   rval;           /* return value */
-       xfs_filblks_t   orig_len;
 
        mp = ip->i_mount;
-
-       /* Calculate the worst-case size of the bmbt. */
-       orig_len = len;
        maxrecs = mp->m_bmap_dmxr[0];
        for (level = 0, rval = 0;
             level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK);
@@ -196,20 +180,12 @@ xfs_bmap_worst_indlen(
                len += maxrecs - 1;
                do_div(len, maxrecs);
                rval += len;
-               if (len == 1) {
-                       rval += XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) -
+               if (len == 1)
+                       return rval + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) -
                                level - 1;
-                       break;
-               }
                if (level == 0)
                        maxrecs = mp->m_bmap_dmxr[1];
        }
-
-       /* Calculate the worst-case size of the rmapbt. */
-       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
-               rval += 1 + xfs_rmapbt_calc_size(mp, orig_len) +
-                               mp->m_rmap_maxlevels;
-
        return rval;
 }
 
@@ -246,7 +222,6 @@ xfs_bmap_forkoff_reset(
 {
        if (whichfork == XFS_ATTR_FORK &&
            ip->i_d.di_format != XFS_DINODE_FMT_DEV &&
-           ip->i_d.di_format != XFS_DINODE_FMT_UUID &&
            ip->i_d.di_format != XFS_DINODE_FMT_BTREE) {
                uint    dfl_forkoff = xfs_default_attroffset(ip) >> 3;
 
@@ -502,31 +477,6 @@ error_norelse:
        return;
 }
 
-/*
- * Add bmap trace insert entries for all the contents of the extent records.
- */
-void
-xfs_bmap_trace_exlist(
-       xfs_inode_t     *ip,            /* incore inode pointer */
-       xfs_extnum_t    cnt,            /* count of entries in the list */
-       int             whichfork,      /* data or attr or cow fork */
-       unsigned long   caller_ip)
-{
-       xfs_extnum_t    idx;            /* extent record index */
-       xfs_ifork_t     *ifp;           /* inode fork pointer */
-       int             state = 0;
-
-       if (whichfork == XFS_ATTR_FORK)
-               state |= BMAP_ATTRFORK;
-       else if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
-
-       ifp = XFS_IFORK_PTR(ip, whichfork);
-       ASSERT(cnt == xfs_iext_count(ifp));
-       for (idx = 0; idx < cnt; idx++)
-               trace_xfs_extlist(ip, idx, state, caller_ip);
-}
-
 /*
  * Validate that the bmbt_irecs being returned from bmapi are valid
  * given the caller's original parameters.  Specifically check the
@@ -570,7 +520,7 @@ xfs_bmap_validate_ret(
 
 #else
 #define xfs_bmap_check_leaf_extents(cur, ip, whichfork)                do { } while (0)
-#define        xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)
+#define        xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)    do { } while (0)
 #endif /* DEBUG */
 
 /*
@@ -661,8 +611,8 @@ xfs_bmap_btree_to_extents(
        cbno = be64_to_cpu(*pp);
        *logflagsp = 0;
 #ifdef DEBUG
-       if ((error = xfs_btree_check_lptr(cur, cbno, 1)))
-               return error;
+       XFS_WANT_CORRUPTED_RETURN(cur->bc_mp,
+                       xfs_btree_check_lptr(cur, cbno, 1));
 #endif
        error = xfs_btree_read_bufl(mp, tp, cbno, 0, &cbp, XFS_BMAP_BTREE_REF,
                                &xfs_bmbt_buf_ops);
@@ -755,7 +705,6 @@ xfs_bmap_extents_to_btree(
                args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
        } else if (dfops->dop_low) {
                args.type = XFS_ALLOCTYPE_START_BNO;
-try_another_ag:
                args.fsbno = *firstblock;
        } else {
                args.type = XFS_ALLOCTYPE_NEAR_BNO;
@@ -770,20 +719,6 @@ 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) {
-               args.type = XFS_ALLOCTYPE_FIRST_AG;
-               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);
@@ -886,7 +821,7 @@ xfs_bmap_local_to_extents(
        xfs_ifork_t     *ifp;           /* inode fork pointer */
        xfs_alloc_arg_t args;           /* allocation arguments */
        xfs_buf_t       *bp;            /* buffer for extent block */
-       xfs_bmbt_rec_host_t *ep;        /* extent record pointer */
+       struct xfs_bmbt_irec rec;
 
        /*
         * We don't want to deal with the case of keeping inode data inline yet.
@@ -916,7 +851,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 {
@@ -929,19 +863,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);
@@ -963,12 +884,12 @@ try_another_ag:
        xfs_bmap_local_to_extents_empty(ip, whichfork);
        flags |= XFS_ILOG_CORE;
 
-       xfs_iext_add(ifp, 0, 1);
-       ep = xfs_iext_get_ext(ifp, 0);
-       xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM);
-       trace_xfs_bmap_post_update(ip, 0,
-                       whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0,
-                       _THIS_IP_);
+       rec.br_startoff = 0;
+       rec.br_startblock = args.fsbno;
+       rec.br_blockcount = 1;
+       rec.br_state = XFS_EXT_NORM;
+       xfs_iext_insert(ip, 0, 1, &rec, 0);
+
        XFS_IFORK_NEXT_SET(ip, whichfork, 1);
        ip->i_d.di_nblocks = 1;
        xfs_trans_mod_dquot_byino(tp, ip,
@@ -1003,7 +924,8 @@ xfs_bmap_add_attrfork_btree(
                cur = xfs_bmbt_init_cursor(mp, tp, ip, XFS_DATA_FORK);
                cur->bc_private.b.dfops = dfops;
                cur->bc_private.b.firstblock = *firstblock;
-               if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat)))
+               error = xfs_bmbt_lookup_first(cur, &stat);
+               if (error)
                        goto error0;
                /* must be at least one entry */
                XFS_WANT_CORRUPTED_GOTO(mp, stat == 1, error0);
@@ -1154,9 +1076,6 @@ xfs_bmap_add_attrfork(
        case XFS_DINODE_FMT_DEV:
                ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
                break;
-       case XFS_DINODE_FMT_UUID:
-               ip->i_d.di_forkoff = roundup(sizeof(uuid_t), 8) >> 3;
-               break;
        case XFS_DINODE_FMT_LOCAL:
        case XFS_DINODE_FMT_EXTENTS:
        case XFS_DINODE_FMT_BTREE:
@@ -1216,7 +1135,7 @@ xfs_bmap_add_attrfork(
                        xfs_log_sb(tp);
        }
 
-       error = xfs_defer_finish(&tp, &dfops, NULL);
+       error = xfs_defer_finish(&tp, &dfops);
        if (error)
                goto bmap_cancel;
        error = xfs_trans_commit(tp);
@@ -1251,7 +1170,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 */
@@ -1259,11 +1177,10 @@ xfs_bmap_read_extents(
        __be64                  *pp;    /* pointer to block address */
        /* REFERENCED */
        xfs_extnum_t            room;   /* number of entries there's room for */
+       int                     state = xfs_bmap_fork_to_state(whichfork);
 
        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.
@@ -1303,7 +1220,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)) {
@@ -1326,25 +1242,16 @@ 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;
                        }
+                       trace_xfs_read_extent(ip, i, state, _THIS_IP_);
                }
                xfs_trans_brelse(tp, bp);
                bno = nextbno;
@@ -1362,7 +1269,6 @@ xfs_bmap_read_extents(
        if (i != XFS_IFORK_NEXTENTS(ip, whichfork))
                return -EFSCORRUPTED;
        ASSERT(i == xfs_iext_count(ifp));
-       XFS_BMAP_TRACE_EXLIST(ip, i, whichfork);
        return 0;
 error0:
        xfs_trans_brelse(tp, bp);
@@ -1390,7 +1296,6 @@ xfs_bmap_first_unused(
        xfs_fileoff_t   lastaddr;               /* last block number seen */
        xfs_fileoff_t   lowest;                 /* lowest useful block */
        xfs_fileoff_t   max;                    /* starting useful block */
-       xfs_fileoff_t   off;                    /* offset for this block */
        xfs_extnum_t    nextents;               /* number of extent entries */
 
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE ||
@@ -1407,16 +1312,19 @@ xfs_bmap_first_unused(
        lowest = *first_unused;
        nextents = xfs_iext_count(ifp);
        for (idx = 0, lastaddr = 0, max = lowest; idx < nextents; idx++) {
-               xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, idx);
-               off = xfs_bmbt_get_startoff(ep);
+               struct xfs_bmbt_irec got;
+
+               xfs_iext_get_extent(ifp, idx, &got);
+
                /*
                 * See if the hole before this extent will work.
                 */
-               if (off >= lowest + len && off - max >= len) {
+               if (got.br_startoff >= lowest + len &&
+                   got.br_startoff - max >= len) {
                        *first_unused = max;
                        return 0;
                }
-               lastaddr = off + xfs_bmbt_get_blockcount(ep);
+               lastaddr = got.br_startoff + got.br_blockcount;
                max = XFS_FILEOFF_MAX(lastaddr, lowest);
        }
        *first_unused = max;
@@ -1496,7 +1404,7 @@ xfs_bmap_last_extent(
                return 0;
        }
 
-       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, nextents - 1), rec);
+       xfs_iext_get_extent(ifp, nextents - 1, rec);
        *is_empty = 0;
        return 0;
 }
@@ -1519,14 +1427,14 @@ xfs_bmap_isaeof(
        int                     is_empty;
        int                     error;
 
-       bma->aeof = 0;
+       bma->aeof = false;
        error = xfs_bmap_last_extent(NULL, bma->ip, whichfork, &rec,
                                     &is_empty);
        if (error)
                return error;
 
        if (is_empty) {
-               bma->aeof = 1;
+               bma->aeof = true;
                return 0;
        }
 
@@ -1582,7 +1490,6 @@ xfs_bmap_one_block(
        xfs_inode_t     *ip,            /* incore inode */
        int             whichfork)      /* data or attr fork */
 {
-       xfs_bmbt_rec_host_t *ep;        /* ptr to fork's extent */
        xfs_ifork_t     *ifp;           /* inode fork pointer */
        int             rval;           /* return value */
        xfs_bmbt_irec_t s;              /* internal version of extent */
@@ -1597,8 +1504,7 @@ xfs_bmap_one_block(
                return 0;
        ifp = XFS_IFORK_PTR(ip, whichfork);
        ASSERT(ifp->if_flags & XFS_IFEXTENTS);
-       ep = xfs_iext_get_ext(ifp, 0);
-       xfs_bmbt_get_all(ep, &s);
+       xfs_iext_get_extent(ifp, 0, &s);
        rval = s.br_startoff == 0 && s.br_blockcount == 1;
        if (rval && whichfork == XFS_DATA_FORK)
                ASSERT(XFS_ISIZE(ip) == ip->i_mount->m_sb.sb_blocksize);
@@ -1618,8 +1524,6 @@ xfs_bmap_add_extent_delay_real(
        int                     whichfork)
 {
        struct xfs_bmbt_irec    *new = &bma->got;
-       int                     diff;   /* temp value */
-       xfs_bmbt_rec_host_t     *ep;    /* extent entry for idx */
        int                     error;  /* error return value */
        int                     i;      /* temp state */
        xfs_ifork_t             *ifp;   /* inode fork pointer */
@@ -1627,14 +1531,14 @@ xfs_bmap_add_extent_delay_real(
        xfs_bmbt_irec_t         r[3];   /* neighbor extent entries */
                                        /* left is 0, right is 1, prev is 2 */
        int                     rval=0; /* return value (logging flags) */
-       int                     state = 0;/* state bits, accessed thru macros */
+       int                     state = xfs_bmap_fork_to_state(whichfork);
        xfs_filblks_t           da_new; /* new count del alloc blocks used */
        xfs_filblks_t           da_old; /* old count del alloc blocks used */
        xfs_filblks_t           temp=0; /* value for da_new calculations */
-       xfs_filblks_t           temp2=0;/* value for da_new calculations */
        int                     tmp_rval;       /* partial logging flags */
        struct xfs_mount        *mp;
        xfs_extnum_t            *nextents;
+       struct xfs_bmbt_irec    old;
 
        mp = bma->ip->i_mount;
        ifp = XFS_IFORK_PTR(bma->ip, whichfork);
@@ -1654,15 +1558,12 @@ xfs_bmap_add_extent_delay_real(
 #define        RIGHT           r[1]
 #define        PREV            r[2]
 
-       if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
-
        /*
         * Set up a bunch of variables to make the tests simpler.
         */
-       ep = xfs_iext_get_ext(ifp, bma->idx);
-       xfs_bmbt_get_all(ep, &PREV);
+       xfs_iext_get_extent(ifp, bma->idx, &PREV);
        new_endoff = new->br_startoff + new->br_blockcount;
+       ASSERT(isnullstartblock(PREV.br_startblock));
        ASSERT(PREV.br_startoff <= new->br_startoff);
        ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
 
@@ -1684,7 +1585,7 @@ xfs_bmap_add_extent_delay_real(
         */
        if (bma->idx > 0) {
                state |= BMAP_LEFT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx - 1), &LEFT);
+               xfs_iext_get_extent(ifp, bma->idx - 1, &LEFT);
 
                if (isnullstartblock(LEFT.br_startblock))
                        state |= BMAP_LEFT_DELAY;
@@ -1704,7 +1605,7 @@ xfs_bmap_add_extent_delay_real(
         */
        if (bma->idx < xfs_iext_count(ifp) - 1) {
                state |= BMAP_RIGHT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx + 1), &RIGHT);
+               xfs_iext_get_extent(ifp, bma->idx + 1, &RIGHT);
 
                if (isnullstartblock(RIGHT.br_startblock))
                        state |= BMAP_RIGHT_DELAY;
@@ -1736,11 +1637,8 @@ xfs_bmap_add_extent_delay_real(
                 * The left and right neighbors are both contiguous with new.
                 */
                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),
-                       LEFT.br_blockcount + PREV.br_blockcount +
-                       RIGHT.br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
 
                xfs_iext_remove(bma->ip, bma->idx + 1, 2, state);
                (*nextents)--;
@@ -1748,9 +1646,7 @@ xfs_bmap_add_extent_delay_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur, RIGHT.br_startoff,
-                                       RIGHT.br_startblock,
-                                       RIGHT.br_blockcount, &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
@@ -1762,11 +1658,7 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, LEFT.br_startoff,
-                                       LEFT.br_startblock,
-                                       LEFT.br_blockcount +
-                                       PREV.br_blockcount +
-                                       RIGHT.br_blockcount, LEFT.br_state);
+                       error = xfs_bmbt_update(bma->cur, &LEFT);
                        if (error)
                                goto done;
                }
@@ -1779,26 +1671,20 @@ xfs_bmap_add_extent_delay_real(
                 */
                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),
-                       LEFT.br_blockcount + PREV.br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               old = LEFT;
+               LEFT.br_blockcount += PREV.br_blockcount;
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
 
                xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
                if (bma->cur == NULL)
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur, LEFT.br_startoff,
-                                       LEFT.br_startblock, LEFT.br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, &old, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, LEFT.br_startoff,
-                                       LEFT.br_startblock,
-                                       LEFT.br_blockcount +
-                                       PREV.br_blockcount, LEFT.br_state);
+                       error = xfs_bmbt_update(bma->cur, &LEFT);
                        if (error)
                                goto done;
                }
@@ -1809,27 +1695,20 @@ xfs_bmap_add_extent_delay_real(
                 * Filling in all of a previously delayed allocation extent.
                 * The right neighbor is contiguous, the left is not.
                 */
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_startblock(ep, new->br_startblock);
-               xfs_bmbt_set_blockcount(ep,
-                       PREV.br_blockcount + RIGHT.br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               PREV.br_startblock = new->br_startblock;
+               PREV.br_blockcount += RIGHT.br_blockcount;
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 
                xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
                if (bma->cur == NULL)
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur, RIGHT.br_startoff,
-                                       RIGHT.br_startblock,
-                                       RIGHT.br_blockcount, &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, &RIGHT, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, PREV.br_startoff,
-                                       new->br_startblock,
-                                       PREV.br_blockcount +
-                                       RIGHT.br_blockcount, PREV.br_state);
+                       error = xfs_bmbt_update(bma->cur, &PREV);
                        if (error)
                                goto done;
                }
@@ -1841,23 +1720,19 @@ xfs_bmap_add_extent_delay_real(
                 * Neither the left nor right neighbors are contiguous with
                 * the new one.
                 */
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_startblock(ep, new->br_startblock);
-               xfs_bmbt_set_state(ep, new->br_state);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               PREV.br_startblock = new->br_startblock;
+               PREV.br_state = new->br_state;
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 
                (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
-                       bma->cur->bc_rec.b.br_state = XFS_EXT_NORM;
                        error = xfs_btree_insert(bma->cur, &i);
                        if (error)
                                goto done;
@@ -1870,38 +1745,31 @@ xfs_bmap_add_extent_delay_real(
                 * Filling in the first part of a previous delayed allocation.
                 * The left neighbor is contiguous.
                 */
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx - 1, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, bma->idx - 1),
-                       LEFT.br_blockcount + new->br_blockcount);
-               xfs_bmbt_set_startoff(ep,
-                       PREV.br_startoff + new->br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx - 1, state, _THIS_IP_);
-
+               old = LEFT;
                temp = PREV.br_blockcount - new->br_blockcount;
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep, temp);
+               da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
+                               startblockval(PREV.br_startblock));
+
+               LEFT.br_blockcount += new->br_blockcount;
+               xfs_iext_update_extent(bma->ip, state, bma->idx - 1, &LEFT);
+
+               PREV.br_blockcount = temp = PREV.br_blockcount - new->br_blockcount;
+               PREV.br_startoff += new->br_blockcount;
+               PREV.br_startblock = nullstartblock(da_new);
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+
                if (bma->cur == NULL)
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur, LEFT.br_startoff,
-                                       LEFT.br_startblock, LEFT.br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, &old, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, LEFT.br_startoff,
-                                       LEFT.br_startblock,
-                                       LEFT.br_blockcount +
-                                       new->br_blockcount,
-                                       LEFT.br_state);
+                       error = xfs_bmbt_update(bma->cur, &LEFT);
                        if (error)
                                goto done;
                }
-               da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
-                       startblockval(PREV.br_startblock));
-               xfs_bmbt_set_startblock(ep, nullstartblock(da_new));
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
 
                bma->idx--;
                break;
@@ -1911,23 +1779,16 @@ xfs_bmap_add_extent_delay_real(
                 * Filling in the first part of a previous delayed allocation.
                 * The left neighbor is not contiguous.
                 */
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_startoff(ep, new_endoff);
-               temp = PREV.br_blockcount - new->br_blockcount;
-               xfs_bmbt_set_blockcount(ep, temp);
                xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
                (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
-                       bma->cur->bc_rec.b.br_state = XFS_EXT_NORM;
                        error = xfs_btree_insert(bma->cur, &i);
                        if (error)
                                goto done;
@@ -1942,12 +1803,16 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+
+               temp = PREV.br_blockcount - new->br_blockcount;
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
                        startblockval(PREV.br_startblock) -
                        (bma->cur ? bma->cur->bc_private.b.allocated : 0));
-               ep = xfs_iext_get_ext(ifp, bma->idx + 1);
-               xfs_bmbt_set_startblock(ep, nullstartblock(da_new));
-               trace_xfs_bmap_post_update(bma->ip, bma->idx + 1, state, _THIS_IP_);
+
+               PREV.br_startoff = new_endoff;
+               PREV.br_blockcount = temp;
+               PREV.br_startblock = nullstartblock(da_new);
+               xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &PREV);
                break;
 
        case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -1955,38 +1820,32 @@ xfs_bmap_add_extent_delay_real(
                 * Filling in the last part of a previous delayed allocation.
                 * The right neighbor is contiguous with the new allocation.
                 */
-               temp = PREV.br_blockcount - new->br_blockcount;
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx + 1, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep, temp);
-               xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, bma->idx + 1),
-                       new->br_startoff, new->br_startblock,
-                       new->br_blockcount + RIGHT.br_blockcount,
-                       RIGHT.br_state);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx + 1, state, _THIS_IP_);
+               old = RIGHT;
+               RIGHT.br_startoff = new->br_startoff;
+               RIGHT.br_startblock = new->br_startblock;
+               RIGHT.br_blockcount += new->br_blockcount;
+               xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &RIGHT);
+
                if (bma->cur == NULL)
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur, RIGHT.br_startoff,
-                                       RIGHT.br_startblock,
-                                       RIGHT.br_blockcount, &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, &old, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, new->br_startoff,
-                                       new->br_startblock,
-                                       new->br_blockcount +
-                                       RIGHT.br_blockcount,
-                                       RIGHT.br_state);
+                       error = xfs_bmbt_update(bma->cur, &RIGHT);
                        if (error)
                                goto done;
                }
 
+               temp = PREV.br_blockcount - new->br_blockcount;
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
                        startblockval(PREV.br_startblock));
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_startblock(ep, nullstartblock(da_new));
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+
+               PREV.br_blockcount = temp;
+               PREV.br_startblock = nullstartblock(da_new);
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 
                bma->idx++;
                break;
@@ -1996,22 +1855,16 @@ xfs_bmap_add_extent_delay_real(
                 * Filling in the last part of a previous delayed allocation.
                 * The right neighbor is not contiguous.
                 */
-               temp = PREV.br_blockcount - new->br_blockcount;
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep, temp);
                xfs_iext_insert(bma->ip, bma->idx + 1, 1, new, state);
                (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
-                       bma->cur->bc_rec.b.br_state = XFS_EXT_NORM;
                        error = xfs_btree_insert(bma->cur, &i);
                        if (error)
                                goto done;
@@ -2026,12 +1879,15 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
+
+               temp = PREV.br_blockcount - new->br_blockcount;
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(bma->ip, temp),
                        startblockval(PREV.br_startblock) -
                        (bma->cur ? bma->cur->bc_private.b.allocated : 0));
-               ep = xfs_iext_get_ext(ifp, bma->idx);
-               xfs_bmbt_set_startblock(ep, nullstartblock(da_new));
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+
+               PREV.br_startblock = nullstartblock(da_new);
+               PREV.br_blockcount = temp;
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 
                bma->idx++;
                break;
@@ -2057,30 +1913,39 @@ xfs_bmap_add_extent_delay_real(
                 *  PREV @ idx          LEFT              RIGHT
                 *                      inserted at idx + 1
                 */
-               temp = new->br_startoff - PREV.br_startoff;
-               temp2 = PREV.br_startoff + PREV.br_blockcount - new_endoff;
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx, 0, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep, temp);      /* truncate PREV */
+               old = PREV;
+
+               /* LEFT is the new middle */
                LEFT = *new;
+
+               /* RIGHT is the new right */
                RIGHT.br_state = PREV.br_state;
-               RIGHT.br_startblock = nullstartblock(
-                               (int)xfs_bmap_worst_indlen(bma->ip, temp2));
                RIGHT.br_startoff = new_endoff;
-               RIGHT.br_blockcount = temp2;
+               RIGHT.br_blockcount =
+                       PREV.br_startoff + PREV.br_blockcount - new_endoff;
+               RIGHT.br_startblock =
+                       nullstartblock(xfs_bmap_worst_indlen(bma->ip,
+                                       RIGHT.br_blockcount));
+
+               /* truncate PREV */
+               PREV.br_blockcount = new->br_startoff - PREV.br_startoff;
+               PREV.br_startblock =
+                       nullstartblock(xfs_bmap_worst_indlen(bma->ip,
+                                       PREV.br_blockcount));
+               xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+
                /* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
                xfs_iext_insert(bma->ip, bma->idx + 1, 2, &LEFT, state);
                (*nextents)++;
+
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       error = xfs_bmbt_lookup_eq(bma->cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(bma->cur, new, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
-                       bma->cur->bc_rec.b.br_state = XFS_EXT_NORM;
                        error = xfs_btree_insert(bma->cur, &i);
                        if (error)
                                goto done;
@@ -2095,28 +1960,10 @@ xfs_bmap_add_extent_delay_real(
                        if (error)
                                goto done;
                }
-               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));
-               if (diff > 0) {
-                       error = xfs_mod_fdblocks(bma->ip->i_mount,
-                                                -((int64_t)diff), false);
-                       ASSERT(!error);
-                       if (error)
-                               goto done;
-               }
-
-               ep = xfs_iext_get_ext(ifp, bma->idx);
-               xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
-               trace_xfs_bmap_pre_update(bma->ip, bma->idx + 2, state, _THIS_IP_);
-               xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, bma->idx + 2),
-                       nullstartblock((int)temp2));
-               trace_xfs_bmap_post_update(bma->ip, bma->idx + 2, state, _THIS_IP_);
 
+               da_new = startblockval(PREV.br_startblock) +
+                        startblockval(RIGHT.br_startblock);
                bma->idx++;
-               da_new = temp + temp2;
                break;
 
        case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
@@ -2150,20 +1997,17 @@ xfs_bmap_add_extent_delay_real(
                        goto done;
        }
 
-       /* adjust for changes in reserved delayed indirect blocks */
-       if (da_old || da_new) {
-               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);
+       if (bma->cur) {
+               da_new += bma->cur->bc_private.b.allocated;
+               bma->cur->bc_private.b.allocated = 0;
        }
 
-       /* clear out the allocated field, done with it now in any case. */
-       if (bma->cur)
-               bma->cur->bc_private.b.allocated = 0;
+       /* adjust for changes in reserved delayed indirect blocks */
+       if (da_new != da_old) {
+               ASSERT(state == 0 || da_new < da_old);
+               error = xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new),
+                               false);
+       }
 
        xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
 done:
@@ -2191,25 +2035,21 @@ xfs_bmap_add_extent_unwritten_real(
        int                     *logflagsp) /* inode logging flags */
 {
        xfs_btree_cur_t         *cur;   /* btree cursor */
-       xfs_bmbt_rec_host_t     *ep;    /* extent entry for idx */
        int                     error;  /* error return value */
        int                     i;      /* temp state */
        xfs_ifork_t             *ifp;   /* inode fork pointer */
        xfs_fileoff_t           new_endoff;     /* end offset of new entry */
-       xfs_exntst_t            newext; /* new extent state */
-       xfs_exntst_t            oldext; /* old extent state */
        xfs_bmbt_irec_t         r[3];   /* neighbor extent entries */
                                        /* left is 0, right is 1, prev is 2 */
        int                     rval=0; /* return value (logging flags) */
-       int                     state = 0;/* state bits, accessed thru macros */
+       int                     state = xfs_bmap_fork_to_state(whichfork);
        struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_bmbt_irec    old;
 
        *logflagsp = 0;
 
        cur = *curp;
        ifp = XFS_IFORK_PTR(ip, whichfork);
-       if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
 
        ASSERT(*idx >= 0);
        ASSERT(*idx <= xfs_iext_count(ifp));
@@ -2225,12 +2065,8 @@ xfs_bmap_add_extent_unwritten_real(
         * Set up a bunch of variables to make the tests simpler.
         */
        error = 0;
-       ep = xfs_iext_get_ext(ifp, *idx);
-       xfs_bmbt_get_all(ep, &PREV);
-       newext = new->br_state;
-       oldext = (newext == XFS_EXT_UNWRITTEN) ?
-               XFS_EXT_NORM : XFS_EXT_UNWRITTEN;
-       ASSERT(PREV.br_state == oldext);
+       xfs_iext_get_extent(ifp, *idx, &PREV);
+       ASSERT(new->br_state != PREV.br_state);
        new_endoff = new->br_startoff + new->br_blockcount;
        ASSERT(PREV.br_startoff <= new->br_startoff);
        ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
@@ -2250,7 +2086,7 @@ xfs_bmap_add_extent_unwritten_real(
         */
        if (*idx > 0) {
                state |= BMAP_LEFT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &LEFT);
+               xfs_iext_get_extent(ifp, *idx - 1, &LEFT);
 
                if (isnullstartblock(LEFT.br_startblock))
                        state |= BMAP_LEFT_DELAY;
@@ -2259,7 +2095,7 @@ xfs_bmap_add_extent_unwritten_real(
        if ((state & BMAP_LEFT_VALID) && !(state & BMAP_LEFT_DELAY) &&
            LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
            LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
-           LEFT.br_state == newext &&
+           LEFT.br_state == new->br_state &&
            LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN)
                state |= BMAP_LEFT_CONTIG;
 
@@ -2270,7 +2106,7 @@ xfs_bmap_add_extent_unwritten_real(
         */
        if (*idx < xfs_iext_count(ifp) - 1) {
                state |= BMAP_RIGHT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx + 1), &RIGHT);
+               xfs_iext_get_extent(ifp, *idx + 1, &RIGHT);
                if (isnullstartblock(RIGHT.br_startblock))
                        state |= BMAP_RIGHT_DELAY;
        }
@@ -2278,7 +2114,7 @@ xfs_bmap_add_extent_unwritten_real(
        if ((state & BMAP_RIGHT_VALID) && !(state & BMAP_RIGHT_DELAY) &&
            new_endoff == RIGHT.br_startoff &&
            new->br_startblock + new->br_blockcount == RIGHT.br_startblock &&
-           newext == RIGHT.br_state &&
+           new->br_state == RIGHT.br_state &&
            new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN &&
            ((state & (BMAP_LEFT_CONTIG | BMAP_LEFT_FILLING |
                       BMAP_RIGHT_FILLING)) !=
@@ -2301,11 +2137,8 @@ xfs_bmap_add_extent_unwritten_real(
                 */
                --*idx;
 
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
-                       LEFT.br_blockcount + PREV.br_blockcount +
-                       RIGHT.br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &LEFT);
 
                xfs_iext_remove(ip, *idx + 1, 2, state);
                XFS_IFORK_NEXT_SET(ip, whichfork,
@@ -2314,9 +2147,8 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
-                                       RIGHT.br_startblock,
-                                       RIGHT.br_blockcount, &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
@@ -2331,10 +2163,8 @@ xfs_bmap_add_extent_unwritten_real(
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
-                               LEFT.br_startblock,
-                               LEFT.br_blockcount + PREV.br_blockcount +
-                               RIGHT.br_blockcount, LEFT.br_state)))
+                       error = xfs_bmbt_update(cur, &LEFT);
+                       if (error)
                                goto done;
                }
                break;
@@ -2346,10 +2176,8 @@ xfs_bmap_add_extent_unwritten_real(
                 */
                --*idx;
 
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx),
-                       LEFT.br_blockcount + PREV.br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               LEFT.br_blockcount += PREV.br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &LEFT);
 
                xfs_iext_remove(ip, *idx + 1, 1, state);
                XFS_IFORK_NEXT_SET(ip, whichfork,
@@ -2358,9 +2186,8 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
-                                       PREV.br_startblock, PREV.br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &PREV, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
@@ -2369,10 +2196,8 @@ xfs_bmap_add_extent_unwritten_real(
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
-                               LEFT.br_startblock,
-                               LEFT.br_blockcount + PREV.br_blockcount,
-                               LEFT.br_state)))
+                       error = xfs_bmbt_update(cur, &LEFT);
+                       if (error)
                                goto done;
                }
                break;
@@ -2382,11 +2207,10 @@ xfs_bmap_add_extent_unwritten_real(
                 * Setting all of a previous oldext extent to newext.
                 * The right neighbor is contiguous, the left is not.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep,
-                       PREV.br_blockcount + RIGHT.br_blockcount);
-               xfs_bmbt_set_state(ep, newext);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               PREV.br_blockcount += RIGHT.br_blockcount;
+               PREV.br_state = new->br_state;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
+
                xfs_iext_remove(ip, *idx + 1, 1, state);
                XFS_IFORK_NEXT_SET(ip, whichfork,
                                XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
@@ -2394,9 +2218,8 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
-                                       RIGHT.br_startblock,
-                                       RIGHT.br_blockcount, &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &RIGHT, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
@@ -2405,10 +2228,8 @@ xfs_bmap_add_extent_unwritten_real(
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur, new->br_startoff,
-                               new->br_startblock,
-                               new->br_blockcount + RIGHT.br_blockcount,
-                               newext)))
+                       error = xfs_bmbt_update(cur, &PREV);
+                       if (error)
                                goto done;
                }
                break;
@@ -2419,22 +2240,19 @@ xfs_bmap_add_extent_unwritten_real(
                 * Neither the left nor right neighbors are contiguous with
                 * the new one.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_state(ep, newext);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               PREV.br_state = new->br_state;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
 
                if (cur == NULL)
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, new, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur, new->br_startoff,
-                               new->br_startblock, new->br_blockcount,
-                               newext)))
+                       error = xfs_bmbt_update(cur, &PREV);
+                       if (error)
                                goto done;
                }
                break;
@@ -2444,19 +2262,14 @@ xfs_bmap_add_extent_unwritten_real(
                 * Setting the first part of a previous oldext extent to newext.
                 * The left neighbor is contiguous.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx - 1, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx - 1),
-                       LEFT.br_blockcount + new->br_blockcount);
-               xfs_bmbt_set_startoff(ep,
-                       PREV.br_startoff + new->br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx - 1, state, _THIS_IP_);
-
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_startblock(ep,
-                       new->br_startblock + new->br_blockcount);
-               xfs_bmbt_set_blockcount(ep,
-                       PREV.br_blockcount - new->br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               LEFT.br_blockcount += new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx - 1, &LEFT);
+
+               old = PREV;
+               PREV.br_startoff += new->br_blockcount;
+               PREV.br_startblock += new->br_blockcount;
+               PREV.br_blockcount -= new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
 
                --*idx;
 
@@ -2464,23 +2277,17 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
-                                       PREV.br_startblock, PREV.br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur,
-                               PREV.br_startoff + new->br_blockcount,
-                               PREV.br_startblock + new->br_blockcount,
-                               PREV.br_blockcount - new->br_blockcount,
-                               oldext)))
+                       error = xfs_bmbt_update(cur, &PREV);
+                       if (error)
                                goto done;
-                       if ((error = xfs_btree_decrement(cur, 0, &i)))
+                       error = xfs_btree_decrement(cur, 0, &i);
+                       if (error)
                                goto done;
-                       error = xfs_bmbt_update(cur, LEFT.br_startoff,
-                               LEFT.br_startblock,
-                               LEFT.br_blockcount + new->br_blockcount,
-                               LEFT.br_state);
+                       error = xfs_bmbt_update(cur, &LEFT);
                        if (error)
                                goto done;
                }
@@ -2491,14 +2298,11 @@ xfs_bmap_add_extent_unwritten_real(
                 * Setting the first part of a previous oldext extent to newext.
                 * The left neighbor is not contiguous.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               ASSERT(ep && xfs_bmbt_get_state(ep) == oldext);
-               xfs_bmbt_set_startoff(ep, new_endoff);
-               xfs_bmbt_set_blockcount(ep,
-                       PREV.br_blockcount - new->br_blockcount);
-               xfs_bmbt_set_startblock(ep,
-                       new->br_startblock + new->br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               old = PREV;
+               PREV.br_startoff += new->br_blockcount;
+               PREV.br_startblock += new->br_blockcount;
+               PREV.br_blockcount -= new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
 
                xfs_iext_insert(ip, *idx, 1, new, state);
                XFS_IFORK_NEXT_SET(ip, whichfork,
@@ -2507,16 +2311,12 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
-                                       PREV.br_startblock, PREV.br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur,
-                               PREV.br_startoff + new->br_blockcount,
-                               PREV.br_startblock + new->br_blockcount,
-                               PREV.br_blockcount - new->br_blockcount,
-                               oldext)))
+                       error = xfs_bmbt_update(cur, &PREV);
+                       if (error)
                                goto done;
                        cur->bc_rec.b = *new;
                        if ((error = xfs_btree_insert(cur, &i)))
@@ -2530,39 +2330,33 @@ xfs_bmap_add_extent_unwritten_real(
                 * Setting the last part of a previous oldext extent to newext.
                 * The right neighbor is contiguous with the new allocation.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep,
-                       PREV.br_blockcount - new->br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               old = PREV;
+               PREV.br_blockcount -= new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
 
                ++*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, newext);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               RIGHT.br_startoff = new->br_startoff;
+               RIGHT.br_startblock = new->br_startblock;
+               RIGHT.br_blockcount += new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &RIGHT);
 
                if (cur == NULL)
                        rval = XFS_ILOG_DEXT;
                else {
                        rval = 0;
-                       if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
-                                       PREV.br_startblock,
-                                       PREV.br_blockcount, &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
-                               PREV.br_startblock,
-                               PREV.br_blockcount - new->br_blockcount,
-                               oldext)))
+                       error = xfs_bmbt_update(cur, &PREV);
+                       if (error)
                                goto done;
-                       if ((error = xfs_btree_increment(cur, 0, &i)))
+                       error = xfs_btree_increment(cur, 0, &i);
+                       if (error)
                                goto done;
-                       if ((error = xfs_bmbt_update(cur, new->br_startoff,
-                               new->br_startblock,
-                               new->br_blockcount + RIGHT.br_blockcount,
-                               newext)))
+                       error = xfs_bmbt_update(cur, &RIGHT);
+                       if (error)
                                goto done;
                }
                break;
@@ -2572,10 +2366,9 @@ xfs_bmap_add_extent_unwritten_real(
                 * Setting the last part of a previous oldext extent to newext.
                 * The right neighbor is not contiguous.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep,
-                       PREV.br_blockcount - new->br_blockcount);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               old = PREV;
+               PREV.br_blockcount -= new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
 
                ++*idx;
                xfs_iext_insert(ip, *idx, 1, new, state);
@@ -2586,22 +2379,17 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
-                                       PREV.br_startblock, PREV.br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
-                               PREV.br_startblock,
-                               PREV.br_blockcount - new->br_blockcount,
-                               oldext)))
+                       error = xfs_bmbt_update(cur, &PREV);
+                       if (error)
                                goto done;
-                       if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, new, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
-                       cur->bc_rec.b.br_state = XFS_EXT_NORM;
                        if ((error = xfs_btree_insert(cur, &i)))
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
@@ -2614,17 +2402,16 @@ xfs_bmap_add_extent_unwritten_real(
                 * newext.  Contiguity is impossible here.
                 * One extent becomes three extents.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep,
-                       new->br_startoff - PREV.br_startoff);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               old = PREV;
+               PREV.br_blockcount = new->br_startoff - PREV.br_startoff;
+               xfs_iext_update_extent(ip, state, *idx, &PREV);
 
                r[0] = *new;
                r[1].br_startoff = new_endoff;
                r[1].br_blockcount =
-                       PREV.br_startoff + PREV.br_blockcount - new_endoff;
+                       old.br_startoff + old.br_blockcount - new_endoff;
                r[1].br_startblock = new->br_startblock + new->br_blockcount;
-               r[1].br_state = oldext;
+               r[1].br_state = PREV.br_state;
 
                ++*idx;
                xfs_iext_insert(ip, *idx, 2, &r[0], state);
@@ -2635,20 +2422,16 @@ xfs_bmap_add_extent_unwritten_real(
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
                        rval = XFS_ILOG_CORE;
-                       if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
-                                       PREV.br_startblock, PREV.br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        /* new right extent - oldext */
-                       if ((error = xfs_bmbt_update(cur, r[1].br_startoff,
-                               r[1].br_startblock, r[1].br_blockcount,
-                               r[1].br_state)))
+                       error = xfs_bmbt_update(cur, &r[1]);
+                       if (error)
                                goto done;
                        /* new left extent - oldext */
                        cur->bc_rec.b = PREV;
-                       cur->bc_rec.b.br_blockcount =
-                               new->br_startoff - PREV.br_startoff;
                        if ((error = xfs_btree_insert(cur, &i)))
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
@@ -2657,13 +2440,11 @@ xfs_bmap_add_extent_unwritten_real(
                         * we are about to insert as we can't trust it after
                         * the previous insert.
                         */
-                       if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
-                                       new->br_startblock, new->br_blockcount,
-                                       &i)))
+                       error = xfs_bmbt_lookup_eq(cur, new, &i);
+                       if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 0, done);
                        /* new middle extent - newext */
-                       cur->bc_rec.b.br_state = new->br_state;
                        if ((error = xfs_btree_insert(cur, &i)))
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
@@ -2730,13 +2511,10 @@ xfs_bmap_add_extent_hole_delay(
        xfs_filblks_t           newlen=0;       /* new indirect size */
        xfs_filblks_t           oldlen=0;       /* old indirect size */
        xfs_bmbt_irec_t         right;  /* right neighbor extent entry */
-       int                     state;  /* state bits, accessed thru macros */
-       xfs_filblks_t           temp=0; /* temp for indirect calculations */
+       int                     state = xfs_bmap_fork_to_state(whichfork);
+       xfs_filblks_t           temp;    /* temp for indirect calculations */
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
-       state = 0;
-       if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
        ASSERT(isnullstartblock(new->br_startblock));
 
        /*
@@ -2744,7 +2522,7 @@ xfs_bmap_add_extent_hole_delay(
         */
        if (*idx > 0) {
                state |= BMAP_LEFT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx - 1), &left);
+               xfs_iext_get_extent(ifp, *idx - 1, &left);
 
                if (isnullstartblock(left.br_startblock))
                        state |= BMAP_LEFT_DELAY;
@@ -2756,7 +2534,7 @@ xfs_bmap_add_extent_hole_delay(
         */
        if (*idx < xfs_iext_count(ifp)) {
                state |= BMAP_RIGHT_VALID;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
+               xfs_iext_get_extent(ifp, *idx, &right);
 
                if (isnullstartblock(right.br_startblock))
                        state |= BMAP_RIGHT_DELAY;
@@ -2793,16 +2571,14 @@ xfs_bmap_add_extent_hole_delay(
                temp = left.br_blockcount + new->br_blockcount +
                        right.br_blockcount;
 
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
                oldlen = startblockval(left.br_startblock) +
                        startblockval(new->br_startblock) +
                        startblockval(right.br_startblock);
                newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
                                         oldlen);
-               xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
-                       nullstartblock((int)newlen));
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               left.br_startblock = nullstartblock(newlen);
+               left.br_blockcount = temp;
+               xfs_iext_update_extent(ip, state, *idx, &left);
 
                xfs_iext_remove(ip, *idx + 1, 1, state);
                break;
@@ -2816,15 +2592,13 @@ xfs_bmap_add_extent_hole_delay(
                --*idx;
                temp = left.br_blockcount + new->br_blockcount;
 
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(xfs_iext_get_ext(ifp, *idx), temp);
                oldlen = startblockval(left.br_startblock) +
                        startblockval(new->br_startblock);
                newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
                                         oldlen);
-               xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
-                       nullstartblock((int)newlen));
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               left.br_blockcount = temp;
+               left.br_startblock = nullstartblock(newlen);
+               xfs_iext_update_extent(ip, state, *idx, &left);
                break;
 
        case BMAP_RIGHT_CONTIG:
@@ -2833,16 +2607,15 @@ xfs_bmap_add_extent_hole_delay(
                 * on the right.
                 * Merge the new allocation with the right neighbor.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                temp = new->br_blockcount + right.br_blockcount;
                oldlen = startblockval(new->br_startblock) +
                        startblockval(right.br_startblock);
                newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
                                         oldlen);
-               xfs_bmbt_set_allf(xfs_iext_get_ext(ifp, *idx),
-                       new->br_startoff,
-                       nullstartblock((int)newlen), temp, right.br_state);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               right.br_startoff = new->br_startoff;
+               right.br_startblock = nullstartblock(newlen);
+               right.br_blockcount = temp;
+               xfs_iext_update_extent(ip, state, *idx, &right);
                break;
 
        case 0:
@@ -2870,42 +2643,40 @@ 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);
+       int                     state = xfs_bmap_fork_to_state(whichfork);
+       struct xfs_bmbt_irec    old;
 
-       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);
 
-       state = 0;
-       if (whichfork == XFS_ATTR_FORK)
-               state |= BMAP_ATTRFORK;
-       if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
-
        /*
         * 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_iext_get_extent(ifp, *idx - 1, &left);
                if (isnullstartblock(left.br_startblock))
                        state |= BMAP_LEFT_DELAY;
        }
@@ -2914,9 +2685,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_iext_get_extent(ifp, *idx, &right);
                if (isnullstartblock(right.br_startblock))
                        state |= BMAP_RIGHT_DELAY;
        }
@@ -2953,41 +2724,31 @@ 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),
-                       left.br_blockcount + new->br_blockcount +
-                       right.br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               --*idx;
+               left.br_blockcount += new->br_blockcount + right.br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &left);
 
-               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,
-                                       right.br_startblock, right.br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(cur, &right, &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,
-                                       left.br_startblock,
-                                       left.br_blockcount +
-                                               new->br_blockcount +
-                                               right.br_blockcount,
-                                       left.br_state);
+                       error = xfs_bmbt_update(cur, &left);
                        if (error)
                                goto done;
                }
@@ -2999,27 +2760,21 @@ 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),
-                       left.br_blockcount + new->br_blockcount);
-               trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
+               --*idx;
+               old = left;
+
+               left.br_blockcount += new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &left);
 
-               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,
-                                       left.br_startblock, left.br_blockcount,
-                                       &i);
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, left.br_startoff,
-                                       left.br_startblock,
-                                       left.br_blockcount +
-                                               new->br_blockcount,
-                                       left.br_state);
+                       error = xfs_bmbt_update(cur, &left);
                        if (error)
                                goto done;
                }
@@ -3031,29 +2786,22 @@ 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),
-                       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_);
-
-               if (bma->cur == NULL) {
+               old = right;
+
+               right.br_startoff = new->br_startoff;
+               right.br_startblock = new->br_startblock;
+               right.br_blockcount += new->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &right);
+
+               if (cur == NULL) {
                        rval = xfs_ilog_fext(whichfork);
                } else {
                        rval = 0;
-                       error = xfs_bmbt_lookup_eq(bma->cur,
-                                       right.br_startoff,
-                                       right.br_startblock,
-                                       right.br_blockcount, &i);
+                       error = xfs_bmbt_lookup_eq(cur, &old, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       error = xfs_bmbt_update(bma->cur, new->br_startoff,
-                                       new->br_startblock,
-                                       new->br_blockcount +
-                                               right.br_blockcount,
-                                       right.br_state);
+                       error = xfs_bmbt_update(cur, &right);
                        if (error)
                                goto done;
                }
@@ -3065,22 +2813,18 @@ 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,
-                                       new->br_startoff,
-                                       new->br_startblock,
-                                       new->br_blockcount, &i);
+                       error = xfs_bmbt_lookup_eq(cur, new, &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);
+                       error = xfs_btree_insert(cur, &i);
                        if (error)
                                goto done;
                        XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
@@ -3089,30 +2833,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;
 }
 
@@ -3843,60 +3587,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_fsblock_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.
@@ -3905,8 +3595,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);
@@ -3946,6 +3634,17 @@ xfs_trim_extent(
        }
 }
 
+/* trim extent to within eof */
+void
+xfs_trim_extent_eof(
+       struct xfs_bmbt_irec    *irec,
+       struct xfs_inode        *ip)
+
+{
+       xfs_trim_extent(irec, 0, XFS_B_TO_FSB(ip->i_mount,
+                                             i_size_read(VFS_I(ip))));
+}
+
 /*
  * Trim the returned map to the required bounds
  */
@@ -4078,7 +3777,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;
        }
@@ -4290,10 +3989,8 @@ xfs_bmapi_allocate(
        if (bma->wasdel) {
                bma->length = (xfs_extlen_t)bma->got.br_blockcount;
                bma->offset = bma->got.br_startoff;
-               if (bma->idx) {
-                       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx - 1),
-                                        &bma->prev);
-               }
+               if (bma->idx)
+                       xfs_iext_get_extent(ifp, bma->idx - 1, &bma->prev);
        } else {
                bma->length = XFS_FILBLKS_MIN(bma->length, MAXEXTLEN);
                if (!bma->eof)
@@ -4377,7 +4074,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)
@@ -4388,7 +4087,7 @@ xfs_bmapi_allocate(
         * or xfs_bmap_add_extent_hole_real might have merged it into one of
         * the neighbouring ones.
         */
-       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx), &bma->got);
+       xfs_iext_get_extent(ifp, bma->idx, &bma->got);
 
        ASSERT(bma->got.br_startoff <= bma->offset);
        ASSERT(bma->got.br_startoff + bma->got.br_blockcount >=
@@ -4469,7 +4168,7 @@ xfs_bmapi_convert_unwritten(
         * xfs_bmap_add_extent_unwritten_real might have merged it into one
         * of the neighbouring ones.
         */
-       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, bma->idx), &bma->got);
+       xfs_iext_get_extent(ifp, bma->idx, &bma->got);
 
        /*
         * We may have combined previously unwritten space with written space,
@@ -4540,9 +4239,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)) !=
@@ -4559,7 +4256,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;
        }
@@ -4626,13 +4323,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;
                }
 
                /*
@@ -4761,6 +4453,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
@@ -4860,7 +4639,8 @@ xfs_bmap_del_extent_delay(
        int64_t                 da_old, da_new, da_diff = 0;
        xfs_fileoff_t           del_endoff, got_endoff;
        xfs_filblks_t           got_indlen, new_indlen, stolen;
-       int                     error = 0, state = 0;
+       int                     state = xfs_bmap_fork_to_state(whichfork);
+       int                     error = 0;
        bool                    isrt;
 
        XFS_STATS_INC(mp, xs_del_exlist);
@@ -4878,7 +4658,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);
@@ -4896,46 +4676,39 @@ xfs_bmap_del_extent_delay(
                return error;
        ip->i_delayed_blks -= del->br_blockcount;
 
-       if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
-
        if (got->br_startoff == del->br_startoff)
-               state |= BMAP_LEFT_CONTIG;
+               state |= BMAP_LEFT_FILLING;
        if (got_endoff == del_endoff)
-               state |= BMAP_RIGHT_CONTIG;
+               state |= BMAP_RIGHT_FILLING;
 
-       switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) {
-       case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
+       switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+       case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
                /*
                 * Matches the whole extent.  Delete the entry.
                 */
                xfs_iext_remove(ip, *idx, 1, state);
                --*idx;
                break;
-       case BMAP_LEFT_CONTIG:
+       case BMAP_LEFT_FILLING:
                /*
                 * Deleting the first part of the extent.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                got->br_startoff = del_endoff;
                got->br_blockcount -= del->br_blockcount;
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
                                got->br_blockcount), da_old);
                got->br_startblock = nullstartblock((int)da_new);
-               xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               xfs_iext_update_extent(ip, state, *idx, got);
                break;
-       case BMAP_RIGHT_CONTIG:
+       case BMAP_RIGHT_FILLING:
                /*
                 * Deleting the last part of the extent.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                got->br_blockcount = got->br_blockcount - del->br_blockcount;
                da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
                                got->br_blockcount), da_old);
                got->br_startblock = nullstartblock((int)da_new);
-               xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               xfs_iext_update_extent(ip, state, *idx, got);
                break;
        case 0:
                /*
@@ -4947,8 +4720,6 @@ xfs_bmap_del_extent_delay(
                 * Warn if either of the new indlen reservations is zero as this
                 * can lead to delalloc problems.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-
                got->br_blockcount = del->br_startoff - got->br_startoff;
                got_indlen = xfs_bmap_worst_indlen(ip, got->br_blockcount);
 
@@ -4960,8 +4731,7 @@ xfs_bmap_del_extent_delay(
                                                       del->br_blockcount);
 
                got->br_startblock = nullstartblock((int)got_indlen);
-               xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
-               trace_xfs_bmap_post_update(ip, *idx, 0, _THIS_IP_);
+               xfs_iext_update_extent(ip, state, *idx, got);
 
                new.br_startoff = del_endoff;
                new.br_state = got->br_state;
@@ -5010,46 +4780,40 @@ xfs_bmap_del_extent_cow(
        ASSERT(!isnullstartblock(got->br_startblock));
 
        if (got->br_startoff == del->br_startoff)
-               state |= BMAP_LEFT_CONTIG;
+               state |= BMAP_LEFT_FILLING;
        if (got_endoff == del_endoff)
-               state |= BMAP_RIGHT_CONTIG;
+               state |= BMAP_RIGHT_FILLING;
 
-       switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) {
-       case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
+       switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+       case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
                /*
                 * Matches the whole extent.  Delete the entry.
                 */
                xfs_iext_remove(ip, *idx, 1, state);
                --*idx;
                break;
-       case BMAP_LEFT_CONTIG:
+       case BMAP_LEFT_FILLING:
                /*
                 * Deleting the first part of the extent.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                got->br_startoff = del_endoff;
                got->br_blockcount -= del->br_blockcount;
                got->br_startblock = del->br_startblock + del->br_blockcount;
-               xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               xfs_iext_update_extent(ip, state, *idx, got);
                break;
-       case BMAP_RIGHT_CONTIG:
+       case BMAP_RIGHT_FILLING:
                /*
                 * Deleting the last part of the extent.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                got->br_blockcount -= del->br_blockcount;
-               xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               xfs_iext_update_extent(ip, state, *idx, got);
                break;
        case 0:
                /*
                 * Deleting the middle of the extent.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                got->br_blockcount = del->br_startoff - got->br_startoff;
-               xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               xfs_iext_update_extent(ip, state, *idx, got);
 
                new.br_startoff = del_endoff;
                new.br_blockcount = got_endoff - del_endoff;
@@ -5064,10 +4828,10 @@ xfs_bmap_del_extent_cow(
 
 /*
  * Called by xfs_bmapi to update file extent records and the btree
- * after removing space (or undoing a delayed allocation).
+ * after removing space.
  */
 STATIC int                             /* error */
-xfs_bmap_del_extent(
+xfs_bmap_del_extent_real(
        xfs_inode_t             *ip,    /* incore inode pointer */
        xfs_trans_t             *tp,    /* current transaction pointer */
        xfs_extnum_t            *idx,   /* extent number to update/delete */
@@ -5078,16 +4842,12 @@ xfs_bmap_del_extent(
        int                     whichfork, /* data or attr fork */
        int                     bflags) /* bmapi flags */
 {
-       xfs_filblks_t           da_new; /* new delay-alloc indirect blocks */
-       xfs_filblks_t           da_old; /* old delay-alloc indirect blocks */
        xfs_fsblock_t           del_endblock=0; /* first block past del */
        xfs_fileoff_t           del_endoff;     /* first offset past del */
-       int                     delay;  /* current block is delayed allocated */
        int                     do_fx;  /* free extent at end of routine */
-       xfs_bmbt_rec_host_t     *ep;    /* current extent entry pointer */
        int                     error;  /* error return value */
-       int                     flags;  /* inode logging flags */
-       xfs_bmbt_irec_t         got;    /* current extent entry */
+       int                     flags = 0;/* inode logging flags */
+       struct xfs_bmbt_irec    got;    /* current extent entry */
        xfs_fileoff_t           got_endoff;     /* first offset past got */
        int                     i;      /* temp state */
        xfs_ifork_t             *ifp;   /* inode fork pointer */
@@ -5096,102 +4856,82 @@ xfs_bmap_del_extent(
        xfs_bmbt_irec_t         new;    /* new record to be inserted */
        /* REFERENCED */
        uint                    qfield; /* quota field to update */
-       xfs_filblks_t           temp;   /* for indirect length calculations */
-       xfs_filblks_t           temp2;  /* for indirect length calculations */
-       int                     state = 0;
+       int                     state = xfs_bmap_fork_to_state(whichfork);
+       struct xfs_bmbt_irec    old;
 
        mp = ip->i_mount;
        XFS_STATS_INC(mp, xs_del_exlist);
 
-       if (whichfork == XFS_ATTR_FORK)
-               state |= BMAP_ATTRFORK;
-       else if (whichfork == XFS_COW_FORK)
-               state |= BMAP_COWFORK;
-
        ifp = XFS_IFORK_PTR(ip, whichfork);
        ASSERT((*idx >= 0) && (*idx < xfs_iext_count(ifp)));
        ASSERT(del->br_blockcount > 0);
-       ep = xfs_iext_get_ext(ifp, *idx);
-       xfs_bmbt_get_all(ep, &got);
+       xfs_iext_get_extent(ifp, *idx, &got);
        ASSERT(got.br_startoff <= del->br_startoff);
        del_endoff = del->br_startoff + del->br_blockcount;
        got_endoff = got.br_startoff + got.br_blockcount;
        ASSERT(got_endoff >= del_endoff);
-       delay = isnullstartblock(got.br_startblock);
-       ASSERT(isnullstartblock(del->br_startblock) == delay);
-       flags = 0;
+       ASSERT(!isnullstartblock(got.br_startblock));
        qfield = 0;
        error = 0;
+
        /*
-        * If deleting a real allocation, must free up the disk space.
+        * If it's the case where the directory code is running with no block
+        * reservation, and the deleted block is in the middle of its extent,
+        * and the resulting insert of an extent would cause transformation to
+        * btree format, then reject it.  The calling code will then swap blocks
+        * around instead.  We have to do this now, rather than waiting for the
+        * conversion to btree format, since the transaction will be dirty then.
         */
-       if (!delay) {
-               flags = XFS_ILOG_CORE;
-               /*
-                * Realtime allocation.  Free it and record di_nblocks update.
-                */
-               if (whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip)) {
-                       xfs_fsblock_t   bno;
-                       xfs_filblks_t   len;
-
-                       ASSERT(do_mod(del->br_blockcount,
-                                     mp->m_sb.sb_rextsize) == 0);
-                       ASSERT(do_mod(del->br_startblock,
-                                     mp->m_sb.sb_rextsize) == 0);
-                       bno = del->br_startblock;
-                       len = del->br_blockcount;
-                       do_div(bno, mp->m_sb.sb_rextsize);
-                       do_div(len, mp->m_sb.sb_rextsize);
-                       error = xfs_rtfree_extent(tp, bno, (xfs_extlen_t)len);
-                       if (error)
-                               goto done;
-                       do_fx = 0;
-                       nblks = len * mp->m_sb.sb_rextsize;
-                       qfield = XFS_TRANS_DQ_RTBCOUNT;
-               }
-               /*
-                * Ordinary allocation.
-                */
-               else {
-                       do_fx = 1;
-                       nblks = del->br_blockcount;
-                       qfield = XFS_TRANS_DQ_BCOUNT;
-               }
-               /*
-                * Set up del_endblock and cur for later.
-                */
-               del_endblock = del->br_startblock + del->br_blockcount;
-               if (cur) {
-                       if ((error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
-                                       got.br_startblock, got.br_blockcount,
-                                       &i)))
-                               goto done;
-                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-               }
-               da_old = da_new = 0;
-       } else {
-               da_old = startblockval(got.br_startblock);
-               da_new = 0;
-               nblks = 0;
+       if (tp->t_blk_res == 0 &&
+           XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
+           XFS_IFORK_NEXTENTS(ip, whichfork) >=
+                       XFS_IFORK_MAXEXT(ip, whichfork) &&
+           del->br_startoff > got.br_startoff && del_endoff < got_endoff)
+               return -ENOSPC;
+
+       flags = XFS_ILOG_CORE;
+       if (whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip)) {
+               xfs_fsblock_t   bno;
+               xfs_filblks_t   len;
+
+               ASSERT(do_mod(del->br_blockcount, mp->m_sb.sb_rextsize) == 0);
+               ASSERT(do_mod(del->br_startblock, mp->m_sb.sb_rextsize) == 0);
+               bno = del->br_startblock;
+               len = del->br_blockcount;
+               do_div(bno, mp->m_sb.sb_rextsize);
+               do_div(len, mp->m_sb.sb_rextsize);
+               error = xfs_rtfree_extent(tp, bno, (xfs_extlen_t)len);
+               if (error)
+                       goto done;
                do_fx = 0;
+               nblks = len * mp->m_sb.sb_rextsize;
+               qfield = XFS_TRANS_DQ_RTBCOUNT;
+       } else {
+               do_fx = 1;
+               nblks = del->br_blockcount;
+               qfield = XFS_TRANS_DQ_BCOUNT;
        }
 
-       /*
-        * Set flag value to use in switch statement.
-        * Left-contig is 2, right-contig is 1.
-        */
-       switch (((got.br_startoff == del->br_startoff) << 1) |
-               (got_endoff == del_endoff)) {
-       case 3:
+       del_endblock = del->br_startblock + del->br_blockcount;
+       if (cur) {
+               error = xfs_bmbt_lookup_eq(cur, &got, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+       }
+
+       if (got.br_startoff == del->br_startoff)
+               state |= BMAP_LEFT_FILLING;
+       if (got_endoff == del_endoff)
+               state |= BMAP_RIGHT_FILLING;
+
+       switch (state & (BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING)) {
+       case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING:
                /*
                 * Matches the whole extent.  Delete the entry.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_iext_remove(ip, *idx, 1,
-                               whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
+               xfs_iext_remove(ip, *idx, 1, state);
                --*idx;
-               if (delay)
-                       break;
 
                XFS_IFORK_NEXT_SET(ip, whichfork,
                        XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
@@ -5204,168 +4944,106 @@ xfs_bmap_del_extent(
                        goto done;
                XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                break;
-
-       case 2:
+       case BMAP_LEFT_FILLING:
                /*
                 * Deleting the first part of the extent.
                 */
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_startoff(ep, del_endoff);
-               temp = got.br_blockcount - del->br_blockcount;
-               xfs_bmbt_set_blockcount(ep, temp);
-               if (delay) {
-                       temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
-                               da_old);
-                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
-                       trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
-                       da_new = temp;
-                       break;
-               }
-               xfs_bmbt_set_startblock(ep, del_endblock);
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               got.br_startoff = del_endoff;
+               got.br_startblock = del_endblock;
+               got.br_blockcount -= del->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &got);
                if (!cur) {
                        flags |= xfs_ilog_fext(whichfork);
                        break;
                }
-               if ((error = xfs_bmbt_update(cur, del_endoff, del_endblock,
-                               got.br_blockcount - del->br_blockcount,
-                               got.br_state)))
+               error = xfs_bmbt_update(cur, &got);
+               if (error)
                        goto done;
                break;
-
-       case 1:
+       case BMAP_RIGHT_FILLING:
                /*
                 * Deleting the last part of the extent.
                 */
-               temp = got.br_blockcount - del->br_blockcount;
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep, temp);
-               if (delay) {
-                       temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
-                               da_old);
-                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
-                       trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
-                       da_new = temp;
-                       break;
-               }
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               got.br_blockcount -= del->br_blockcount;
+               xfs_iext_update_extent(ip, state, *idx, &got);
                if (!cur) {
                        flags |= xfs_ilog_fext(whichfork);
                        break;
                }
-               if ((error = xfs_bmbt_update(cur, got.br_startoff,
-                               got.br_startblock,
-                               got.br_blockcount - del->br_blockcount,
-                               got.br_state)))
+               error = xfs_bmbt_update(cur, &got);
+               if (error)
                        goto done;
                break;
-
        case 0:
                /*
                 * Deleting the middle of the extent.
                 */
-               temp = del->br_startoff - got.br_startoff;
-               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
-               xfs_bmbt_set_blockcount(ep, temp);
+               old = got;
+
+               got.br_blockcount = del->br_startoff - got.br_startoff;
+               xfs_iext_update_extent(ip, state, *idx, &got);
+
                new.br_startoff = del_endoff;
-               temp2 = got_endoff - del_endoff;
-               new.br_blockcount = temp2;
+               new.br_blockcount = got_endoff - del_endoff;
                new.br_state = got.br_state;
-               if (!delay) {
-                       new.br_startblock = del_endblock;
-                       flags |= XFS_ILOG_CORE;
-                       if (cur) {
-                               if ((error = xfs_bmbt_update(cur,
-                                               got.br_startoff,
-                                               got.br_startblock, temp,
-                                               got.br_state)))
-                                       goto done;
-                               if ((error = xfs_btree_increment(cur, 0, &i)))
-                                       goto done;
-                               cur->bc_rec.b = new;
-                               error = xfs_btree_insert(cur, &i);
-                               if (error && error != -ENOSPC)
-                                       goto done;
+               new.br_startblock = del_endblock;
+
+               flags |= XFS_ILOG_CORE;
+               if (cur) {
+                       error = xfs_bmbt_update(cur, &got);
+                       if (error)
+                               goto done;
+                       error = xfs_btree_increment(cur, 0, &i);
+                       if (error)
+                               goto done;
+                       cur->bc_rec.b = new;
+                       error = xfs_btree_insert(cur, &i);
+                       if (error && error != -ENOSPC)
+                               goto done;
+                       /*
+                        * If get no-space back from btree insert, it tried a
+                        * split, and we have a zero block reservation.  Fix up
+                        * our state and return the error.
+                        */
+                       if (error == -ENOSPC) {
                                /*
-                                * If get no-space back from btree insert,
-                                * it tried a split, and we have a zero
-                                * block reservation.
-                                * Fix up our state and return the error.
+                                * Reset the cursor, don't trust it after any
+                                * insert operation.
                                 */
-                               if (error == -ENOSPC) {
-                                       /*
-                                        * Reset the cursor, don't trust
-                                        * it after any insert operation.
-                                        */
-                                       if ((error = xfs_bmbt_lookup_eq(cur,
-                                                       got.br_startoff,
-                                                       got.br_startblock,
-                                                       temp, &i)))
-                                               goto done;
-                                       XFS_WANT_CORRUPTED_GOTO(mp,
-                                                               i == 1, done);
-                                       /*
-                                        * Update the btree record back
-                                        * to the original value.
-                                        */
-                                       if ((error = xfs_bmbt_update(cur,
-                                                       got.br_startoff,
-                                                       got.br_startblock,
-                                                       got.br_blockcount,
-                                                       got.br_state)))
-                                               goto done;
-                                       /*
-                                        * Reset the extent record back
-                                        * to the original value.
-                                        */
-                                       xfs_bmbt_set_blockcount(ep,
-                                               got.br_blockcount);
-                                       flags = 0;
-                                       error = -ENOSPC;
+                               error = xfs_bmbt_lookup_eq(cur, &got, &i);
+                               if (error)
                                        goto done;
-                               }
                                XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
-                       } else
-                               flags |= xfs_ilog_fext(whichfork);
-                       XFS_IFORK_NEXT_SET(ip, whichfork,
-                               XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
-               } else {
-                       xfs_filblks_t   stolen;
-                       ASSERT(whichfork == XFS_DATA_FORK);
-
-                       /*
-                        * Distribute the original indlen reservation across the
-                        * two new extents. Steal blocks from the deleted extent
-                        * if necessary. Stealing blocks simply fudges the
-                        * fdblocks accounting in xfs_bunmapi().
-                        */
-                       temp = xfs_bmap_worst_indlen(ip, got.br_blockcount);
-                       temp2 = xfs_bmap_worst_indlen(ip, new.br_blockcount);
-                       stolen = xfs_bmap_split_indlen(da_old, &temp, &temp2,
-                                                      del->br_blockcount);
-                       da_new = temp + temp2 - stolen;
-                       del->br_blockcount -= stolen;
-
-                       /*
-                        * Set the reservation for each extent. Warn if either
-                        * is zero as this can lead to delalloc problems.
-                        */
-                       WARN_ON_ONCE(!temp || !temp2);
-                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
-                       new.br_startblock = nullstartblock((int)temp2);
-               }
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+                               /*
+                                * Update the btree record back
+                                * to the original value.
+                                */
+                               error = xfs_bmbt_update(cur, &old);
+                               if (error)
+                                       goto done;
+                               /*
+                                * Reset the extent record back
+                                * to the original value.
+                                */
+                               xfs_iext_update_extent(ip, state, *idx, &old);
+                               flags = 0;
+                               error = -ENOSPC;
+                               goto done;
+                       }
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               } else
+                       flags |= xfs_ilog_fext(whichfork);
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                       XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
                xfs_iext_insert(ip, *idx + 1, 1, &new, state);
                ++*idx;
                break;
        }
 
        /* remove reverse mapping */
-       if (!delay) {
-               error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, del);
-               if (error)
-                       goto done;
-       }
+       error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, del);
+       if (error)
+               goto done;
 
        /*
         * If we need to, add to list of extents to delete.
@@ -5391,13 +5069,6 @@ xfs_bmap_del_extent(
        if (qfield && !(bflags & XFS_BMAPI_REMAP))
                xfs_trans_mod_dquot_byino(tp, ip, qfield, (long)-nblks);
 
-       /*
-        * Account for change in delayed indirect blocks.
-        * Nothing to do for disk quota accounting here.
-        */
-       ASSERT(da_old >= da_new);
-       if (da_old > da_new)
-               xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false);
 done:
        *logflagsp = flags;
        return error;
@@ -5413,7 +5084,7 @@ int                                               /* error */
 __xfs_bunmapi(
        xfs_trans_t             *tp,            /* transaction pointer */
        struct xfs_inode        *ip,            /* incore inode */
-       xfs_fileoff_t           bno,            /* starting offset to unmap */
+       xfs_fileoff_t           start,          /* first file offset deleted */
        xfs_filblks_t           *rlen,          /* i/o: amount remaining */
        int                     flags,          /* misc flags */
        xfs_extnum_t            nexts,          /* number of extents max */
@@ -5432,14 +5103,16 @@ __xfs_bunmapi(
        int                     logflags;       /* transaction logging flags */
        xfs_extlen_t            mod;            /* rt extent offset */
        xfs_mount_t             *mp;            /* mount structure */
-       xfs_fileoff_t           start;          /* first file offset deleted */
        int                     tmp_logflags;   /* partial logging flags */
        int                     wasdel;         /* was a delayed alloc extent */
        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;
+       xfs_agnumber_t          prev_agno = NULLAGNUMBER, agno;
+       xfs_fileoff_t           end;
 
-       trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
+       trace_xfs_bunmap(ip, start, len, flags, _RET_IP_);
 
        whichfork = xfs_bmapi_whichfork(flags);
        ASSERT(whichfork != XFS_COW_FORK);
@@ -5459,6 +5132,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;
@@ -5468,17 +5151,16 @@ __xfs_bunmapi(
        }
        XFS_STATS_INC(mp, xs_blk_unmap);
        isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip);
-       start = bno;
-       bno = start + len - 1;
+       end = start + len - 1;
 
        /*
         * Check to see if the given block number is past the end of the
         * file, back up to the last block if so...
         */
-       if (!xfs_iext_lookup_extent(ip, ifp, bno, &lastx, &got)) {
+       if (!xfs_iext_lookup_extent(ip, ifp, end, &lastx, &got)) {
                ASSERT(lastx > 0);
                xfs_iext_get_extent(ifp, --lastx, &got);
-               bno = got.br_startoff + got.br_blockcount - 1;
+               end = got.br_startoff + got.br_blockcount - 1;
        }
 
        logflags = 0;
@@ -5502,13 +5184,13 @@ __xfs_bunmapi(
        }
 
        extno = 0;
-       while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 &&
-              (nexts == 0 || extno < nexts)) {
+       while (end != (xfs_fileoff_t)-1 && end >= start && lastx >= 0 &&
+              (nexts == 0 || extno < nexts) && max_len > 0) {
                /*
-                * Is the found extent after a hole in which bno lives?
+                * Is the found extent after a hole in which end lives?
                 * Just back up to the previous extent, if so.
                 */
-               if (got.br_startoff > bno) {
+               if (got.br_startoff > end) {
                        if (--lastx < 0)
                                break;
                        xfs_iext_get_extent(ifp, lastx, &got);
@@ -5517,9 +5199,9 @@ __xfs_bunmapi(
                 * Is the last block of this extent before the range
                 * we're supposed to delete?  If so, we're done.
                 */
-               bno = XFS_FILEOFF_MIN(bno,
+               end = XFS_FILEOFF_MIN(end,
                        got.br_startoff + got.br_blockcount - 1);
-               if (bno < start)
+               if (end < start)
                        break;
                /*
                 * Then deal with the (possibly delayed) allocated space
@@ -5527,14 +5209,34 @@ __xfs_bunmapi(
                 */
                del = got;
                wasdel = isnullstartblock(del.br_startblock);
+
+               /*
+                * Make sure we don't touch multiple AGF headers out of order
+                * in a single transaction, as that could cause AB-BA deadlocks.
+                */
+               if (!wasdel) {
+                       agno = XFS_FSB_TO_AGNO(mp, del.br_startblock);
+                       if (prev_agno != NULLAGNUMBER && prev_agno > agno)
+                               break;
+                       prev_agno = agno;
+               }
                if (got.br_startoff < start) {
                        del.br_startoff = start;
                        del.br_blockcount -= start - got.br_startoff;
                        if (!wasdel)
                                del.br_startblock += start - got.br_startoff;
                }
-               if (del.br_startoff + del.br_blockcount > bno + 1)
-                       del.br_blockcount = bno + 1 - del.br_startoff;
+               if (del.br_startoff + del.br_blockcount > end + 1)
+                       del.br_blockcount = end + 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))) {
@@ -5551,13 +5253,13 @@ __xfs_bunmapi(
                                 * This piece is unwritten, or we're not
                                 * using unwritten extents.  Skip over it.
                                 */
-                               ASSERT(bno >= mod);
-                               bno -= mod > del.br_blockcount ?
+                               ASSERT(end >= mod);
+                               end -= mod > del.br_blockcount ?
                                        del.br_blockcount : mod;
-                               if (bno < got.br_startoff) {
+                               if (end < got.br_startoff) {
                                        if (--lastx >= 0)
-                                               xfs_bmbt_get_all(xfs_iext_get_ext(
-                                                       ifp, lastx), &got);
+                                               xfs_iext_get_extent(ifp, lastx,
+                                                               &got);
                                }
                                continue;
                        }
@@ -5603,9 +5305,9 @@ __xfs_bunmapi(
                                 * Can't make it unwritten.  There isn't
                                 * a full extent here so just skip it.
                                 */
-                               ASSERT(bno >= del.br_blockcount);
-                               bno -= del.br_blockcount;
-                               if (got.br_startoff > bno && --lastx >= 0)
+                               ASSERT(end >= del.br_blockcount);
+                               end -= del.br_blockcount;
+                               if (got.br_startoff > end && --lastx >= 0)
                                        xfs_iext_get_extent(ifp, lastx, &got);
                                continue;
                        } else if (del.br_state == XFS_EXT_UNWRITTEN) {
@@ -5651,84 +5353,38 @@ __xfs_bunmapi(
                        }
                }
 
-               /*
-                * If it's the case where the directory code is running
-                * with no block reservation, and the deleted block is in
-                * the middle of its extent, and the resulting insert
-                * of an extent would cause transformation to btree format,
-                * then reject it.  The calling code will then swap
-                * blocks around instead.
-                * We have to do this now, rather than waiting for the
-                * conversion to btree format, since the transaction
-                * will be dirty.
-                */
-               if (!wasdel && tp->t_blk_res == 0 &&
-                   XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
-                   XFS_IFORK_NEXTENTS(ip, whichfork) >= /* Note the >= */
-                       XFS_IFORK_MAXEXT(ip, whichfork) &&
-                   del.br_startoff > got.br_startoff &&
-                   del.br_startoff + del.br_blockcount <
-                   got.br_startoff + got.br_blockcount) {
-                       error = -ENOSPC;
-                       goto error0;
+               if (wasdel) {
+                       error = xfs_bmap_del_extent_delay(ip, whichfork, &lastx,
+                                       &got, &del);
+               } else {
+                       error = xfs_bmap_del_extent_real(ip, tp, &lastx, dfops,
+                                       cur, &del, &tmp_logflags, whichfork,
+                                       flags);
+                       logflags |= tmp_logflags;
                }
 
-               /*
-                * Unreserve quota and update realtime free space, if
-                * appropriate. If delayed allocation, update the inode delalloc
-                * counter now and wait to update the sb counters as
-                * xfs_bmap_del_extent() might need to borrow some blocks.
-                */
-               if (wasdel) {
-                       ASSERT(startblockval(del.br_startblock) > 0);
-                       if (isrt) {
-                               xfs_filblks_t rtexts;
-
-                               rtexts = XFS_FSB_TO_B(mp, del.br_blockcount);
-                               do_div(rtexts, mp->m_sb.sb_rextsize);
-                               xfs_mod_frextents(mp, (int64_t)rtexts);
-                               (void)xfs_trans_reserve_quota_nblks(NULL,
-                                       ip, -((long)del.br_blockcount), 0,
-                                       XFS_QMOPT_RES_RTBLKS);
-                       } else {
-                               (void)xfs_trans_reserve_quota_nblks(NULL,
-                                       ip, -((long)del.br_blockcount), 0,
-                                       XFS_QMOPT_RES_REGBLKS);
-                       }
-                       ip->i_delayed_blks -= del.br_blockcount;
-                       if (cur)
-                               cur->bc_private.b.flags |=
-                                       XFS_BTCUR_BPRV_WASDEL;
-               } else if (cur)
-                       cur->bc_private.b.flags &= ~XFS_BTCUR_BPRV_WASDEL;
-
-               error = xfs_bmap_del_extent(ip, tp, &lastx, dfops, cur, &del,
-                               &tmp_logflags, whichfork, flags);
-               logflags |= tmp_logflags;
                if (error)
                        goto error0;
 
-               if (!isrt && wasdel)
-                       xfs_mod_fdblocks(mp, (int64_t)del.br_blockcount, false);
-
-               bno = del.br_startoff - 1;
+               max_len -= del.br_blockcount;
+               end = del.br_startoff - 1;
 nodelete:
                /*
                 * If not done go on to the next (previous) record.
                 */
-               if (bno != (xfs_fileoff_t)-1 && bno >= start) {
+               if (end != (xfs_fileoff_t)-1 && end >= start) {
                        if (lastx >= 0) {
                                xfs_iext_get_extent(ifp, lastx, &got);
-                               if (got.br_startoff > bno && --lastx >= 0)
+                               if (got.br_startoff > end && --lastx >= 0)
                                        xfs_iext_get_extent(ifp, lastx, &got);
                        }
                        extno++;
                }
        }
-       if (bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0)
+       if (end == (xfs_fileoff_t)-1 || end < start || lastx < 0)
                *rlen = 0;
        else
-               *rlen = bno - start + 1;
+               *rlen = end - start + 1;
 
        /*
         * Convert to a btree if necessary.
@@ -5847,32 +5503,25 @@ xfs_bmse_merge(
        int                             whichfork,
        xfs_fileoff_t                   shift,          /* shift fsb */
        int                             current_ext,    /* idx of gotp */
-       struct xfs_bmbt_rec_host        *gotp,          /* extent to shift */
-       struct xfs_bmbt_rec_host        *leftp,         /* preceding extent */
+       struct xfs_bmbt_irec            *got,           /* extent to shift */
+       struct xfs_bmbt_irec            *left,          /* preceding extent */
        struct xfs_btree_cur            *cur,
-       int                             *logflags)      /* output */
+       int                             *logflags,      /* output */
+       struct xfs_defer_ops            *dfops)
 {
-       struct xfs_bmbt_irec            got;
-       struct xfs_bmbt_irec            left;
+       struct xfs_bmbt_irec            new;
        xfs_filblks_t                   blockcount;
        int                             error, i;
        struct xfs_mount                *mp = ip->i_mount;
 
-       xfs_bmbt_get_all(gotp, &got);
-       xfs_bmbt_get_all(leftp, &left);
-       blockcount = left.br_blockcount + got.br_blockcount;
+       blockcount = left->br_blockcount + got->br_blockcount;
 
        ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-       ASSERT(xfs_bmse_can_merge(&left, &got, shift));
+       ASSERT(xfs_bmse_can_merge(left, got, shift));
 
-       /*
-        * Merge the in-core extents. Note that the host record pointers and
-        * current_ext index are invalid once the extent has been removed via
-        * xfs_iext_remove().
-        */
-       xfs_bmbt_set_blockcount(leftp, blockcount);
-       xfs_iext_remove(ip, current_ext, 1, 0);
+       new = *left;
+       new.br_blockcount = blockcount;
 
        /*
         * Update the on-disk extent count, the btree if necessary and log the
@@ -5883,12 +5532,11 @@ xfs_bmse_merge(
        *logflags |= XFS_ILOG_CORE;
        if (!cur) {
                *logflags |= XFS_ILOG_DEXT;
-               return 0;
+               goto done;
        }
 
        /* lookup and remove the extent to merge */
-       error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock,
-                                  got.br_blockcount, &i);
+       error = xfs_bmbt_lookup_eq(cur, got, &i);
        if (error)
                return error;
        XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
@@ -5899,205 +5547,209 @@ xfs_bmse_merge(
        XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
 
        /* lookup and update size of the previous extent */
-       error = xfs_bmbt_lookup_eq(cur, left.br_startoff, left.br_startblock,
-                                  left.br_blockcount, &i);
+       error = xfs_bmbt_lookup_eq(cur, left, &i);
        if (error)
                return error;
        XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
 
-       left.br_blockcount = blockcount;
+       error = xfs_bmbt_update(cur, &new);
+       if (error)
+               return error;
 
-       return xfs_bmbt_update(cur, left.br_startoff, left.br_startblock,
-                              left.br_blockcount, left.br_state);
+done:
+       xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
+                       current_ext - 1, &new);
+       xfs_iext_remove(ip, current_ext, 1, 0);
+
+       /* update reverse mapping. rmap functions merge the rmaps for us */
+       error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, got);
+       if (error)
+               return error;
+       memcpy(&new, got, sizeof(new));
+       new.br_startoff = left->br_startoff + left->br_blockcount;
+       return xfs_rmap_map_extent(mp, dfops, ip, whichfork, &new);
 }
 
-/*
- * Shift a single extent.
- */
-STATIC int
-xfs_bmse_shift_one(
-       struct xfs_inode                *ip,
-       int                             whichfork,
-       xfs_fileoff_t                   offset_shift_fsb,
-       int                             *current_ext,
-       struct xfs_bmbt_rec_host        *gotp,
-       struct xfs_btree_cur            *cur,
-       int                             *logflags,
-       enum shift_direction            direction,
-       struct xfs_defer_ops            *dfops)
+static int
+xfs_bmap_shift_update_extent(
+       struct xfs_inode        *ip,
+       int                     whichfork,
+       xfs_extnum_t            idx,
+       struct xfs_bmbt_irec    *got,
+       struct xfs_btree_cur    *cur,
+       int                     *logflags,
+       struct xfs_defer_ops    *dfops,
+       xfs_fileoff_t           startoff)
 {
-       struct xfs_ifork                *ifp;
-       struct xfs_mount                *mp;
-       xfs_fileoff_t                   startoff;
-       struct xfs_bmbt_rec_host        *adj_irecp;
-       struct xfs_bmbt_irec            got;
-       struct xfs_bmbt_irec            adj_irec;
-       int                             error;
-       int                             i;
-       int                             total_extents;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_bmbt_irec    prev = *got;
+       int                     error, i;
 
-       mp = ip->i_mount;
-       ifp = XFS_IFORK_PTR(ip, whichfork);
-       total_extents = xfs_iext_count(ifp);
+       *logflags |= XFS_ILOG_CORE;
 
-       xfs_bmbt_get_all(gotp, &got);
+       got->br_startoff = startoff;
 
-       /* delalloc extents should be prevented by caller */
-       XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
+       if (cur) {
+               error = xfs_bmbt_lookup_eq(cur, &prev, &i);
+               if (error)
+                       return error;
+               XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
 
-       if (direction == SHIFT_LEFT) {
-               startoff = got.br_startoff - offset_shift_fsb;
+               error = xfs_bmbt_update(cur, got);
+               if (error)
+                       return error;
+       } else {
+               *logflags |= XFS_ILOG_DEXT;
+       }
 
-               /*
-                * Check for merge if we've got an extent to the left,
-                * otherwise make sure there's enough room at the start
-                * of the file for the shift.
-                */
-               if (!*current_ext) {
-                       if (got.br_startoff < offset_shift_fsb)
-                               return -EINVAL;
-                       goto update_current_ext;
-               }
-               /*
-                * grab the left extent and check for a large
-                * enough hole.
-                */
-               adj_irecp = xfs_iext_get_ext(ifp, *current_ext - 1);
-               xfs_bmbt_get_all(adj_irecp, &adj_irec);
+       xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), idx, got);
 
-               if (startoff <
-                   adj_irec.br_startoff + adj_irec.br_blockcount)
-                       return -EINVAL;
+       /* update reverse mapping */
+       error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, &prev);
+       if (error)
+               return error;
+       return xfs_rmap_map_extent(mp, dfops, ip, whichfork, got);
+}
 
-               /* check whether to merge the extent or shift it down */
-               if (xfs_bmse_can_merge(&adj_irec, &got,
-                                      offset_shift_fsb)) {
+int
+xfs_bmap_collapse_extents(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           *next_fsb,
+       xfs_fileoff_t           offset_shift_fsb,
+       bool                    *done,
+       xfs_fileoff_t           stop_fsb,
+       xfs_fsblock_t           *firstblock,
+       struct xfs_defer_ops    *dfops)
+{
+       int                     whichfork = XFS_DATA_FORK;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
+       struct xfs_btree_cur    *cur = NULL;
+       struct xfs_bmbt_irec    got, prev;
+       xfs_extnum_t            current_ext;
+       xfs_fileoff_t           new_startoff;
+       int                     error = 0;
+       int                     logflags = 0;
+
+       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_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
+               return -EFSCORRUPTED;
+       }
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL));
+
+       if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+               error = xfs_iread_extents(tp, ip, whichfork);
+               if (error)
+                       return error;
+       }
+
+       if (ifp->if_flags & XFS_IFBROOT) {
+               cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
+               cur->bc_private.b.firstblock = *firstblock;
+               cur->bc_private.b.dfops = dfops;
+               cur->bc_private.b.flags = 0;
+       }
+
+       if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &current_ext, &got)) {
+               *done = true;
+               goto del_cursor;
+       }
+       XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
+
+       new_startoff = got.br_startoff - offset_shift_fsb;
+       if (xfs_iext_get_extent(ifp, current_ext - 1, &prev)) {
+               if (new_startoff < prev.br_startoff + prev.br_blockcount) {
+                       error = -EINVAL;
+                       goto del_cursor;
+               }
+
+               if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) {
                        error = xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
-                                              *current_ext, gotp, adj_irecp,
-                                              cur, logflags);
+                                       current_ext, &got, &prev, cur,
+                                       &logflags, dfops);
                        if (error)
-                               return error;
-                       adj_irec = got;
-                       goto update_rmap;
+                               goto del_cursor;
+
+                       /* update got after merge */
+                       if (!xfs_iext_get_extent(ifp, current_ext, &got)) {
+                               *done = true;
+                               goto del_cursor;
+                       }
+                       goto done;
                }
        } else {
-               startoff = got.br_startoff + offset_shift_fsb;
-               /* nothing to move if this is the last extent */
-               if (*current_ext >= (total_extents - 1))
-                       goto update_current_ext;
-               /*
-                * If this is not the last extent in the file, make sure there
-                * is enough room between current extent and next extent for
-                * accommodating the shift.
-                */
-               adj_irecp = xfs_iext_get_ext(ifp, *current_ext + 1);
-               xfs_bmbt_get_all(adj_irecp, &adj_irec);
-               if (startoff + got.br_blockcount > adj_irec.br_startoff)
-                       return -EINVAL;
-               /*
-                * Unlike a left shift (which involves a hole punch),
-                * a right shift does not modify extent neighbors
-                * in any way. We should never find mergeable extents
-                * in this scenario. Check anyways and warn if we
-                * encounter two extents that could be one.
-                */
-               if (xfs_bmse_can_merge(&got, &adj_irec, offset_shift_fsb))
-                       WARN_ON_ONCE(1);
-       }
-       /*
-        * Increment the extent index for the next iteration, update the start
-        * offset of the in-core extent and update the btree if applicable.
-        */
-update_current_ext:
-       if (direction == SHIFT_LEFT)
-               (*current_ext)++;
-       else
-               (*current_ext)--;
-       xfs_bmbt_set_startoff(gotp, startoff);
-       *logflags |= XFS_ILOG_CORE;
-       adj_irec = got;
-       if (!cur) {
-               *logflags |= XFS_ILOG_DEXT;
-               goto update_rmap;
+               if (got.br_startoff < offset_shift_fsb) {
+                       error = -EINVAL;
+                       goto del_cursor;
+               }
        }
 
-       error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock,
-                                  got.br_blockcount, &i);
+       error = xfs_bmap_shift_update_extent(ip, whichfork, current_ext, &got,
+                       cur, &logflags, dfops, new_startoff);
        if (error)
-               return error;
-       XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
+               goto del_cursor;
 
-       got.br_startoff = startoff;
-       error = xfs_bmbt_update(cur, got.br_startoff, got.br_startblock,
-                       got.br_blockcount, got.br_state);
-       if (error)
-               return error;
+       if (!xfs_iext_get_extent(ifp, ++current_ext, &got)) {
+                *done = true;
+                goto del_cursor;
+       }
 
-update_rmap:
-       /* update reverse mapping */
-       error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, &adj_irec);
-       if (error)
-               return error;
-       adj_irec.br_startoff = startoff;
-       return xfs_rmap_map_extent(mp, dfops, ip, whichfork, &adj_irec);
+done:
+       *next_fsb = got.br_startoff;
+del_cursor:
+       if (cur)
+               xfs_btree_del_cursor(cur,
+                       error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       if (logflags)
+               xfs_trans_log_inode(tp, ip, logflags);
+       return error;
 }
 
-/*
- * Shift extent records to the left/right to cover/create a hole.
- *
- * The maximum number of extents to be shifted in a single operation is
- * @num_exts. @stop_fsb specifies the file offset at which to stop shift and the
- * file offset where we've left off is returned in @next_fsb. @offset_shift_fsb
- * is the length by which each extent is shifted. If there is no hole to shift
- * the extents into, this will be considered invalid operation and we abort
- * immediately.
- */
 int
-xfs_bmap_shift_extents(
+xfs_bmap_insert_extents(
        struct xfs_trans        *tp,
        struct xfs_inode        *ip,
        xfs_fileoff_t           *next_fsb,
        xfs_fileoff_t           offset_shift_fsb,
-       int                     *done,
+       bool                    *done,
        xfs_fileoff_t           stop_fsb,
        xfs_fsblock_t           *firstblock,
-       struct xfs_defer_ops    *dfops,
-       enum shift_direction    direction,
-       int                     num_exts)
+       struct xfs_defer_ops    *dfops)
 {
-       struct xfs_btree_cur            *cur = NULL;
-       struct xfs_bmbt_rec_host        *gotp;
-       struct xfs_bmbt_irec            got;
-       struct xfs_mount                *mp = ip->i_mount;
-       struct xfs_ifork                *ifp;
-       xfs_extnum_t                    nexts = 0;
-       xfs_extnum_t                    current_ext;
-       xfs_extnum_t                    total_extents;
-       xfs_extnum_t                    stop_extent;
-       int                             error = 0;
-       int                             whichfork = XFS_DATA_FORK;
-       int                             logflags = 0;
+       int                     whichfork = XFS_DATA_FORK;
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
+       struct xfs_btree_cur    *cur = NULL;
+       struct xfs_bmbt_irec    got, next, s;
+       xfs_extnum_t            current_ext;
+       xfs_extnum_t            total_extents;
+       xfs_extnum_t            stop_extent;
+       xfs_fileoff_t           new_startoff;
+       int                     error = 0;
+       int                     logflags = 0;
 
        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))) {
-               XFS_ERROR_REPORT("xfs_bmap_shift_extents",
-                                XFS_ERRLEVEL_LOW, mp);
+            mp, XFS_ERRTAG_BMAPIFORMAT))) {
+               XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
                return -EFSCORRUPTED;
        }
 
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
-       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
-       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
-       ASSERT(direction == SHIFT_LEFT || direction == SHIFT_RIGHT);
-       ASSERT(*next_fsb != NULLFSBLOCK || direction == SHIFT_RIGHT);
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL));
 
-       ifp = XFS_IFORK_PTR(ip, whichfork);
        if (!(ifp->if_flags & XFS_IFEXTENTS)) {
-               /* Read in all the extents */
                error = xfs_iread_extents(tp, ip, whichfork);
                if (error)
                        return error;
@@ -6117,7 +5769,7 @@ xfs_bmap_shift_extents(
         */
        total_extents = xfs_iext_count(ifp);
        if (total_extents == 0) {
-               *done = 1;
+               *done = true;
                goto del_cursor;
        }
 
@@ -6125,81 +5777,74 @@ xfs_bmap_shift_extents(
         * In case of first right shift, we need to initialize next_fsb
         */
        if (*next_fsb == NULLFSBLOCK) {
-               gotp = xfs_iext_get_ext(ifp, total_extents - 1);
-               xfs_bmbt_get_all(gotp, &got);
+               current_ext = total_extents - 1;
+               xfs_iext_get_extent(ifp, current_ext, &got);
+               if (stop_fsb > got.br_startoff) {
+                       *done = true;
+                       goto del_cursor;
+               }
                *next_fsb = got.br_startoff;
-               if (stop_fsb > *next_fsb) {
-                       *done = 1;
+       } else {
+               /*
+                * Look up the extent index for the fsb where we start shifting. We can
+                * henceforth iterate with current_ext as extent list changes are locked
+                * out via ilock.
+                *
+                * If next_fsb lies in a hole beyond which there are no extents we are
+                * done.
+                */
+               if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &current_ext,
+                               &got)) {
+                       *done = true;
                        goto del_cursor;
                }
        }
+       XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
 
        /* Lookup the extent index at which we have to stop */
-       if (direction == SHIFT_RIGHT) {
-               gotp = xfs_iext_bno_to_ext(ifp, stop_fsb, &stop_extent);
-               /* Make stop_extent exclusive of shift range */
-               stop_extent--;
-       } else
-               stop_extent = total_extents;
-
-       /*
-        * Look up the extent index for the fsb where we start shifting. We can
-        * henceforth iterate with current_ext as extent list changes are locked
-        * out via ilock.
-        *
-        * gotp can be null in 2 cases: 1) if there are no extents or 2)
-        * *next_fsb lies in a hole beyond which there are no extents. Either
-        * way, we are done.
-        */
-       gotp = xfs_iext_bno_to_ext(ifp, *next_fsb, &current_ext);
-       if (!gotp) {
-               *done = 1;
-               goto del_cursor;
-       }
-
-       /* some sanity checking before we finally start shifting extents */
-       if ((direction == SHIFT_LEFT && current_ext >= stop_extent) ||
-            (direction == SHIFT_RIGHT && current_ext <= stop_extent)) {
+       xfs_iext_lookup_extent(ip, ifp, stop_fsb, &stop_extent, &s);
+       /* Make stop_extent exclusive of shift range */
+       stop_extent--;
+       if (current_ext <= stop_extent) {
                error = -EIO;
                goto del_cursor;
        }
 
-       while (nexts++ < num_exts) {
-               error = xfs_bmse_shift_one(ip, whichfork, offset_shift_fsb,
-                                          &current_ext, gotp, cur, &logflags,
-                                          direction, dfops);
-               if (error)
+       new_startoff = got.br_startoff + offset_shift_fsb;
+       if (current_ext < total_extents - 1) {
+               xfs_iext_get_extent(ifp, current_ext + 1, &next);
+               if (new_startoff + got.br_blockcount > next.br_startoff) {
+                       error = -EINVAL;
                        goto del_cursor;
-               /*
-                * If there was an extent merge during the shift, the extent
-                * count can change. Update the total and grade the next record.
-                */
-               if (direction == SHIFT_LEFT) {
-                       total_extents = xfs_iext_count(ifp);
-                       stop_extent = total_extents;
                }
 
-               if (current_ext == stop_extent) {
-                       *done = 1;
-                       *next_fsb = NULLFSBLOCK;
-                       break;
-               }
-               gotp = xfs_iext_get_ext(ifp, current_ext);
+               /*
+                * Unlike a left shift (which involves a hole punch), a right
+                * shift does not modify extent neighbors in any way.  We should
+                * never find mergeable extents in this scenario.  Check anyways
+                * and warn if we encounter two extents that could be one.
+                */
+               if (xfs_bmse_can_merge(&got, &next, offset_shift_fsb))
+                       WARN_ON_ONCE(1);
        }
 
-       if (!*done) {
-               xfs_bmbt_get_all(gotp, &got);
-               *next_fsb = got.br_startoff;
+       error = xfs_bmap_shift_update_extent(ip, whichfork, current_ext, &got,
+                       cur, &logflags, dfops, new_startoff);
+       if (error)
+               goto del_cursor;
+       if (--current_ext == stop_extent) {
+               *done = true;
+               goto del_cursor;
        }
+       xfs_iext_get_extent(ifp, current_ext, &got);
 
+       *next_fsb = got.br_startoff;
 del_cursor:
        if (cur)
                xfs_btree_del_cursor(cur,
                        error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
-
        if (logflags)
                xfs_trans_log_inode(tp, ip, logflags);
-
        return error;
 }
 
@@ -6219,7 +5864,6 @@ xfs_bmap_split_extent_at(
 {
        int                             whichfork = XFS_DATA_FORK;
        struct xfs_btree_cur            *cur = NULL;
-       struct xfs_bmbt_rec_host        *gotp;
        struct xfs_bmbt_irec            got;
        struct xfs_bmbt_irec            new; /* split extent */
        struct xfs_mount                *mp = ip->i_mount;
@@ -6233,7 +5877,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;
@@ -6251,21 +5895,10 @@ xfs_bmap_split_extent_at(
        }
 
        /*
-        * gotp can be null in 2 cases: 1) if there are no extents
-        * or 2) split_fsb lies in a hole beyond which there are
-        * no extents. Either way, we are done.
-        */
-       gotp = xfs_iext_bno_to_ext(ifp, split_fsb, &current_ext);
-       if (!gotp)
-               return 0;
-
-       xfs_bmbt_get_all(gotp, &got);
-
-       /*
-        * Check split_fsb lies in a hole or the start boundary offset
-        * of the extent.
+        * If there are not extents, or split_fsb lies in a hole we are done.
         */
-       if (got.br_startoff >= split_fsb)
+       if (!xfs_iext_lookup_extent(ip, ifp, split_fsb, &current_ext, &got) ||
+           got.br_startoff >= split_fsb)
                return 0;
 
        gotblkcnt = split_fsb - got.br_startoff;
@@ -6279,24 +5912,19 @@ xfs_bmap_split_extent_at(
                cur->bc_private.b.firstblock = *firstfsb;
                cur->bc_private.b.dfops = dfops;
                cur->bc_private.b.flags = 0;
-               error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
-                               got.br_startblock,
-                               got.br_blockcount,
-                               &i);
+               error = xfs_bmbt_lookup_eq(cur, &got, &i);
                if (error)
                        goto del_cursor;
                XFS_WANT_CORRUPTED_GOTO(mp, i == 1, del_cursor);
        }
 
-       xfs_bmbt_set_blockcount(gotp, gotblkcnt);
        got.br_blockcount = gotblkcnt;
+       xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
+                       current_ext, &got);
 
        logflags = XFS_ILOG_CORE;
        if (cur) {
-               error = xfs_bmbt_update(cur, got.br_startoff,
-                               got.br_startblock,
-                               got.br_blockcount,
-                               got.br_state);
+               error = xfs_bmbt_update(cur, &got);
                if (error)
                        goto del_cursor;
        } else
@@ -6309,14 +5937,10 @@ xfs_bmap_split_extent_at(
                           XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 
        if (cur) {
-               error = xfs_bmbt_lookup_eq(cur, new.br_startoff,
-                               new.br_startblock, new.br_blockcount,
-                               &i);
+               error = xfs_bmbt_lookup_eq(cur, &new, &i);
                if (error)
                        goto del_cursor;
                XFS_WANT_CORRUPTED_GOTO(mp, i == 0, del_cursor);
-               cur->bc_rec.b.br_state = new.br_state;
-
                error = xfs_btree_insert(cur, &i);
                if (error)
                        goto del_cursor;
@@ -6373,7 +5997,7 @@ xfs_bmap_split_extent(
        if (error)
                goto out;
 
-       error = xfs_defer_finish(&tp, &dfops, NULL);
+       error = xfs_defer_finish(&tp, &dfops);
        if (error)
                goto out;
 
@@ -6423,7 +6047,7 @@ __xfs_bmap_add(
        bi->bi_whichfork = whichfork;
        bi->bi_bmap = *bmap;
 
-       error = xfs_defer_join(dfops, bi->bi_owner);
+       error = xfs_defer_ijoin(dfops, bi->bi_owner);
        if (error) {
                kmem_free(bi);
                return error;
@@ -6476,46 +6100,42 @@ 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                             done;
        int                             error = 0;
 
-       bmap.br_startblock = startblock;
-       bmap.br_startoff = startoff;
-       bmap.br_blockcount = blockcount;
-       bmap.br_state = state;
+       /*
+        * firstfsb is tied to the transaction lifetime and is used to
+        * ensure correct AG locking order and schedule work item
+        * continuations.  XFS_BUI_MAX_FAST_EXTENTS (== 1) restricts us
+        * to only making one bmap call per transaction, so it should
+        * be safe to have it as a local variable here.
+        */
+       firstfsb = NULLFSBLOCK;
 
        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 (WARN_ON_ONCE(whichfork != XFS_DATA_FORK))
                return -EFSCORRUPTED;
 
        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, XFS_BMAPI_REMAP, &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, XFS_BMAPI_REMAP, 1, &firstfsb,
-                               dfops, &done);
-               ASSERT(done);
+               error = __xfs_bunmapi(tp, ip, startoff, blockcount,
+                               XFS_BMAPI_REMAP, 1, &firstfsb, dfops);
                break;
        default:
                ASSERT(0);