]> 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 be2449af4dcd47ae613d726b4011006bd9a30e61..52bf4b4c30a5d522e8fb401692fcb93c41b5efae 100644 (file)
@@ -103,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);
 }
 
@@ -151,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);
 }
 
@@ -233,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;
 
@@ -489,29 +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 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;
-
-       ifp = XFS_IFORK_PTR(ip, whichfork);
-       ASSERT(cnt == xfs_iext_count(ifp));
-       for (idx = 0; idx < cnt; idx++)
-               trace_xfs_extlist(ip, idx, whichfork, caller_ip);
-}
-
 /*
  * Validate that the bmbt_irecs being returned from bmapi are valid
  * given the caller's original parameters.  Specifically check the
@@ -555,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 */
 
 /*
@@ -646,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);
@@ -716,15 +681,9 @@ xfs_bmap_extents_to_btree(
         * Fill in the root.
         */
        block = ifp->if_broot;
-       if (xfs_sb_version_hascrc(&mp->m_sb))
-               xfs_btree_init_block_int(mp, block, XFS_BUF_DADDR_NULL,
-                                XFS_BMAP_CRC_MAGIC, 1, 1, ip->i_ino,
-                                XFS_BTREE_LONG_PTRS | XFS_BTREE_CRC_BLOCKS);
-       else
-               xfs_btree_init_block_int(mp, block, XFS_BUF_DADDR_NULL,
-                                XFS_BMAP_MAGIC, 1, 1, ip->i_ino,
+       xfs_btree_init_block_int(mp, block, XFS_BUF_DADDR_NULL,
+                                XFS_BTNUM_BMAP, 1, 1, ip->i_ino,
                                 XFS_BTREE_LONG_PTRS);
-
        /*
         * Need a cursor.  Can't allocate until bb_level is filled in.
         */
@@ -745,7 +704,6 @@ xfs_bmap_extents_to_btree(
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
        } else if (dfops->dop_low) {
-try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = *firstblock;
        } else {
@@ -761,28 +719,16 @@ try_another_ag:
                return error;
        }
 
-       /*
-        * During a CoW operation, the allocation and bmbt updates occur in
-        * different transactions.  The mapping code tries to put new bmbt
-        * blocks near extents being mapped, but the only way to guarantee this
-        * is if the alloc and the mapping happen in a single transaction that
-        * has a block reservation.  That isn't the case here, so if we run out
-        * of space we'll try again with another AG.
-        */
-       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
-           args.fsbno == NULLFSBLOCK &&
-           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
-               dfops->dop_low = true;
-               goto try_another_ag;
+       if (WARN_ON_ONCE(args.fsbno == NULLFSBLOCK)) {
+               xfs_iroot_realloc(ip, -1, whichfork);
+               xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+               return -ENOSPC;
        }
        /*
         * Allocation can't fail, the space was reserved.
         */
-       ASSERT(args.fsbno != NULLFSBLOCK);
        ASSERT(*firstblock == NULLFSBLOCK ||
-              args.agno == XFS_FSB_TO_AGNO(mp, *firstblock) ||
-              (dfops->dop_low &&
-               args.agno > XFS_FSB_TO_AGNO(mp, *firstblock)));
+              args.agno >= XFS_FSB_TO_AGNO(mp, *firstblock));
        *firstblock = cur->bc_private.b.firstblock = args.fsbno;
        cur->bc_private.b.allocated++;
        ip->i_d.di_nblocks++;
@@ -793,13 +739,8 @@ try_another_ag:
         */
        abp->b_ops = &xfs_bmbt_buf_ops;
        ablock = XFS_BUF_TO_BLOCK(abp);
-       if (xfs_sb_version_hascrc(&mp->m_sb))
-               xfs_btree_init_block_int(mp, ablock, abp->b_bn,
-                               XFS_BMAP_CRC_MAGIC, 0, 0, ip->i_ino,
-                               XFS_BTREE_LONG_PTRS | XFS_BTREE_CRC_BLOCKS);
-       else
-               xfs_btree_init_block_int(mp, ablock, abp->b_bn,
-                               XFS_BMAP_MAGIC, 0, 0, ip->i_ino,
+       xfs_btree_init_block_int(mp, ablock, abp->b_bn,
+                               XFS_BTNUM_BMAP, 0, 0, ip->i_ino,
                                XFS_BTREE_LONG_PTRS);
 
        arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
@@ -880,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.
@@ -910,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 {
@@ -923,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);
@@ -957,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,
@@ -997,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);
@@ -1129,6 +1057,10 @@ xfs_bmap_add_attrfork(
                goto trans_cancel;
        if (XFS_IFORK_Q(ip))
                goto trans_cancel;
+       if (ip->i_d.di_anextents != 0) {
+               error = -EFSCORRUPTED;
+               goto trans_cancel;
+       }
        if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) {
                /*
                 * For inodes coming from pre-6.2 filesystems.
@@ -1136,7 +1068,6 @@ xfs_bmap_add_attrfork(
                ASSERT(ip->i_d.di_aformat == 0);
                ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
        }
-       ASSERT(ip->i_d.di_anextents == 0);
 
        xfs_trans_ijoin(tp, ip, 0);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
@@ -1145,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:
@@ -1207,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);
@@ -1242,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 */
@@ -1250,12 +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);
 
-       bno = NULLFSBLOCK;
        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.
@@ -1264,9 +1189,7 @@ xfs_bmap_read_extents(
        ASSERT(level > 0);
        pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes);
        bno = be64_to_cpu(*pp);
-       ASSERT(bno != NULLFSBLOCK);
-       ASSERT(XFS_FSB_TO_AGNO(mp, bno) < mp->m_sb.sb_agcount);
-       ASSERT(XFS_FSB_TO_AGBNO(mp, bno) < mp->m_sb.sb_agblocks);
+
        /*
         * Go down the tree until leaf level is reached, following the first
         * pointer (leftmost) at each level.
@@ -1297,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)) {
@@ -1320,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;
@@ -1353,9 +1266,9 @@ xfs_bmap_read_extents(
                        return error;
                block = XFS_BUF_TO_BLOCK(bp);
        }
+       if (i != XFS_IFORK_NEXTENTS(ip, whichfork))
+               return -EFSCORRUPTED;
        ASSERT(i == xfs_iext_count(ifp));
-       ASSERT(i == XFS_IFORK_NEXTENTS(ip, whichfork));
-       XFS_BMAP_TRACE_EXLIST(ip, i, whichfork);
        return 0;
 error0:
        xfs_trans_brelse(tp, bp);
@@ -1383,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 ||
@@ -1400,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;
@@ -1489,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;
 }
@@ -1512,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;
        }
 
@@ -1575,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 */
@@ -1590,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);
@@ -1611,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 */
@@ -1620,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);
@@ -1647,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);
 
@@ -1677,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;
@@ -1697,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;
@@ -1729,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)--;
@@ -1741,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);
@@ -1755,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;
                }
@@ -1772,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;
                }
@@ -1802,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;
                }
@@ -1834,22 +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);
-               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;
@@ -1862,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;
@@ -1903,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;
@@ -1934,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:
@@ -1947,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;
@@ -1988,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;
@@ -2018,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;
@@ -2049,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;
@@ -2087,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:
@@ -2142,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:
@@ -2174,6 +2026,7 @@ STATIC int                                /* error */
 xfs_bmap_add_extent_unwritten_real(
        struct xfs_trans        *tp,
        xfs_inode_t             *ip,    /* incore inode pointer */
+       int                     whichfork,
        xfs_extnum_t            *idx,   /* extent number to update/insert */
        xfs_btree_cur_t         **curp, /* if *curp is null, not a btree */
        xfs_bmbt_irec_t         *new,   /* new data to add to file extents */
@@ -2182,23 +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 */
-       struct xfs_mount        *mp = tp->t_mountp;
+       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, XFS_DATA_FORK);
+       ifp = XFS_IFORK_PTR(ip, whichfork);
 
        ASSERT(*idx >= 0);
        ASSERT(*idx <= xfs_iext_count(ifp));
@@ -2214,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);
@@ -2239,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;
@@ -2248,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;
 
@@ -2257,9 +2104,9 @@ xfs_bmap_add_extent_unwritten_real(
         * Don't set contiguous if the combined extent would be too large.
         * Also check for all-three-contiguous being too large.
         */
-       if (*idx < xfs_iext_count(&ip->i_df) - 1) {
+       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;
        }
@@ -2267,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)) !=
@@ -2290,21 +2137,18 @@ 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);
-               ip->i_d.di_nextents -= 2;
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                               XFS_IFORK_NEXTENTS(ip, whichfork) - 2);
                if (cur == NULL)
                        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)))
@@ -2319,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;
@@ -2334,20 +2176,18 @@ 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);
-               ip->i_d.di_nextents--;
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                               XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
                if (cur == NULL)
                        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)))
@@ -2356,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;
@@ -2369,20 +2207,19 @@ 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);
-               ip->i_d.di_nextents--;
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                               XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
                if (cur == NULL)
                        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)))
@@ -2391,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;
@@ -2405,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;
@@ -2430,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;
 
@@ -2450,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;
                }
@@ -2477,31 +2298,25 @@ 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);
-               ip->i_d.di_nextents++;
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                               XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
                if (cur == NULL)
                        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)))
@@ -2515,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;
@@ -2557,35 +2366,30 @@ 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);
 
-               ip->i_d.di_nextents++;
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                               XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
                if (cur == NULL)
                        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);
@@ -2598,40 +2402,36 @@ 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);
 
-               ip->i_d.di_nextents += 2;
+               XFS_IFORK_NEXT_SET(ip, whichfork,
+                               XFS_IFORK_NEXTENTS(ip, whichfork) + 2);
                if (cur == NULL)
                        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);
@@ -2640,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);
@@ -2667,17 +2465,17 @@ xfs_bmap_add_extent_unwritten_real(
        }
 
        /* update reverse mappings */
-       error = xfs_rmap_convert_extent(mp, dfops, ip, XFS_DATA_FORK, new);
+       error = xfs_rmap_convert_extent(mp, dfops, ip, whichfork, new);
        if (error)
                goto done;
 
        /* convert to a btree if necessary */
-       if (xfs_bmap_needs_btree(ip, XFS_DATA_FORK)) {
+       if (xfs_bmap_needs_btree(ip, whichfork)) {
                int     tmp_logflags;   /* partial log flag return val */
 
                ASSERT(cur == NULL);
                error = xfs_bmap_extents_to_btree(tp, ip, first, dfops, &cur,
-                               0, &tmp_logflags, XFS_DATA_FORK);
+                               0, &tmp_logflags, whichfork);
                *logflagsp |= tmp_logflags;
                if (error)
                        goto done;
@@ -2689,7 +2487,7 @@ xfs_bmap_add_extent_unwritten_real(
                *curp = cur;
        }
 
-       xfs_bmap_check_leaf_extents(*curp, ip, XFS_DATA_FORK);
+       xfs_bmap_check_leaf_extents(*curp, ip, whichfork);
 done:
        *logflagsp |= rval;
        return error;
@@ -2713,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));
 
        /*
@@ -2727,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;
@@ -2739,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;
@@ -2776,15 +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_bmap_worst_indlen(ip, temp);
-               xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
-                       nullstartblock((int)newlen));
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                                        oldlen);
+               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;
@@ -2798,14 +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_bmap_worst_indlen(ip, temp);
-               xfs_bmbt_set_startblock(xfs_iext_get_ext(ifp, *idx),
-                       nullstartblock((int)newlen));
-               trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
+               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                                        oldlen);
+               left.br_blockcount = temp;
+               left.br_startblock = nullstartblock(newlen);
+               xfs_iext_update_extent(ip, state, *idx, &left);
                break;
 
        case BMAP_RIGHT_CONTIG:
@@ -2814,15 +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_bmap_worst_indlen(ip, temp);
-               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_);
+               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                                        oldlen);
+               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:
@@ -2850,41 +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(whichfork != XFS_COW_FORK);
+       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;
-
        /*
         * 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;
        }
@@ -2893,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;
        }
@@ -2932,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;
                }
@@ -2978,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;
                }
@@ -3010,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;
                }
@@ -3044,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);
@@ -3068,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;
 }
 
@@ -3601,7 +3366,7 @@ xfs_bmap_btalloc(
                align = xfs_get_cowextsz_hint(ap->ip);
        else if (xfs_alloc_is_userdata(ap->datatype))
                align = xfs_get_extsz_hint(ap->ip);
-       if (unlikely(align)) {
+       if (align) {
                error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
                                                align, 0, ap->eof, 0, ap->conv,
                                                &ap->offset, &ap->length);
@@ -3673,7 +3438,7 @@ xfs_bmap_btalloc(
                args.minlen = ap->minlen;
        }
        /* apply extent size hints if obtained earlier */
-       if (unlikely(align)) {
+       if (align) {
                args.prod = align;
                if ((args.mod = (xfs_extlen_t)do_mod(ap->offset, args.prod)))
                        args.mod = (xfs_extlen_t)(args.prod - args.mod);
@@ -3784,7 +3549,6 @@ xfs_bmap_btalloc(
                args.fsbno = 0;
                args.type = XFS_ALLOCTYPE_FIRST_AG;
                args.total = ap->minlen;
-               args.minleft = 0;
                if ((error = xfs_alloc_vextent(&args)))
                        return error;
                ap->dfops->dop_low = true;
@@ -3795,17 +3559,13 @@ xfs_bmap_btalloc(
                 * the first block that was allocated.
                 */
                ASSERT(*ap->firstblock == NULLFSBLOCK ||
-                      XFS_FSB_TO_AGNO(mp, *ap->firstblock) ==
-                      XFS_FSB_TO_AGNO(mp, args.fsbno) ||
-                      (ap->dfops->dop_low &&
-                       XFS_FSB_TO_AGNO(mp, *ap->firstblock) <
-                       XFS_FSB_TO_AGNO(mp, args.fsbno)));
+                      XFS_FSB_TO_AGNO(mp, *ap->firstblock) <=
+                      XFS_FSB_TO_AGNO(mp, args.fsbno));
 
                ap->blkno = args.fsbno;
                if (*ap->firstblock == NULLFSBLOCK)
                        *ap->firstblock = args.fsbno;
-               ASSERT(nullfb || fb_agno == args.agno ||
-                      (ap->dfops->dop_low && fb_agno < args.agno));
+               ASSERT(nullfb || fb_agno <= args.agno);
                ap->length = args.len;
                if (!(ap->flags & XFS_BMAPI_COWFORK))
                        ap->ip->i_d.di_nblocks += args.len;
@@ -3827,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_agblock_t           bno;
-       struct xfs_alloc_arg    args;
-       int                     error;
-
-       /*
-        * validate that the block number is legal - the enables us to detect
-        * and handle a silent filesystem corruption rather than crashing.
-        */
-       memset(&args, 0, sizeof(struct xfs_alloc_arg));
-       args.tp = ap->tp;
-       args.mp = ap->tp->t_mountp;
-       bno = *ap->firstblock;
-       args.agno = XFS_FSB_TO_AGNO(mp, bno);
-       args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
-       if (args.agno >= mp->m_sb.sb_agcount ||
-           args.agbno >= mp->m_sb.sb_agblocks)
-               return -EFSCORRUPTED;
-
-       /* "Allocate" the extent from the range we passed in. */
-       trace_xfs_bmap_remap_alloc(ap->ip, *ap->firstblock, ap->length);
-       ap->blkno = bno;
-       ap->ip->i_d.di_nblocks += ap->length;
-       xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
-
-       /* Fix the freelist, like a real allocator does. */
-       args.datatype = ap->datatype;
-       args.pag = xfs_perag_get(args.mp, args.agno);
-       ASSERT(args.pag);
-
-       /*
-        * The freelist fixing code will decline the allocation if
-        * the size and shape of the free space doesn't allow for
-        * allocating the extent and updating all the metadata that
-        * happens during an allocation.  We're remapping, not
-        * allocating, so skip that check by pretending to be freeing.
-        */
-       error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
-       xfs_perag_put(args.pag);
-       if (error)
-               trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
-       return error;
-}
-
 /*
  * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
  * It figures out where to ask the underlying allocator to put the new extent.
@@ -3889,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);
@@ -3930,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
  */
@@ -4062,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;
        }
@@ -4129,12 +3844,26 @@ xfs_bmapi_read(
        return 0;
 }
 
+/*
+ * Add a delayed allocation extent to an inode. Blocks are reserved from the
+ * global pool and the extent inserted into the inode in-core extent tree.
+ *
+ * On entry, got refers to the first extent beyond the offset of the extent to
+ * allocate or eof is specified if no such extent exists. On return, got refers
+ * to the extent record that was inserted to the inode fork.
+ *
+ * Note that the allocated extent may have been merged with contiguous extents
+ * during insertion into the inode fork. Thus, got does not reflect the current
+ * state of the inode fork on return. If necessary, the caller can use lastx to
+ * look up the updated record in the inode fork.
+ */
 int
 xfs_bmapi_reserve_delalloc(
        struct xfs_inode        *ip,
        int                     whichfork,
-       xfs_fileoff_t           aoff,
+       xfs_fileoff_t           off,
        xfs_filblks_t           len,
+       xfs_filblks_t           prealloc,
        struct xfs_bmbt_irec    *got,
        xfs_extnum_t            *lastx,
        int                     eof)
@@ -4146,10 +3875,17 @@ xfs_bmapi_reserve_delalloc(
        char                    rt = XFS_IS_REALTIME_INODE(ip);
        xfs_extlen_t            extsz;
        int                     error;
+       xfs_fileoff_t           aoff = off;
 
-       alen = XFS_FILBLKS_MIN(len, MAXEXTLEN);
+       /*
+        * Cap the alloc length. Keep track of prealloc so we know whether to
+        * tag the inode before we return.
+        */
+       alen = XFS_FILBLKS_MIN(len + prealloc, MAXEXTLEN);
        if (!eof)
                alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
+       if (prealloc && alen >= len)
+               prealloc = alen - len;
 
        /* Figure out the extent size, adjust alen */
        if (whichfork == XFS_COW_FORK)
@@ -4207,18 +3943,19 @@ xfs_bmapi_reserve_delalloc(
        got->br_startblock = nullstartblock(indlen);
        got->br_blockcount = alen;
        got->br_state = XFS_EXT_NORM;
+
        xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
 
        /*
-        * Update our extent pointer, given that xfs_bmap_add_extent_hole_delay
-        * might have merged it into one of the neighbouring ones.
+        * Tag the inode if blocks were preallocated. Note that COW fork
+        * preallocation can occur at the start or end of the extent, even when
+        * prealloc == 0, so we must also check the aligned offset and length.
         */
-       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *lastx), got);
+       if (whichfork == XFS_DATA_FORK && prealloc)
+               xfs_inode_set_eofblocks_tag(ip);
+       if (whichfork == XFS_COW_FORK && (prealloc || aoff < off || alen > len))
+               xfs_inode_set_cowblocks_tag(ip);
 
-       ASSERT(got->br_startoff <= aoff);
-       ASSERT(got->br_startoff + got->br_blockcount >= aoff + alen);
-       ASSERT(isnullstartblock(got->br_startblock));
-       ASSERT(got->br_state == XFS_EXT_NORM);
        return 0;
 
 out_unreserve_blocks:
@@ -4252,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)
@@ -4298,8 +4033,6 @@ xfs_bmapi_allocate(
        if (error)
                return error;
 
-       if (bma->dfops->dop_low)
-               bma->minleft = 0;
        if (bma->cur)
                bma->cur->bc_private.b.firstblock = *bma->firstblock;
        if (bma->blkno == NULLFSBLOCK)
@@ -4325,17 +4058,25 @@ xfs_bmapi_allocate(
        bma->got.br_state = XFS_EXT_NORM;
 
        /*
-        * A wasdelay extent has been initialized, so shouldn't be flagged
-        * as unwritten.
+        * In the data fork, a wasdelay extent has been initialized, so
+        * shouldn't be flagged as unwritten.
+        *
+        * For the cow fork, however, we convert delalloc reservations
+        * (extents allocated for speculative preallocation) to
+        * allocated unwritten extents, and only convert the unwritten
+        * extents to real extents when we're about to write the data.
         */
-       if (!bma->wasdel && (bma->flags & XFS_BMAPI_PREALLOC) &&
+       if ((!bma->wasdel || (bma->flags & XFS_BMAPI_COWFORK)) &&
+           (bma->flags & XFS_BMAPI_PREALLOC) &&
            xfs_sb_version_hasextflgbit(&mp->m_sb))
                bma->got.br_state = XFS_EXT_UNWRITTEN;
 
        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)
@@ -4346,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 >=
@@ -4379,8 +4120,6 @@ xfs_bmapi_convert_unwritten(
                        (XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT))
                return 0;
 
-       ASSERT(whichfork != XFS_COW_FORK);
-
        /*
         * Modify (by adding) the state flag, if writing.
         */
@@ -4405,8 +4144,8 @@ xfs_bmapi_convert_unwritten(
                        return error;
        }
 
-       error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, &bma->idx,
-                       &bma->cur, mval, bma->firstblock, bma->dfops,
+       error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, whichfork,
+                       &bma->idx, &bma->cur, mval, bma->firstblock, bma->dfops,
                        &tmp_logflags);
        /*
         * Log the inode core unconditionally in the unwritten extent conversion
@@ -4415,8 +4154,12 @@ xfs_bmapi_convert_unwritten(
         * in the transaction for the sake of fsync(), even if nothing has
         * changed, because fsync() will not force the log for this transaction
         * unless it sees the inode pinned.
+        *
+        * Note: If we're only converting cow fork extents, there aren't
+        * any on-disk updates to make, so we don't need to log anything.
         */
-       bma->logflags |= tmp_logflags | XFS_ILOG_CORE;
+       if (whichfork != XFS_COW_FORK)
+               bma->logflags |= tmp_logflags | XFS_ILOG_CORE;
        if (error)
                return error;
 
@@ -4425,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,
@@ -4471,8 +4214,6 @@ xfs_bmapi_write(
        int                     n;              /* current extent index */
        xfs_fileoff_t           obno;           /* old block number (offset) */
        int                     whichfork;      /* data or attr fork */
-       char                    inhole;         /* current location is hole in file */
-       char                    wasdelay;       /* old extent was delayed */
 
 #ifdef DEBUG
        xfs_fileoff_t           orig_bno;       /* original block number value */
@@ -4492,15 +4233,13 @@ xfs_bmapi_write(
        ASSERT(*nmap >= 1);
        ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
        ASSERT(!(flags & XFS_BMAPI_IGSTATE));
-       ASSERT(tp != NULL);
+       ASSERT(tp != NULL ||
+              (flags & (XFS_BMAPI_CONVERT | XFS_BMAPI_COWFORK)) ==
+                       (XFS_BMAPI_CONVERT | XFS_BMAPI_COWFORK));
        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_PREALLOC) || whichfork != XFS_COW_FORK);
-       ASSERT(!(flags & XFS_BMAPI_CONVERT) || whichfork != XFS_COW_FORK);
+       ASSERT(!(flags & XFS_BMAPI_REMAP));
 
        /* zeroing is for currently only for data extents, not metadata */
        ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
@@ -4517,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;
        }
@@ -4560,22 +4299,39 @@ xfs_bmapi_write(
        bma.firstblock = firstblock;
 
        while (bno < end && n < *nmap) {
-               inhole = eof || bma.got.br_startoff > bno;
-               wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
+               bool                    need_alloc = false, wasdelay = false;
 
-               /*
-                * Make sure we only reflink into a hole.
-                */
-               if (flags & XFS_BMAPI_REMAP)
-                       ASSERT(inhole);
-               if (flags & XFS_BMAPI_COWFORK)
-                       ASSERT(!inhole);
+               /* in hole or beyoned EOF? */
+               if (eof || bma.got.br_startoff > bno) {
+                       if (flags & XFS_BMAPI_DELALLOC) {
+                               /*
+                                * For the COW fork we can reasonably get a
+                                * request for converting an extent that races
+                                * with other threads already having converted
+                                * part of it, as there converting COW to
+                                * regular blocks is not protected using the
+                                * IOLOCK.
+                                */
+                               ASSERT(flags & XFS_BMAPI_COWFORK);
+                               if (!(flags & XFS_BMAPI_COWFORK)) {
+                                       error = -EIO;
+                                       goto error0;
+                               }
+
+                               if (eof || bno >= end)
+                                       break;
+                       } else {
+                               need_alloc = true;
+                       }
+               } else if (isnullstartblock(bma.got.br_startblock)) {
+                       wasdelay = true;
+               }
 
                /*
                 * First, deal with the hole before the allocated space
                 * that we found, if any.
                 */
-               if (inhole || wasdelay) {
+               if (need_alloc || wasdelay) {
                        bma.eof = eof;
                        bma.conv = !!(flags & XFS_BMAPI_CONVERT);
                        bma.wasdel = wasdelay;
@@ -4683,13 +4439,9 @@ error0:
        if (bma.cur) {
                if (!error) {
                        ASSERT(*firstblock == NULLFSBLOCK ||
-                              XFS_FSB_TO_AGNO(mp, *firstblock) ==
+                              XFS_FSB_TO_AGNO(mp, *firstblock) <=
                               XFS_FSB_TO_AGNO(mp,
-                                      bma.cur->bc_private.b.firstblock) ||
-                              (dfops->dop_low &&
-                               XFS_FSB_TO_AGNO(mp, *firstblock) <
-                               XFS_FSB_TO_AGNO(mp,
-                                       bma.cur->bc_private.b.firstblock)));
+                                      bma.cur->bc_private.b.firstblock));
                        *firstblock = bma.cur->bc_private.b.firstblock;
                }
                xfs_btree_del_cursor(bma.cur,
@@ -4701,57 +4453,169 @@ error0:
        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
- * behind.
- *
- * Given the original reservation and the worst case indlen for the two new
- * extents (as calculated by xfs_bmap_worst_indlen()), split the original
- * reservation fairly across the two new extents. If necessary, steal available
- * blocks from a deleted extent to make up a reservation deficiency (e.g., if
- * ores == 1). The number of stolen blocks is returned. The availability and
- * subsequent accounting of stolen blocks is the responsibility of the caller.
- */
-static xfs_filblks_t
-xfs_bmap_split_indlen(
-       xfs_filblks_t                   ores,           /* original res. */
-       xfs_filblks_t                   *indlen1,       /* ext1 worst indlen */
-       xfs_filblks_t                   *indlen2,       /* ext2 worst indlen */
-       xfs_filblks_t                   avail)          /* stealable blocks */
-{
+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
+ * behind.
+ *
+ * Given the original reservation and the worst case indlen for the two new
+ * extents (as calculated by xfs_bmap_worst_indlen()), split the original
+ * reservation fairly across the two new extents. If necessary, steal available
+ * blocks from a deleted extent to make up a reservation deficiency (e.g., if
+ * ores == 1). The number of stolen blocks is returned. The availability and
+ * subsequent accounting of stolen blocks is the responsibility of the caller.
+ */
+static xfs_filblks_t
+xfs_bmap_split_indlen(
+       xfs_filblks_t                   ores,           /* original res. */
+       xfs_filblks_t                   *indlen1,       /* ext1 worst indlen */
+       xfs_filblks_t                   *indlen2,       /* ext2 worst indlen */
+       xfs_filblks_t                   avail)          /* stealable blocks */
+{
        xfs_filblks_t                   len1 = *indlen1;
        xfs_filblks_t                   len2 = *indlen2;
        xfs_filblks_t                   nres = len1 + len2; /* new total res. */
        xfs_filblks_t                   stolen = 0;
+       xfs_filblks_t                   resfactor;
 
        /*
         * Steal as many blocks as we can to try and satisfy the worst case
         * indlen for both new extents.
         */
-       while (nres > ores && avail) {
-               nres--;
-               avail--;
-               stolen++;
-       }
+       if (ores < nres && avail)
+               stolen = XFS_FILBLKS_MIN(nres - ores, avail);
+       ores += stolen;
+
+        /* nothing else to do if we've satisfied the new reservation */
+       if (ores >= nres)
+               return stolen;
+
+       /*
+        * We can't meet the total required reservation for the two extents.
+        * Calculate the percent of the overall shortage between both extents
+        * and apply this percentage to each of the requested indlen values.
+        * This distributes the shortage fairly and reduces the chances that one
+        * of the two extents is left with nothing when extents are repeatedly
+        * split.
+        */
+       resfactor = (ores * 100);
+       do_div(resfactor, nres);
+       len1 *= resfactor;
+       do_div(len1, 100);
+       len2 *= resfactor;
+       do_div(len2, 100);
+       ASSERT(len1 + len2 <= ores);
+       ASSERT(len1 < *indlen1 && len2 < *indlen2);
 
        /*
-        * The only blocks available are those reserved for the original
-        * extent and what we can steal from the extent being removed.
-        * If this still isn't enough to satisfy the combined
-        * requirements for the two new extents, skim blocks off of each
-        * of the new reservations until they match what is available.
+        * Hand out the remainder to each extent. If one of the two reservations
+        * is zero, we want to make sure that one gets a block first. The loop
+        * below starts with len1, so hand len2 a block right off the bat if it
+        * is zero.
         */
-       while (nres > ores) {
-               if (len1) {
-                       len1--;
-                       nres--;
+       ores -= (len1 + len2);
+       ASSERT((*indlen1 - len1) + (*indlen2 - len2) >= ores);
+       if (ores && !len2 && *indlen2) {
+               len2++;
+               ores--;
+       }
+       while (ores) {
+               if (len1 < *indlen1) {
+                       len1++;
+                       ores--;
                }
-               if (nres == ores)
+               if (!ores)
                        break;
-               if (len2) {
-                       len2--;
-                       nres--;
+               if (len2 < *indlen2) {
+                       len2++;
+                       ores--;
                }
        }
 
@@ -4775,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);
@@ -4793,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);
@@ -4811,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:
                /*
@@ -4862,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);
 
@@ -4875,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;
@@ -4925,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;
@@ -4979,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 */
@@ -4993,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 */
@@ -5011,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);
@@ -5119,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.
@@ -5306,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;
@@ -5328,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 */
@@ -5347,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);
@@ -5374,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;
@@ -5383,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;
@@ -5417,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);
@@ -5432,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
@@ -5442,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))) {
@@ -5466,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;
                        }
@@ -5493,8 +5280,8 @@ __xfs_bunmapi(
                        }
                        del.br_state = XFS_EXT_UNWRITTEN;
                        error = xfs_bmap_add_extent_unwritten_real(tp, ip,
-                                       &lastx, &cur, &del, firstblock, dfops,
-                                       &logflags);
+                                       whichfork, &lastx, &cur, &del,
+                                       firstblock, dfops, &logflags);
                        if (error)
                                goto error0;
                        goto nodelete;
@@ -5518,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) {
@@ -5547,8 +5334,9 @@ __xfs_bunmapi(
                                prev.br_state = XFS_EXT_UNWRITTEN;
                                lastx--;
                                error = xfs_bmap_add_extent_unwritten_real(tp,
-                                               ip, &lastx, &cur, &prev,
-                                               firstblock, dfops, &logflags);
+                                               ip, whichfork, &lastx, &cur,
+                                               &prev, firstblock, dfops,
+                                               &logflags);
                                if (error)
                                        goto error0;
                                goto nodelete;
@@ -5556,92 +5344,47 @@ __xfs_bunmapi(
                                ASSERT(del.br_state == XFS_EXT_NORM);
                                del.br_state = XFS_EXT_UNWRITTEN;
                                error = xfs_bmap_add_extent_unwritten_real(tp,
-                                               ip, &lastx, &cur, &del,
-                                               firstblock, dfops, &logflags);
+                                               ip, whichfork, &lastx, &cur,
+                                               &del, firstblock, dfops,
+                                               &logflags);
                                if (error)
                                        goto error0;
                                goto nodelete;
                        }
                }
 
-               /*
-                * 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.
@@ -5760,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
@@ -5796,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);
@@ -5812,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);
+}
+
+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));
 
-               /* check whether to merge the extent or shift it down */
-               if (xfs_bmse_can_merge(&adj_irec, &got,
-                                      offset_shift_fsb)) {
+       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;
@@ -6030,7 +5769,7 @@ xfs_bmap_shift_extents(
         */
        total_extents = xfs_iext_count(ifp);
        if (total_extents == 0) {
-               *done = 1;
+               *done = true;
                goto del_cursor;
        }
 
@@ -6038,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;
 }
 
@@ -6132,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;
@@ -6146,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;
@@ -6164,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.
+        * If there are not extents, or split_fsb lies in a hole 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 (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;
@@ -6192,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
@@ -6222,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;
@@ -6286,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;
 
@@ -6336,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;
@@ -6389,49 +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                             flags = XFS_BMAPI_REMAP;
-       int                             done;
        int                             error = 0;
 
-       bmap.br_startblock = startblock;
-       bmap.br_startoff = startoff;
-       bmap.br_blockcount = blockcount;
-       bmap.br_state = state;
+       /*
+        * 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 (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
+       if (WARN_ON_ONCE(whichfork != XFS_DATA_FORK))
                return -EFSCORRUPTED;
-       if (whichfork == XFS_ATTR_FORK)
-               flags |= XFS_BMAPI_ATTRFORK;
 
        if (XFS_TEST_ERROR(false, tp->t_mountp,
-                       XFS_ERRTAG_BMAP_FINISH_ONE,
-                       XFS_RANDOM_BMAP_FINISH_ONE))
+                       XFS_ERRTAG_BMAP_FINISH_ONE))
                return -EIO;
 
        switch (type) {
        case XFS_BMAP_MAP:
-               firstfsb = bmap.br_startblock;
-               error = xfs_bmapi_write(tp, ip, bmap.br_startoff,
-                                       bmap.br_blockcount, flags, &firstfsb,
-                                       bmap.br_blockcount, &bmap, &nimaps,
-                                       dfops);
+               error = xfs_bmapi_remap(tp, ip, startoff, *blockcount,
+                               startblock, dfops);
+               *blockcount = 0;
                break;
        case XFS_BMAP_UNMAP:
-               error = xfs_bunmapi(tp, ip, bmap.br_startoff,
-                               bmap.br_blockcount, flags, 1, &firstfsb,
-                               dfops, &done);
-               ASSERT(done);
+               error = __xfs_bunmapi(tp, ip, startoff, blockcount,
+                               XFS_BMAPI_REMAP, 1, &firstfsb, dfops);
                break;
        default:
                ASSERT(0);