#define XFS_TRANS_DQ_DIRTY     0x10    /* at least one dquot in trx dirty */
 #define XFS_TRANS_RESERVE      0x20    /* OK to use reserved data blocks */
 #define XFS_TRANS_NO_WRITECOUNT 0x40   /* do not elevate SB writecount */
+#define XFS_TRANS_RES_FDBLKS   0x80    /* reserve newly freed blocks */
 /*
  * LOWMODE is used by the allocator to activate the lowspace algorithm - when
  * free space is running low the extent allocator may choose to allocate an
 
        int                     lock_flags;
        uint64_t                f;
        int                     resblks = 0;
+       unsigned int            flags = 0;
 
        /*
         * Lock the inodes against other IO, page faults and truncate to
                resblks +=  XFS_SWAP_RMAP_SPACE_RES(mp, tipnext, w);
 
                /*
-                * Handle the corner case where either inode might straddle the
-                * btree format boundary. If so, the inode could bounce between
-                * btree <-> extent format on unmap -> remap cycles, freeing and
-                * allocating a bmapbt block each time.
+                * If either inode straddles a bmapbt block allocation boundary,
+                * the rmapbt algorithm triggers repeated allocs and frees as
+                * extents are remapped. This can exhaust the block reservation
+                * prematurely and cause shutdown. Return freed blocks to the
+                * transaction reservation to counter this behavior.
                 */
-               if (ipnext == (XFS_IFORK_MAXEXT(ip, w) + 1))
-                       resblks += XFS_IFORK_MAXEXT(ip, w);
-               if (tipnext == (XFS_IFORK_MAXEXT(tip, w) + 1))
-                       resblks += XFS_IFORK_MAXEXT(tip, w);
+               flags |= XFS_TRANS_RES_FDBLKS;
        }
-       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, flags,
+                               &tp);
        if (error)
                goto out_unlock;
 
 
 
        ntp->t_flags = XFS_TRANS_PERM_LOG_RES |
                       (tp->t_flags & XFS_TRANS_RESERVE) |
-                      (tp->t_flags & XFS_TRANS_NO_WRITECOUNT);
+                      (tp->t_flags & XFS_TRANS_NO_WRITECOUNT) |
+                      (tp->t_flags & XFS_TRANS_RES_FDBLKS);
        /* We gave our writer reference to the new transaction */
        tp->t_flags |= XFS_TRANS_NO_WRITECOUNT;
        ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket);
         */
        WARN_ON(resp->tr_logres > 0 &&
                mp->m_super->s_writers.frozen == SB_FREEZE_COMPLETE);
+       ASSERT(!(flags & XFS_TRANS_RES_FDBLKS) ||
+              xfs_sb_version_haslazysbcount(&mp->m_sb));
 
        tp->t_magic = XFS_TRANS_HEADER_MAGIC;
        tp->t_flags = flags;
                        tp->t_blk_res_used += (uint)-delta;
                        if (tp->t_blk_res_used > tp->t_blk_res)
                                xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+               } else if (delta > 0 && (tp->t_flags & XFS_TRANS_RES_FDBLKS)) {
+                       int64_t blkres_delta;
+
+                       /*
+                        * Return freed blocks directly to the reservation
+                        * instead of the global pool, being careful not to
+                        * overflow the trans counter. This is used to preserve
+                        * reservation across chains of transaction rolls that
+                        * repeatedly free and allocate blocks.
+                        */
+                       blkres_delta = min_t(int64_t, delta,
+                                            UINT_MAX - tp->t_blk_res);
+                       tp->t_blk_res += blkres_delta;
+                       delta -= blkres_delta;
                }
                tp->t_fdblocks_delta += delta;
                if (xfs_sb_version_haslazysbcount(&mp->m_sb))