]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/xfs/xfs_bmap.c
xfs: introduce xfs_bmapi_delay()
[mv-sheeva.git] / fs / xfs / xfs_bmap.c
index 452a291383abaf72c16b138769fe5a2eba2b1f7c..6f32769745553ca0b11ec9fd5db73dce77f1a3f6 100644 (file)
@@ -465,27 +465,10 @@ xfs_bmap_add_extent(
        ASSERT(*idx >= 0);
        ASSERT(*idx <= nextents);
 
-       /*
-        * This is the first extent added to a new/empty file.
-        * Special case this one, so other routines get to assume there are
-        * already extents in the list.
-        */
-       if (nextents == 0) {
-               xfs_iext_insert(ip, *idx, 1, new,
-                               whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
-
-               ASSERT(cur == NULL);
-
-               if (!isnullstartblock(new->br_startblock)) {
-                       XFS_IFORK_NEXT_SET(ip, whichfork, 1);
-                       logflags = XFS_ILOG_CORE | xfs_ilog_fext(whichfork);
-               } else
-                       logflags = 0;
-       }
        /*
         * Any kind of new delayed allocation goes here.
         */
-       else if (isnullstartblock(new->br_startblock)) {
+       if (isnullstartblock(new->br_startblock)) {
                if (cur)
                        ASSERT((cur->bc_private.b.flags &
                                XFS_BTCUR_BPRV_WASDEL) == 0);
@@ -1062,34 +1045,15 @@ xfs_bmap_add_extent_delay_real(
                temp2 = xfs_bmap_worst_indlen(ip, temp2);
                diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) -
                        (cur ? cur->bc_private.b.allocated : 0));
-               if (diff > 0 &&
-                   xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS,
-                                            -((int64_t)diff), 0)) {
-                       /*
-                        * Ick gross gag me with a spoon.
-                        */
-                       ASSERT(0);      /* want to see if this ever happens! */
-                       while (diff > 0) {
-                               if (temp) {
-                                       temp--;
-                                       diff--;
-                                       if (!diff ||
-                                           !xfs_icsb_modify_counters(ip->i_mount,
-                                                   XFS_SBS_FDBLOCKS,
-                                                   -((int64_t)diff), 0))
-                                               break;
-                               }
-                               if (temp2) {
-                                       temp2--;
-                                       diff--;
-                                       if (!diff ||
-                                           !xfs_icsb_modify_counters(ip->i_mount,
-                                                   XFS_SBS_FDBLOCKS,
-                                                   -((int64_t)diff), 0))
-                                               break;
-                               }
-                       }
+               if (diff > 0) {
+                       error = xfs_icsb_modify_counters(ip->i_mount,
+                                       XFS_SBS_FDBLOCKS,
+                                       -((int64_t)diff), 0);
+                       ASSERT(!error);
+                       if (error)
+                               goto done;
                }
+
                ep = xfs_iext_get_ext(ifp, *idx);
                xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
                trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_);
@@ -3782,19 +3746,11 @@ xfs_bmap_compute_maxlevels(
  * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi
  * caller.  Frees all the extents that need freeing, which must be done
  * last due to locking considerations.  We never free any extents in
- * the first transaction.  This is to allow the caller to make the first
- * transaction a synchronous one so that the pointers to the data being
- * broken in this transaction will be permanent before the data is actually
- * freed.  This is necessary to prevent blocks from being reallocated
- * and written to before the free and reallocation are actually permanent.
- * We do not just make the first transaction synchronous here, because
- * there are more efficient ways to gain the same protection in some cases
- * (see the file truncation code).
+ * the first transaction.
  *
  * Return 1 if the given transaction was committed and a new one
  * started, and 0 otherwise in the committed parameter.
  */
-/*ARGSUSED*/
 int                                            /* error */
 xfs_bmap_finish(
        xfs_trans_t             **tp,           /* transaction pointer addr */
@@ -4282,7 +4238,7 @@ xfs_bmap_validate_ret(
                ASSERT(i == 0 ||
                       mval[i - 1].br_startoff + mval[i - 1].br_blockcount ==
                       mval[i].br_startoff);
-               if ((flags & XFS_BMAPI_WRITE) && !(flags & XFS_BMAPI_DELAY))
+               if (flags & XFS_BMAPI_WRITE)
                        ASSERT(mval[i].br_startblock != DELAYSTARTBLOCK &&
                               mval[i].br_startblock != HOLESTARTBLOCK);
                ASSERT(mval[i].br_state == XFS_EXT_NORM ||
@@ -4292,6 +4248,397 @@ xfs_bmap_validate_ret(
 #endif /* DEBUG */
 
 
+/*
+ * Trim the returned map to the required bounds
+ */
+STATIC void
+xfs_bmapi_trim_map(
+       struct xfs_bmbt_irec    *mval,
+       struct xfs_bmbt_irec    *got,
+       xfs_fileoff_t           *bno,
+       xfs_filblks_t           len,
+       xfs_fileoff_t           obno,
+       xfs_fileoff_t           end,
+       int                     n,
+       int                     flags)
+{
+       if ((flags & XFS_BMAPI_ENTIRE) ||
+           got->br_startoff + got->br_blockcount <= obno) {
+               *mval = *got;
+               if (isnullstartblock(got->br_startblock))
+                       mval->br_startblock = DELAYSTARTBLOCK;
+               return;
+       }
+
+       if (obno > *bno)
+               *bno = obno;
+       ASSERT((*bno >= obno) || (n == 0));
+       ASSERT(*bno < end);
+       mval->br_startoff = *bno;
+       if (isnullstartblock(got->br_startblock))
+               mval->br_startblock = DELAYSTARTBLOCK;
+       else
+               mval->br_startblock = got->br_startblock +
+                                       (*bno - got->br_startoff);
+       /*
+        * Return the minimum of what we got and what we asked for for
+        * the length.  We can use the len variable here because it is
+        * modified below and we could have been there before coming
+        * here if the first part of the allocation didn't overlap what
+        * was asked for.
+        */
+       mval->br_blockcount = XFS_FILBLKS_MIN(end - *bno,
+                       got->br_blockcount - (*bno - got->br_startoff));
+       mval->br_state = got->br_state;
+       ASSERT(mval->br_blockcount <= len);
+       return;
+}
+
+/*
+ * Update and validate the extent map to return
+ */
+STATIC void
+xfs_bmapi_update_map(
+       struct xfs_bmbt_irec    **map,
+       xfs_fileoff_t           *bno,
+       xfs_filblks_t           *len,
+       xfs_fileoff_t           obno,
+       xfs_fileoff_t           end,
+       int                     *n,
+       int                     flags)
+{
+       xfs_bmbt_irec_t *mval = *map;
+
+       ASSERT((flags & XFS_BMAPI_ENTIRE) ||
+              ((mval->br_startoff + mval->br_blockcount) <= end));
+       ASSERT((flags & XFS_BMAPI_ENTIRE) || (mval->br_blockcount <= *len) ||
+              (mval->br_startoff < obno));
+
+       *bno = mval->br_startoff + mval->br_blockcount;
+       *len = end - *bno;
+       if (*n > 0 && mval->br_startoff == mval[-1].br_startoff) {
+               /* update previous map with new information */
+               ASSERT(mval->br_startblock == mval[-1].br_startblock);
+               ASSERT(mval->br_blockcount > mval[-1].br_blockcount);
+               ASSERT(mval->br_state == mval[-1].br_state);
+               mval[-1].br_blockcount = mval->br_blockcount;
+               mval[-1].br_state = mval->br_state;
+       } else if (*n > 0 && mval->br_startblock != DELAYSTARTBLOCK &&
+                  mval[-1].br_startblock != DELAYSTARTBLOCK &&
+                  mval[-1].br_startblock != HOLESTARTBLOCK &&
+                  mval->br_startblock == mval[-1].br_startblock +
+                                         mval[-1].br_blockcount &&
+                  ((flags & XFS_BMAPI_IGSTATE) ||
+                       mval[-1].br_state == mval->br_state)) {
+               ASSERT(mval->br_startoff ==
+                      mval[-1].br_startoff + mval[-1].br_blockcount);
+               mval[-1].br_blockcount += mval->br_blockcount;
+       } else if (*n > 0 &&
+                  mval->br_startblock == DELAYSTARTBLOCK &&
+                  mval[-1].br_startblock == DELAYSTARTBLOCK &&
+                  mval->br_startoff ==
+                  mval[-1].br_startoff + mval[-1].br_blockcount) {
+               mval[-1].br_blockcount += mval->br_blockcount;
+               mval[-1].br_state = mval->br_state;
+       } else if (!((*n == 0) &&
+                    ((mval->br_startoff + mval->br_blockcount) <=
+                     obno))) {
+               mval++;
+               (*n)++;
+       }
+       *map = mval;
+}
+
+/*
+ * Map file blocks to filesystem blocks without allocation.
+ */
+int
+xfs_bmapi_read(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           bno,
+       xfs_filblks_t           len,
+       struct xfs_bmbt_irec    *mval,
+       int                     *nmap,
+       int                     flags)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp;
+       struct xfs_bmbt_irec    got;
+       struct xfs_bmbt_irec    prev;
+       xfs_fileoff_t           obno;
+       xfs_fileoff_t           end;
+       xfs_extnum_t            lastx;
+       int                     error;
+       int                     eof;
+       int                     n = 0;
+       int                     whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
+                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+
+       ASSERT(*nmap >= 1);
+       ASSERT(!(flags & ~(XFS_BMAPI_ATTRFORK|XFS_BMAPI_ENTIRE|
+                          XFS_BMAPI_IGSTATE)));
+
+       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_bmapi_read", XFS_ERRLEVEL_LOW, mp);
+               return XFS_ERROR(EFSCORRUPTED);
+       }
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return XFS_ERROR(EIO);
+
+       XFS_STATS_INC(xs_blk_mapr);
+
+       ifp = XFS_IFORK_PTR(ip, whichfork);
+       ASSERT(ifp->if_ext_max ==
+              XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+
+       if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+               error = xfs_iread_extents(NULL, ip, whichfork);
+               if (error)
+                       return error;
+       }
+
+       xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, &prev);
+       end = bno + len;
+       obno = bno;
+
+       while (bno < end && n < *nmap) {
+               /* Reading past eof, act as though there's a hole up to end. */
+               if (eof)
+                       got.br_startoff = end;
+               if (got.br_startoff > bno) {
+                       /* Reading in a hole.  */
+                       mval->br_startoff = bno;
+                       mval->br_startblock = HOLESTARTBLOCK;
+                       mval->br_blockcount =
+                               XFS_FILBLKS_MIN(len, got.br_startoff - bno);
+                       mval->br_state = XFS_EXT_NORM;
+                       bno += mval->br_blockcount;
+                       len -= mval->br_blockcount;
+                       mval++;
+                       n++;
+                       continue;
+               }
+
+               /* set up the extent map to return. */
+               xfs_bmapi_trim_map(mval, &got, &bno, len, obno, end, n, flags);
+               xfs_bmapi_update_map(&mval, &bno, &len, obno, end, &n, flags);
+
+               /* If we're done, stop now. */
+               if (bno >= end || n >= *nmap)
+                       break;
+
+               /* Else go on to the next record. */
+               if (++lastx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+                       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, lastx), &got);
+               else
+                       eof = 1;
+       }
+       *nmap = n;
+       return 0;
+}
+
+STATIC int
+xfs_bmapi_reserve_delalloc(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           aoff,
+       xfs_filblks_t           len,
+       struct xfs_bmbt_irec    *got,
+       struct xfs_bmbt_irec    *prev,
+       xfs_extnum_t            *lastx,
+       int                     eof)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       xfs_extlen_t            alen;
+       xfs_extlen_t            indlen;
+       xfs_fsblock_t           firstblock = NULLFSBLOCK;
+       struct xfs_btree_cur    *cur = NULL;
+       int                     tmp_logflags = 0;
+       char                    rt = XFS_IS_REALTIME_INODE(ip);
+       xfs_extlen_t            extsz;
+       int                     error;
+
+       alen = XFS_FILBLKS_MIN(len, MAXEXTLEN);
+       if (!eof)
+               alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
+
+       /* Figure out the extent size, adjust alen */
+       extsz = xfs_get_extsz_hint(ip);
+       if (extsz) {
+               /*
+                * Make sure we don't exceed a single extent length when we
+                * align the extent by reducing length we are going to
+                * allocate by the maximum amount extent size aligment may
+                * require.
+                */
+               alen = XFS_FILBLKS_MIN(len, MAXEXTLEN - (2 * extsz - 1));
+               error = xfs_bmap_extsize_align(mp, got, prev, extsz, rt, eof,
+                                              1, 0, &aoff, &alen);
+               ASSERT(!error);
+       }
+
+       if (rt)
+               extsz = alen / mp->m_sb.sb_rextsize;
+
+       /*
+        * Make a transaction-less quota reservation for delayed allocation
+        * blocks.  This number gets adjusted later.  We return if we haven't
+        * allocated blocks already inside this loop.
+        */
+       error = xfs_trans_reserve_quota_nblks(NULL, ip, (long)alen, 0,
+                       rt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+       if (error)
+               return error;
+
+       /*
+        * Split changing sb for alen and indlen since they could be coming
+        * from different places.
+        */
+       indlen = (xfs_extlen_t)xfs_bmap_worst_indlen(ip, alen);
+       ASSERT(indlen > 0);
+
+       if (rt) {
+               error = xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS,
+                                         -((int64_t)extsz), 0);
+       } else {
+               error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
+                                                -((int64_t)alen), 0);
+       }
+
+       if (error)
+               goto out_unreserve_quota;
+
+       error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
+                                        -((int64_t)indlen), 0);
+       if (error)
+               goto out_unreserve_blocks;
+
+
+       ip->i_delayed_blks += alen;
+
+       got->br_startoff = aoff;
+       got->br_startblock = nullstartblock(indlen);
+       got->br_blockcount = alen;
+       got->br_state = XFS_EXT_NORM;
+
+       error = xfs_bmap_add_extent(NULL, ip, lastx, &cur, got, &firstblock,
+                                   NULL, &tmp_logflags, XFS_DATA_FORK);
+       ASSERT(!error);
+       ASSERT(!tmp_logflags);
+       ASSERT(!cur);
+
+       /*
+        * Update our extent pointer, given that xfs_bmap_add_extent might
+        * have merged it into one of the neighbouring ones.
+        */
+       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *lastx), got);
+
+       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:
+       if (rt)
+               xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS, extsz, 0);
+       else
+               xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, alen, 0);
+out_unreserve_quota:
+       if (XFS_IS_QUOTA_ON(mp))
+               xfs_trans_unreserve_quota_nblks(NULL, ip, alen, 0, rt ?
+                               XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+       return error;
+}
+
+/*
+ * Map file blocks to filesystem blocks, adding delayed allocations as needed.
+ */
+int
+xfs_bmapi_delay(
+       struct xfs_inode        *ip,    /* incore inode */
+       xfs_fileoff_t           bno,    /* starting file offs. mapped */
+       xfs_filblks_t           len,    /* length to map in file */
+       struct xfs_bmbt_irec    *mval,  /* output: map values */
+       int                     *nmap,  /* i/o: mval size/count */
+       int                     flags)  /* XFS_BMAPI_... */
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       struct xfs_bmbt_irec    got;    /* current file extent record */
+       struct xfs_bmbt_irec    prev;   /* previous file extent record */
+       xfs_fileoff_t           obno;   /* old block number (offset) */
+       xfs_fileoff_t           end;    /* end of mapped file region */
+       xfs_extnum_t            lastx;  /* last useful extent number */
+       int                     eof;    /* we've hit the end of extents */
+       int                     n = 0;  /* current extent index */
+       int                     error = 0;
+
+       ASSERT(*nmap >= 1);
+       ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
+       ASSERT(!(flags & ~XFS_BMAPI_ENTIRE));
+
+       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_RANDOM_BMAPIFORMAT))) {
+               XFS_ERROR_REPORT("xfs_bmapi_delay", XFS_ERRLEVEL_LOW, mp);
+               return XFS_ERROR(EFSCORRUPTED);
+       }
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return XFS_ERROR(EIO);
+
+       XFS_STATS_INC(xs_blk_mapw);
+
+       if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+               error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+               if (error)
+                       return error;
+       }
+
+       xfs_bmap_search_extents(ip, bno, XFS_DATA_FORK, &eof, &lastx, &got, &prev);
+       end = bno + len;
+       obno = bno;
+
+       while (bno < end && n < *nmap) {
+               if (eof || got.br_startoff > bno) {
+                       error = xfs_bmapi_reserve_delalloc(ip, bno, len, &got,
+                                                          &prev, &lastx, eof);
+                       if (error) {
+                               if (n == 0) {
+                                       *nmap = 0;
+                                       return error;
+                               }
+                               break;
+                       }
+               }
+
+               /* set up the extent map to return. */
+               xfs_bmapi_trim_map(mval, &got, &bno, len, obno, end, n, flags);
+               xfs_bmapi_update_map(&mval, &bno, &len, obno, end, &n, flags);
+
+               /* If we're done, stop now. */
+               if (bno >= end || n >= *nmap)
+                       break;
+
+               /* Else go on to the next record. */
+               prev = got;
+               if (++lastx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+                       xfs_bmbt_get_all(xfs_iext_get_ext(ifp, lastx), &got);
+               else
+                       eof = 1;
+       }
+
+       *nmap = n;
+       return 0;
+}
+
+
 /*
  * Map file blocks to filesystem blocks.
  * File range is given by the bno/len pair.
@@ -4330,7 +4677,6 @@ xfs_bmapi(
        int             error;          /* error return */
        xfs_bmbt_irec_t got;            /* current file extent record */
        xfs_ifork_t     *ifp;           /* inode fork pointer */
-       xfs_extlen_t    indlen;         /* indirect blocks length */
        xfs_extnum_t    lastx;          /* last useful extent number */
        int             logflags;       /* flags for transaction logging */
        xfs_extlen_t    minleft;        /* min blocks left after allocation */
@@ -4338,7 +4684,6 @@ xfs_bmapi(
        xfs_mount_t     *mp;            /* xfs mount structure */
        int             n;              /* current extent index */
        int             nallocs;        /* number of extents alloc'd */
-       xfs_extnum_t    nextents;       /* number of extents in file */
        xfs_fileoff_t   obno;           /* old block number (offset) */
        xfs_bmbt_irec_t prev;           /* previous file extent record */
        int             tmp_logflags;   /* temp flags holder */
@@ -4402,7 +4747,6 @@ xfs_bmapi(
         */
        if ((flags & XFS_BMAPI_IGSTATE) && wr)  /* if writing unwritten space */
                wr = 0;                         /* no allocations are allowed */
-       ASSERT(wr || !(flags & XFS_BMAPI_DELAY));
        logflags = 0;
        nallocs = 0;
        cur = NULL;
@@ -4424,7 +4768,6 @@ xfs_bmapi(
                goto error0;
        ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
                &prev);
-       nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
        n = 0;
        end = bno + len;
        obno = bno;
@@ -4438,8 +4781,7 @@ xfs_bmapi(
                if (eof && !wr)
                        got.br_startoff = end;
                inhole = eof || got.br_startoff > bno;
-               wasdelay = wr && !inhole && !(flags & XFS_BMAPI_DELAY) &&
-                       isnullstartblock(got.br_startblock);
+               wasdelay = wr && !inhole && isnullstartblock(got.br_startblock);
                /*
                 * First, deal with the hole before the allocated space
                 * that we found, if any.
@@ -4467,99 +4809,7 @@ xfs_bmapi(
                                aoff = bno;
                        }
                        minlen = (flags & XFS_BMAPI_CONTIG) ? alen : 1;
-                       if (flags & XFS_BMAPI_DELAY) {
-                               xfs_extlen_t    extsz;
-
-                               /* Figure out the extent size, adjust alen */
-                               extsz = xfs_get_extsz_hint(ip);
-                               if (extsz) {
-                                       /*
-                                        * make sure we don't exceed a single
-                                        * extent length when we align the
-                                        * extent by reducing length we are
-                                        * going to allocate by the maximum
-                                        * amount extent size aligment may
-                                        * require.
-                                        */
-                                       alen = XFS_FILBLKS_MIN(len,
-                                                  MAXEXTLEN - (2 * extsz - 1));
-                                       error = xfs_bmap_extsize_align(mp,
-                                                       &got, &prev, extsz,
-                                                       rt, eof,
-                                                       flags&XFS_BMAPI_DELAY,
-                                                       flags&XFS_BMAPI_CONVERT,
-                                                       &aoff, &alen);
-                                       ASSERT(!error);
-                               }
-
-                               if (rt)
-                                       extsz = alen / mp->m_sb.sb_rextsize;
-
-                               /*
-                                * Make a transaction-less quota reservation for
-                                * delayed allocation blocks. This number gets
-                                * adjusted later.  We return if we haven't
-                                * allocated blocks already inside this loop.
-                                */
-                               error = xfs_trans_reserve_quota_nblks(
-                                               NULL, ip, (long)alen, 0,
-                                               rt ? XFS_QMOPT_RES_RTBLKS :
-                                                    XFS_QMOPT_RES_REGBLKS);
-                               if (error) {
-                                       if (n == 0) {
-                                               *nmap = 0;
-                                               ASSERT(cur == NULL);
-                                               return error;
-                                       }
-                                       break;
-                               }
-
-                               /*
-                                * Split changing sb for alen and indlen since
-                                * they could be coming from different places.
-                                */
-                               indlen = (xfs_extlen_t)
-                                       xfs_bmap_worst_indlen(ip, alen);
-                               ASSERT(indlen > 0);
-
-                               if (rt) {
-                                       error = xfs_mod_incore_sb(mp,
-                                                       XFS_SBS_FREXTENTS,
-                                                       -((int64_t)extsz), 0);
-                               } else {
-                                       error = xfs_icsb_modify_counters(mp,
-                                                       XFS_SBS_FDBLOCKS,
-                                                       -((int64_t)alen), 0);
-                               }
-                               if (!error) {
-                                       error = xfs_icsb_modify_counters(mp,
-                                                       XFS_SBS_FDBLOCKS,
-                                                       -((int64_t)indlen), 0);
-                                       if (error && rt)
-                                               xfs_mod_incore_sb(mp,
-                                                       XFS_SBS_FREXTENTS,
-                                                       (int64_t)extsz, 0);
-                                       else if (error)
-                                               xfs_icsb_modify_counters(mp,
-                                                       XFS_SBS_FDBLOCKS,
-                                                       (int64_t)alen, 0);
-                               }
-
-                               if (error) {
-                                       if (XFS_IS_QUOTA_ON(mp))
-                                               /* unreserve the blocks now */
-                                               (void)
-                                               xfs_trans_unreserve_quota_nblks(
-                                                       NULL, ip,
-                                                       (long)alen, 0, rt ?
-                                                       XFS_QMOPT_RES_RTBLKS :
-                                                       XFS_QMOPT_RES_REGBLKS);
-                                       break;
-                               }
-
-                               ip->i_delayed_blks += alen;
-                               abno = nullstartblock(indlen);
-                       } else {
+                       {
                                /*
                                 * If first time, allocate and fill in
                                 * once-only bma fields.
@@ -4666,19 +4916,12 @@ xfs_bmapi(
                        if (error)
                                goto error0;
                        ep = xfs_iext_get_ext(ifp, lastx);
-                       nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
                        xfs_bmbt_get_all(ep, &got);
                        ASSERT(got.br_startoff <= aoff);
                        ASSERT(got.br_startoff + got.br_blockcount >=
                                aoff + alen);
-#ifdef DEBUG
-                       if (flags & XFS_BMAPI_DELAY) {
-                               ASSERT(isnullstartblock(got.br_startblock));
-                               ASSERT(startblockval(got.br_startblock) > 0);
-                       }
                        ASSERT(got.br_state == XFS_EXT_NORM ||
                               got.br_state == XFS_EXT_UNWRITTEN);
-#endif
                        /*
                         * Fall down into the found allocated space case.
                         */
@@ -4697,44 +4940,9 @@ xfs_bmapi(
                        n++;
                        continue;
                }
-               /*
-                * Then deal with the allocated space we found.
-                */
-               ASSERT(ep != NULL);
-               if (!(flags & XFS_BMAPI_ENTIRE) &&
-                   (got.br_startoff + got.br_blockcount > obno)) {
-                       if (obno > bno)
-                               bno = obno;
-                       ASSERT((bno >= obno) || (n == 0));
-                       ASSERT(bno < end);
-                       mval->br_startoff = bno;
-                       if (isnullstartblock(got.br_startblock)) {
-                               ASSERT(!wr || (flags & XFS_BMAPI_DELAY));
-                               mval->br_startblock = DELAYSTARTBLOCK;
-                       } else
-                               mval->br_startblock =
-                                       got.br_startblock +
-                                       (bno - got.br_startoff);
-                       /*
-                        * Return the minimum of what we got and what we
-                        * asked for for the length.  We can use the len
-                        * variable here because it is modified below
-                        * and we could have been there before coming
-                        * here if the first part of the allocation
-                        * didn't overlap what was asked for.
-                        */
-                       mval->br_blockcount =
-                               XFS_FILBLKS_MIN(end - bno, got.br_blockcount -
-                                       (bno - got.br_startoff));
-                       mval->br_state = got.br_state;
-                       ASSERT(mval->br_blockcount <= len);
-               } else {
-                       *mval = got;
-                       if (isnullstartblock(mval->br_startblock)) {
-                               ASSERT(!wr || (flags & XFS_BMAPI_DELAY));
-                               mval->br_startblock = DELAYSTARTBLOCK;
-                       }
-               }
+
+               /* Deal with the allocated space we found.  */
+               xfs_bmapi_trim_map(mval, &got, &bno, len, obno, end, n, flags);
 
                /*
                 * Check if writing previously allocated but
@@ -4742,7 +4950,7 @@ xfs_bmapi(
                 */
                if (wr &&
                    ((mval->br_state == XFS_EXT_UNWRITTEN &&
-                     ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_DELAY)) == 0)) ||
+                     ((flags & XFS_BMAPI_PREALLOC) == 0)) ||
                     (mval->br_state == XFS_EXT_NORM &&
                      ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_CONVERT)) ==
                                (XFS_BMAPI_PREALLOC|XFS_BMAPI_CONVERT))))) {
@@ -4767,7 +4975,6 @@ xfs_bmapi(
                        if (error)
                                goto error0;
                        ep = xfs_iext_get_ext(ifp, lastx);
-                       nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
                        xfs_bmbt_get_all(ep, &got);
                        /*
                         * We may have combined previously unwritten
@@ -4778,42 +4985,9 @@ xfs_bmapi(
                                continue;
                }
 
-               ASSERT((flags & XFS_BMAPI_ENTIRE) ||
-                      ((mval->br_startoff + mval->br_blockcount) <= end));
-               ASSERT((flags & XFS_BMAPI_ENTIRE) ||
-                      (mval->br_blockcount <= len) ||
-                      (mval->br_startoff < obno));
-               bno = mval->br_startoff + mval->br_blockcount;
-               len = end - bno;
-               if (n > 0 && mval->br_startoff == mval[-1].br_startoff) {
-                       ASSERT(mval->br_startblock == mval[-1].br_startblock);
-                       ASSERT(mval->br_blockcount > mval[-1].br_blockcount);
-                       ASSERT(mval->br_state == mval[-1].br_state);
-                       mval[-1].br_blockcount = mval->br_blockcount;
-                       mval[-1].br_state = mval->br_state;
-               } else if (n > 0 && mval->br_startblock != DELAYSTARTBLOCK &&
-                          mval[-1].br_startblock != DELAYSTARTBLOCK &&
-                          mval[-1].br_startblock != HOLESTARTBLOCK &&
-                          mval->br_startblock ==
-                          mval[-1].br_startblock + mval[-1].br_blockcount &&
-                          ((flags & XFS_BMAPI_IGSTATE) ||
-                               mval[-1].br_state == mval->br_state)) {
-                       ASSERT(mval->br_startoff ==
-                              mval[-1].br_startoff + mval[-1].br_blockcount);
-                       mval[-1].br_blockcount += mval->br_blockcount;
-               } else if (n > 0 &&
-                          mval->br_startblock == DELAYSTARTBLOCK &&
-                          mval[-1].br_startblock == DELAYSTARTBLOCK &&
-                          mval->br_startoff ==
-                          mval[-1].br_startoff + mval[-1].br_blockcount) {
-                       mval[-1].br_blockcount += mval->br_blockcount;
-                       mval[-1].br_state = mval->br_state;
-               } else if (!((n == 0) &&
-                            ((mval->br_startoff + mval->br_blockcount) <=
-                             obno))) {
-                       mval++;
-                       n++;
-               }
+               /* update the extent map to return */
+               xfs_bmapi_update_map(&mval, &bno, &len, obno, end, &n, flags);
+
                /*
                 * If we're done, stop now.  Stop when we've allocated
                 * XFS_BMAP_MAX_NMAP extents no matter what.  Otherwise
@@ -4825,7 +4999,7 @@ xfs_bmapi(
                 * Else go on to the next record.
                 */
                prev = got;
-               if (++lastx < nextents) {
+               if (++lastx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t)) {
                        ep = xfs_iext_get_ext(ifp, lastx);
                        xfs_bmbt_get_all(ep, &got);
                } else {
@@ -4891,58 +5065,6 @@ error0:
        return error;
 }
 
-/*
- * Map file blocks to filesystem blocks, simple version.
- * One block (extent) only, read-only.
- * For flags, only the XFS_BMAPI_ATTRFORK flag is examined.
- * For the other flag values, the effect is as if XFS_BMAPI_METADATA
- * was set and all the others were clear.
- */
-int                                            /* error */
-xfs_bmapi_single(
-       xfs_trans_t     *tp,            /* transaction pointer */
-       xfs_inode_t     *ip,            /* incore inode */
-       int             whichfork,      /* data or attr fork */
-       xfs_fsblock_t   *fsb,           /* output: mapped block */
-       xfs_fileoff_t   bno)            /* starting file offs. mapped */
-{
-       int             eof;            /* we've hit the end of extents */
-       int             error;          /* error return */
-       xfs_bmbt_irec_t got;            /* current file extent record */
-       xfs_ifork_t     *ifp;           /* inode fork pointer */
-       xfs_extnum_t    lastx;          /* last useful extent number */
-       xfs_bmbt_irec_t prev;           /* previous file extent record */
-
-       ifp = XFS_IFORK_PTR(ip, whichfork);
-       if (unlikely(
-           XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
-           XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS)) {
-              XFS_ERROR_REPORT("xfs_bmapi_single", XFS_ERRLEVEL_LOW,
-                               ip->i_mount);
-              return XFS_ERROR(EFSCORRUPTED);
-       }
-       if (XFS_FORCED_SHUTDOWN(ip->i_mount))
-               return XFS_ERROR(EIO);
-       XFS_STATS_INC(xs_blk_mapr);
-       if (!(ifp->if_flags & XFS_IFEXTENTS) &&
-           (error = xfs_iread_extents(tp, ip, whichfork)))
-               return error;
-       (void)xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
-               &prev);
-       /*
-        * Reading past eof, act as though there's a hole
-        * up to end.
-        */
-       if (eof || got.br_startoff > bno) {
-               *fsb = NULLFSBLOCK;
-               return 0;
-       }
-       ASSERT(!isnullstartblock(got.br_startblock));
-       ASSERT(bno < got.br_startoff + got.br_blockcount);
-       *fsb = got.br_startblock + (bno - got.br_startoff);
-       return 0;
-}
-
 /*
  * Unmap (remove) blocks from a file.
  * If nexts is nonzero then the number of extents to remove is limited to
@@ -5505,10 +5627,9 @@ xfs_getbmap(
 
        do {
                nmap = (nexleft > subnex) ? subnex : nexleft;
-               error = xfs_bmapi(NULL, ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
-                                 XFS_BB_TO_FSB(mp, bmv->bmv_length),
-                                 bmapi_flags, NULL, 0, map, &nmap,
-                                 NULL);
+               error = xfs_bmapi_read(ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
+                                      XFS_BB_TO_FSB(mp, bmv->bmv_length),
+                                      map, &nmap, bmapi_flags);
                if (error)
                        goto out_free_map;
                ASSERT(nmap <= subnex);
@@ -6099,9 +6220,8 @@ xfs_bmap_punch_delalloc_range(
                 * trying to remove a real extent (which requires a
                 * transaction) or a hole, which is probably a bad idea...
                 */
-               error = xfs_bmapi(NULL, ip, start_fsb, 1,
-                               XFS_BMAPI_ENTIRE,  NULL, 0, &imap,
-                               &nimaps, NULL);
+               error = xfs_bmapi_read(ip, start_fsb, 1, &imap, &nimaps,
+                                      XFS_BMAPI_ENTIRE);
 
                if (error) {
                        /* something screwed, just bail */