int                     error;
        xfs_fileoff_t           next_fsb = XFS_B_TO_FSB(mp, offset + len);
        xfs_fileoff_t           shift_fsb = XFS_B_TO_FSB(mp, len);
-       uint                    resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
        bool                    done = false;
 
        ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        if (error)
                return error;
 
-       while (!error && !done) {
-               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0,
-                                       &tp);
-               if (error)
-                       break;
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
+       if (error)
+               return error;
 
-               xfs_ilock(ip, XFS_ILOCK_EXCL);
-               error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot,
-                               ip->i_gdquot, ip->i_pdquot, resblks, 0,
-                               XFS_QMOPT_RES_REGBLKS);
-               if (error)
-                       goto out_trans_cancel;
-               xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
 
+       while (!done) {
                error = xfs_bmap_collapse_extents(tp, ip, &next_fsb, shift_fsb,
                                &done);
                if (error)
                        goto out_trans_cancel;
+               if (done)
+                       break;
 
-               error = xfs_trans_commit(tp);
+               /* finish any deferred frees and roll the transaction */
+               error = xfs_defer_finish(&tp);
+               if (error)
+                       goto out_trans_cancel;
        }
 
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
        return error;
 
 out_trans_cancel:
        xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
        return error;
 }