]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: move xfs_bmapi_reserve_delalloc to xfs_iomap.c
authorChristoph Hellwig <hch@lst.de>
Sun, 17 Nov 2024 09:22:50 +0000 (10:22 +0100)
committerChristoph Hellwig <hch@lst.de>
Mon, 3 Mar 2025 15:16:44 +0000 (08:16 -0700)
Delalloc reservations are not supported in userspace, and thus it doesn't
make sense to share this helper with xfsprogs.c.  Move it to xfs_iomap.c
toward the two callers.

Note that there rest of the delalloc handling should probably eventually
also move out of xfs_bmap.c, but that will require a bit more surgery.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
fs/xfs/libxfs/xfs_bmap.c
fs/xfs/libxfs/xfs_bmap.h
fs/xfs/xfs_iomap.c

index 0ef19f1469ec9d1eb0d728f5874672154e29d94f..5b17e59ed5b8b50ce960442597b866ff641290a7 100644 (file)
@@ -34,7 +34,6 @@
 #include "xfs_ag.h"
 #include "xfs_ag_resv.h"
 #include "xfs_refcount.h"
-#include "xfs_icache.h"
 #include "xfs_iomap.h"
 #include "xfs_health.h"
 #include "xfs_bmap_item.h"
@@ -171,18 +170,16 @@ xfs_bmbt_update(
  * Compute the worst-case number of indirect blocks that will be used
  * for ip's delayed extent of length "len".
  */
-STATIC xfs_filblks_t
+xfs_filblks_t
 xfs_bmap_worst_indlen(
-       xfs_inode_t     *ip,            /* incore inode pointer */
-       xfs_filblks_t   len)            /* delayed extent length */
+       struct xfs_inode        *ip,            /* incore inode pointer */
+       xfs_filblks_t           len)            /* delayed extent length */
 {
-       int             level;          /* btree level number */
-       int             maxrecs;        /* maximum record count at this level */
-       xfs_mount_t     *mp;            /* mount structure */
-       xfs_filblks_t   rval;           /* return value */
+       struct xfs_mount        *mp = ip->i_mount;
+       int                     maxrecs = mp->m_bmap_dmxr[0];
+       int                     level;
+       xfs_filblks_t           rval;
 
-       mp = ip->i_mount;
-       maxrecs = mp->m_bmap_dmxr[0];
        for (level = 0, rval = 0;
             level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK);
             level++) {
@@ -2571,146 +2568,6 @@ done:
 #undef PREV
 }
 
-/*
- * Convert a hole to a delayed allocation.
- */
-STATIC void
-xfs_bmap_add_extent_hole_delay(
-       xfs_inode_t             *ip,    /* incore inode pointer */
-       int                     whichfork,
-       struct xfs_iext_cursor  *icur,
-       xfs_bmbt_irec_t         *new)   /* new data to add to file extents */
-{
-       struct xfs_ifork        *ifp;   /* inode fork pointer */
-       xfs_bmbt_irec_t         left;   /* left neighbor extent entry */
-       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 */
-       uint32_t                state = xfs_bmap_fork_to_state(whichfork);
-       xfs_filblks_t           temp;    /* temp for indirect calculations */
-
-       ifp = xfs_ifork_ptr(ip, whichfork);
-       ASSERT(isnullstartblock(new->br_startblock));
-
-       /*
-        * Check and set flags if this segment has a left neighbor
-        */
-       if (xfs_iext_peek_prev_extent(ifp, icur, &left)) {
-               state |= BMAP_LEFT_VALID;
-               if (isnullstartblock(left.br_startblock))
-                       state |= BMAP_LEFT_DELAY;
-       }
-
-       /*
-        * Check and set flags if the current (right) segment exists.
-        * If it doesn't exist, we're converting the hole at end-of-file.
-        */
-       if (xfs_iext_get_extent(ifp, icur, &right)) {
-               state |= BMAP_RIGHT_VALID;
-               if (isnullstartblock(right.br_startblock))
-                       state |= BMAP_RIGHT_DELAY;
-       }
-
-       /*
-        * Set contiguity flags on the left and right neighbors.
-        * Don't let extents get too large, even if the pieces are contiguous.
-        */
-       if ((state & BMAP_LEFT_VALID) && (state & BMAP_LEFT_DELAY) &&
-           left.br_startoff + left.br_blockcount == new->br_startoff &&
-           left.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN)
-               state |= BMAP_LEFT_CONTIG;
-
-       if ((state & BMAP_RIGHT_VALID) && (state & BMAP_RIGHT_DELAY) &&
-           new->br_startoff + new->br_blockcount == right.br_startoff &&
-           new->br_blockcount + right.br_blockcount <= XFS_MAX_BMBT_EXTLEN &&
-           (!(state & BMAP_LEFT_CONTIG) ||
-            (left.br_blockcount + new->br_blockcount +
-             right.br_blockcount <= XFS_MAX_BMBT_EXTLEN)))
-               state |= BMAP_RIGHT_CONTIG;
-
-       /*
-        * Switch out based on the contiguity flags.
-        */
-       switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) {
-       case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
-               /*
-                * New allocation is contiguous with delayed allocations
-                * on the left and on the right.
-                * Merge all three into a single extent record.
-                */
-               temp = left.br_blockcount + new->br_blockcount +
-                       right.br_blockcount;
-
-               oldlen = startblockval(left.br_startblock) +
-                       startblockval(new->br_startblock) +
-                       startblockval(right.br_startblock);
-               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
-                                        oldlen);
-               left.br_startblock = nullstartblock(newlen);
-               left.br_blockcount = temp;
-
-               xfs_iext_remove(ip, icur, state);
-               xfs_iext_prev(ifp, icur);
-               xfs_iext_update_extent(ip, state, icur, &left);
-               break;
-
-       case BMAP_LEFT_CONTIG:
-               /*
-                * New allocation is contiguous with a delayed allocation
-                * on the left.
-                * Merge the new allocation with the left neighbor.
-                */
-               temp = left.br_blockcount + new->br_blockcount;
-
-               oldlen = startblockval(left.br_startblock) +
-                       startblockval(new->br_startblock);
-               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
-                                        oldlen);
-               left.br_blockcount = temp;
-               left.br_startblock = nullstartblock(newlen);
-
-               xfs_iext_prev(ifp, icur);
-               xfs_iext_update_extent(ip, state, icur, &left);
-               break;
-
-       case BMAP_RIGHT_CONTIG:
-               /*
-                * New allocation is contiguous with a delayed allocation
-                * on the right.
-                * Merge the new allocation with the right neighbor.
-                */
-               temp = new->br_blockcount + right.br_blockcount;
-               oldlen = startblockval(new->br_startblock) +
-                       startblockval(right.br_startblock);
-               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
-                                        oldlen);
-               right.br_startoff = new->br_startoff;
-               right.br_startblock = nullstartblock(newlen);
-               right.br_blockcount = temp;
-               xfs_iext_update_extent(ip, state, icur, &right);
-               break;
-
-       case 0:
-               /*
-                * New allocation is not contiguous with another
-                * delayed allocation.
-                * Insert a new entry.
-                */
-               oldlen = newlen = 0;
-               xfs_iext_insert(ip, icur, new, state);
-               break;
-       }
-       if (oldlen != newlen) {
-               ASSERT(oldlen > newlen);
-               xfs_add_fdblocks(ip->i_mount, oldlen - newlen);
-
-               /*
-                * Nothing to do for disk quota accounting here.
-                */
-               xfs_mod_delalloc(ip, 0, (int64_t)newlen - oldlen);
-       }
-}
-
 /*
  * Convert a hole to a real allocation.
  */
@@ -4039,144 +3896,6 @@ 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           off,
-       xfs_filblks_t           len,
-       xfs_filblks_t           prealloc,
-       struct xfs_bmbt_irec    *got,
-       struct xfs_iext_cursor  *icur,
-       int                     eof)
-{
-       struct xfs_mount        *mp = ip->i_mount;
-       struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, whichfork);
-       xfs_extlen_t            alen;
-       xfs_extlen_t            indlen;
-       uint64_t                fdblocks;
-       int                     error;
-       xfs_fileoff_t           aoff;
-       bool                    use_cowextszhint =
-                                       whichfork == XFS_COW_FORK && !prealloc;
-
-retry:
-       /*
-        * Cap the alloc length. Keep track of prealloc so we know whether to
-        * tag the inode before we return.
-        */
-       aoff = off;
-       alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN);
-       if (!eof)
-               alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
-       if (prealloc && alen >= len)
-               prealloc = alen - len;
-
-       /*
-        * If we're targetting the COW fork but aren't creating a speculative
-        * posteof preallocation, try to expand the reservation to align with
-        * the COW extent size hint if there's sufficient free space.
-        *
-        * Unlike the data fork, the CoW cancellation functions will free all
-        * the reservations at inactivation, so we don't require that every
-        * delalloc reservation have a dirty pagecache.
-        */
-       if (use_cowextszhint) {
-               struct xfs_bmbt_irec    prev;
-               xfs_extlen_t            extsz = xfs_get_cowextsz_hint(ip);
-
-               if (!xfs_iext_peek_prev_extent(ifp, icur, &prev))
-                       prev.br_startoff = NULLFILEOFF;
-
-               error = xfs_bmap_extsize_align(mp, got, &prev, extsz, 0, eof,
-                                              1, 0, &aoff, &alen);
-               ASSERT(!error);
-       }
-
-       /*
-        * 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_quota_reserve_blkres(ip, alen);
-       if (error)
-               goto out;
-
-       /*
-        * 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);
-
-       fdblocks = indlen;
-       if (XFS_IS_REALTIME_INODE(ip)) {
-               error = xfs_dec_frextents(mp, xfs_blen_to_rtbxlen(mp, alen));
-               if (error)
-                       goto out_unreserve_quota;
-       } else {
-               fdblocks += alen;
-       }
-
-       error = xfs_dec_fdblocks(mp, fdblocks, false);
-       if (error)
-               goto out_unreserve_frextents;
-
-       ip->i_delayed_blks += alen;
-       xfs_mod_delalloc(ip, alen, indlen);
-
-       got->br_startoff = aoff;
-       got->br_startblock = nullstartblock(indlen);
-       got->br_blockcount = alen;
-       got->br_state = XFS_EXT_NORM;
-
-       xfs_bmap_add_extent_hole_delay(ip, whichfork, icur, got);
-
-       /*
-        * 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.
-        */
-       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);
-
-       return 0;
-
-out_unreserve_frextents:
-       if (XFS_IS_REALTIME_INODE(ip))
-               xfs_add_frextents(mp, xfs_blen_to_rtbxlen(mp, alen));
-out_unreserve_quota:
-       if (XFS_IS_QUOTA_ON(mp))
-               xfs_quota_unreserve_blkres(ip, alen);
-out:
-       if (error == -ENOSPC || error == -EDQUOT) {
-               trace_xfs_delalloc_enospc(ip, off, len);
-
-               if (prealloc || use_cowextszhint) {
-                       /* retry without any preallocation */
-                       use_cowextszhint = false;
-                       prealloc = 0;
-                       goto retry;
-               }
-       }
-       return error;
-}
-
 static int
 xfs_bmapi_allocate(
        struct xfs_bmalloca     *bma)
index 4b721d9359943b76f742118378b5a18c8bf4a0ae..4d48087fd3a8ee46eea87fe8ebf3fe0193e2e791 100644 (file)
@@ -219,10 +219,6 @@ int        xfs_bmap_insert_extents(struct xfs_trans *tp, struct xfs_inode *ip,
                bool *done, xfs_fileoff_t stop_fsb);
 int    xfs_bmap_split_extent(struct xfs_trans *tp, struct xfs_inode *ip,
                xfs_fileoff_t split_offset);
-int    xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, int whichfork,
-               xfs_fileoff_t off, xfs_filblks_t len, xfs_filblks_t prealloc,
-               struct xfs_bmbt_irec *got, struct xfs_iext_cursor *cur,
-               int eof);
 int    xfs_bmapi_convert_delalloc(struct xfs_inode *ip, int whichfork,
                xfs_off_t offset, struct iomap *iomap, unsigned int *seq);
 int    xfs_bmap_add_extent_unwritten_real(struct xfs_trans *tp,
@@ -233,6 +229,7 @@ xfs_extlen_t xfs_bmapi_minleft(struct xfs_trans *tp, struct xfs_inode *ip,
                int fork);
 int    xfs_bmap_btalloc_low_space(struct xfs_bmalloca *ap,
                struct xfs_alloc_arg *args);
+xfs_filblks_t xfs_bmap_worst_indlen(struct xfs_inode *ip, xfs_filblks_t len);
 
 enum xfs_bmap_intent_type {
        XFS_BMAP_MAP = 1,
index c669b93bb2d1104077eccf97afbbfbcfa613538d..a724fc2612e3d128236e9be8c9dcada8737b4c23 100644 (file)
@@ -30,6 +30,7 @@
 #include "xfs_reflink.h"
 #include "xfs_health.h"
 #include "xfs_rtbitmap.h"
+#include "xfs_icache.h"
 
 #define XFS_ALLOC_ALIGN(mp, off) \
        (((off) >> mp->m_allocsize_log) << mp->m_allocsize_log)
@@ -988,6 +989,284 @@ const struct iomap_ops xfs_dax_write_iomap_ops = {
        .iomap_end      = xfs_dax_write_iomap_end,
 };
 
+/*
+ * Convert a hole to a delayed allocation.
+ */
+static void
+xfs_bmap_add_extent_hole_delay(
+       struct xfs_inode        *ip,    /* incore inode pointer */
+       int                     whichfork,
+       struct xfs_iext_cursor  *icur,
+       struct xfs_bmbt_irec    *new)   /* new data to add to file extents */
+{
+       struct xfs_ifork        *ifp;   /* inode fork pointer */
+       xfs_bmbt_irec_t         left;   /* left neighbor extent entry */
+       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 */
+       uint32_t                state = xfs_bmap_fork_to_state(whichfork);
+       xfs_filblks_t           temp;    /* temp for indirect calculations */
+
+       ifp = xfs_ifork_ptr(ip, whichfork);
+       ASSERT(isnullstartblock(new->br_startblock));
+
+       /*
+        * Check and set flags if this segment has a left neighbor
+        */
+       if (xfs_iext_peek_prev_extent(ifp, icur, &left)) {
+               state |= BMAP_LEFT_VALID;
+               if (isnullstartblock(left.br_startblock))
+                       state |= BMAP_LEFT_DELAY;
+       }
+
+       /*
+        * Check and set flags if the current (right) segment exists.
+        * If it doesn't exist, we're converting the hole at end-of-file.
+        */
+       if (xfs_iext_get_extent(ifp, icur, &right)) {
+               state |= BMAP_RIGHT_VALID;
+               if (isnullstartblock(right.br_startblock))
+                       state |= BMAP_RIGHT_DELAY;
+       }
+
+       /*
+        * Set contiguity flags on the left and right neighbors.
+        * Don't let extents get too large, even if the pieces are contiguous.
+        */
+       if ((state & BMAP_LEFT_VALID) && (state & BMAP_LEFT_DELAY) &&
+           left.br_startoff + left.br_blockcount == new->br_startoff &&
+           left.br_blockcount + new->br_blockcount <= XFS_MAX_BMBT_EXTLEN)
+               state |= BMAP_LEFT_CONTIG;
+
+       if ((state & BMAP_RIGHT_VALID) && (state & BMAP_RIGHT_DELAY) &&
+           new->br_startoff + new->br_blockcount == right.br_startoff &&
+           new->br_blockcount + right.br_blockcount <= XFS_MAX_BMBT_EXTLEN &&
+           (!(state & BMAP_LEFT_CONTIG) ||
+            (left.br_blockcount + new->br_blockcount +
+             right.br_blockcount <= XFS_MAX_BMBT_EXTLEN)))
+               state |= BMAP_RIGHT_CONTIG;
+
+       /*
+        * Switch out based on the contiguity flags.
+        */
+       switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) {
+       case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
+               /*
+                * New allocation is contiguous with delayed allocations
+                * on the left and on the right.
+                * Merge all three into a single extent record.
+                */
+               temp = left.br_blockcount + new->br_blockcount +
+                       right.br_blockcount;
+
+               oldlen = startblockval(left.br_startblock) +
+                       startblockval(new->br_startblock) +
+                       startblockval(right.br_startblock);
+               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                                        oldlen);
+               left.br_startblock = nullstartblock(newlen);
+               left.br_blockcount = temp;
+
+               xfs_iext_remove(ip, icur, state);
+               xfs_iext_prev(ifp, icur);
+               xfs_iext_update_extent(ip, state, icur, &left);
+               break;
+
+       case BMAP_LEFT_CONTIG:
+               /*
+                * New allocation is contiguous with a delayed allocation
+                * on the left.
+                * Merge the new allocation with the left neighbor.
+                */
+               temp = left.br_blockcount + new->br_blockcount;
+
+               oldlen = startblockval(left.br_startblock) +
+                       startblockval(new->br_startblock);
+               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                                        oldlen);
+               left.br_blockcount = temp;
+               left.br_startblock = nullstartblock(newlen);
+
+               xfs_iext_prev(ifp, icur);
+               xfs_iext_update_extent(ip, state, icur, &left);
+               break;
+
+       case BMAP_RIGHT_CONTIG:
+               /*
+                * New allocation is contiguous with a delayed allocation
+                * on the right.
+                * Merge the new allocation with the right neighbor.
+                */
+               temp = new->br_blockcount + right.br_blockcount;
+               oldlen = startblockval(new->br_startblock) +
+                       startblockval(right.br_startblock);
+               newlen = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                                        oldlen);
+               right.br_startoff = new->br_startoff;
+               right.br_startblock = nullstartblock(newlen);
+               right.br_blockcount = temp;
+               xfs_iext_update_extent(ip, state, icur, &right);
+               break;
+
+       case 0:
+               /*
+                * New allocation is not contiguous with another
+                * delayed allocation.
+                * Insert a new entry.
+                */
+               oldlen = newlen = 0;
+               xfs_iext_insert(ip, icur, new, state);
+               break;
+       }
+       if (oldlen != newlen) {
+               ASSERT(oldlen > newlen);
+               xfs_add_fdblocks(ip->i_mount, oldlen - newlen);
+
+               /*
+                * Nothing to do for disk quota accounting here.
+                */
+               xfs_mod_delalloc(ip, 0, (int64_t)newlen - oldlen);
+       }
+}
+
+/*
+ * 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.
+ */
+static int
+xfs_bmapi_reserve_delalloc(
+       struct xfs_inode        *ip,
+       int                     whichfork,
+       xfs_fileoff_t           off,
+       xfs_filblks_t           len,
+       xfs_filblks_t           prealloc,
+       struct xfs_bmbt_irec    *got,
+       struct xfs_iext_cursor  *icur,
+       int                     eof)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_ifork        *ifp = xfs_ifork_ptr(ip, whichfork);
+       xfs_extlen_t            alen;
+       xfs_extlen_t            indlen;
+       uint64_t                fdblocks;
+       int                     error;
+       xfs_fileoff_t           aoff;
+       bool                    use_cowextszhint =
+                                       whichfork == XFS_COW_FORK && !prealloc;
+
+retry:
+       /*
+        * Cap the alloc length. Keep track of prealloc so we know whether to
+        * tag the inode before we return.
+        */
+       aoff = off;
+       alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN);
+       if (!eof)
+               alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
+       if (prealloc && alen >= len)
+               prealloc = alen - len;
+
+       /*
+        * If we're targetting the COW fork but aren't creating a speculative
+        * posteof preallocation, try to expand the reservation to align with
+        * the COW extent size hint if there's sufficient free space.
+        *
+        * Unlike the data fork, the CoW cancellation functions will free all
+        * the reservations at inactivation, so we don't require that every
+        * delalloc reservation have a dirty pagecache.
+        */
+       if (use_cowextszhint) {
+               struct xfs_bmbt_irec    prev;
+               xfs_extlen_t            extsz = xfs_get_cowextsz_hint(ip);
+
+               if (!xfs_iext_peek_prev_extent(ifp, icur, &prev))
+                       prev.br_startoff = NULLFILEOFF;
+
+               error = xfs_bmap_extsize_align(mp, got, &prev, extsz, 0, eof,
+                                              1, 0, &aoff, &alen);
+               ASSERT(!error);
+       }
+
+       /*
+        * 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_quota_reserve_blkres(ip, alen);
+       if (error)
+               goto out;
+
+       /*
+        * 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);
+
+       fdblocks = indlen;
+       if (XFS_IS_REALTIME_INODE(ip)) {
+               error = xfs_dec_frextents(mp, xfs_blen_to_rtbxlen(mp, alen));
+               if (error)
+                       goto out_unreserve_quota;
+       } else {
+               fdblocks += alen;
+       }
+
+       error = xfs_dec_fdblocks(mp, fdblocks, false);
+       if (error)
+               goto out_unreserve_frextents;
+
+       ip->i_delayed_blks += alen;
+       xfs_mod_delalloc(ip, alen, indlen);
+
+       got->br_startoff = aoff;
+       got->br_startblock = nullstartblock(indlen);
+       got->br_blockcount = alen;
+       got->br_state = XFS_EXT_NORM;
+
+       xfs_bmap_add_extent_hole_delay(ip, whichfork, icur, got);
+
+       /*
+        * 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.
+        */
+       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);
+
+       return 0;
+
+out_unreserve_frextents:
+       if (XFS_IS_REALTIME_INODE(ip))
+               xfs_add_frextents(mp, xfs_blen_to_rtbxlen(mp, alen));
+out_unreserve_quota:
+       if (XFS_IS_QUOTA_ON(mp))
+               xfs_quota_unreserve_blkres(ip, alen);
+out:
+       if (error == -ENOSPC || error == -EDQUOT) {
+               trace_xfs_delalloc_enospc(ip, off, len);
+
+               if (prealloc || use_cowextszhint) {
+                       /* retry without any preallocation */
+                       use_cowextszhint = false;
+                       prealloc = 0;
+                       goto retry;
+               }
+       }
+       return error;
+}
+
 static int
 xfs_buffered_write_iomap_begin(
        struct inode            *inode,