Merge branch 'drm-fixes-4.1' of git://people.freedesktop.org/~agd5f/linux into drm...
[sfrench/cifs-2.6.git] / fs / xfs / libxfs / xfs_bmap.c
index 61ec015dca168e4b3d6c067159f252d6f59506b1..f1026e86dabc9a00ead716785a3acb5c19ee8e10 100644 (file)
@@ -244,30 +244,6 @@ xfs_bmap_forkoff_reset(
        }
 }
 
-/*
- * Debug/sanity checking code
- */
-
-STATIC int
-xfs_bmap_sanity_check(
-       struct xfs_mount        *mp,
-       struct xfs_buf          *bp,
-       int                     level)
-{
-       struct xfs_btree_block  *block = XFS_BUF_TO_BLOCK(bp);
-
-       if (block->bb_magic != cpu_to_be32(XFS_BMAP_CRC_MAGIC) &&
-           block->bb_magic != cpu_to_be32(XFS_BMAP_MAGIC))
-               return 0;
-
-       if (be16_to_cpu(block->bb_level) != level ||
-           be16_to_cpu(block->bb_numrecs) == 0 ||
-           be16_to_cpu(block->bb_numrecs) > mp->m_bmap_dmxr[level != 0])
-               return 0;
-
-       return 1;
-}
-
 #ifdef DEBUG
 STATIC struct xfs_buf *
 xfs_bmap_get_bp(
@@ -410,9 +386,6 @@ xfs_bmap_check_leaf_extents(
                                goto error_norelse;
                }
                block = XFS_BUF_TO_BLOCK(bp);
-               XFS_WANT_CORRUPTED_GOTO(
-                       xfs_bmap_sanity_check(mp, bp, level),
-                       error0);
                if (level == 0)
                        break;
 
@@ -424,7 +397,8 @@ xfs_bmap_check_leaf_extents(
                xfs_check_block(block, mp, 0, 0);
                pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
                bno = be64_to_cpu(*pp);
-               XFS_WANT_CORRUPTED_GOTO(XFS_FSB_SANITY_CHECK(mp, bno), error0);
+               XFS_WANT_CORRUPTED_GOTO(mp,
+                                       XFS_FSB_SANITY_CHECK(mp, bno), error0);
                if (bp_release) {
                        bp_release = 0;
                        xfs_trans_brelse(NULL, bp);
@@ -1029,7 +1003,7 @@ xfs_bmap_add_attrfork_btree(
                if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat)))
                        goto error0;
                /* must be at least one entry */
-               XFS_WANT_CORRUPTED_GOTO(stat == 1, error0);
+               XFS_WANT_CORRUPTED_GOTO(mp, stat == 1, error0);
                if ((error = xfs_btree_new_iroot(cur, flags, &stat)))
                        goto error0;
                if (stat == 0) {
@@ -1311,14 +1285,12 @@ xfs_bmap_read_extents(
                if (error)
                        return error;
                block = XFS_BUF_TO_BLOCK(bp);
-               XFS_WANT_CORRUPTED_GOTO(
-                       xfs_bmap_sanity_check(mp, bp, level),
-                       error0);
                if (level == 0)
                        break;
                pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]);
                bno = be64_to_cpu(*pp);
-               XFS_WANT_CORRUPTED_GOTO(XFS_FSB_SANITY_CHECK(mp, bno), error0);
+               XFS_WANT_CORRUPTED_GOTO(mp,
+                       XFS_FSB_SANITY_CHECK(mp, bno), error0);
                xfs_trans_brelse(tp, bp);
        }
        /*
@@ -1345,9 +1317,6 @@ xfs_bmap_read_extents(
                                XFS_ERRLEVEL_LOW, ip->i_mount, block);
                        goto error0;
                }
-               XFS_WANT_CORRUPTED_GOTO(
-                       xfs_bmap_sanity_check(mp, bp, 0),
-                       error0);
                /*
                 * Read-ahead the next leaf block, if any.
                 */
@@ -1755,7 +1724,9 @@ xfs_bmap_add_extent_delay_real(
        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;
 
+       mp  = bma->tp ? bma->tp->t_mountp : NULL;
        ifp = XFS_IFORK_PTR(bma->ip, XFS_DATA_FORK);
 
        ASSERT(bma->idx >= 0);
@@ -1866,15 +1837,15 @@ xfs_bmap_add_extent_delay_real(
                                        RIGHT.br_blockcount, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_btree_delete(bma->cur, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_btree_decrement(bma->cur, 0, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, LEFT.br_startoff,
                                        LEFT.br_startblock,
                                        LEFT.br_blockcount +
@@ -1907,7 +1878,7 @@ xfs_bmap_add_extent_delay_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, LEFT.br_startoff,
                                        LEFT.br_startblock,
                                        LEFT.br_blockcount +
@@ -1938,7 +1909,7 @@ xfs_bmap_add_extent_delay_real(
                                        RIGHT.br_blockcount, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, PREV.br_startoff,
                                        new->br_startblock,
                                        PREV.br_blockcount +
@@ -1968,12 +1939,12 @@ xfs_bmap_add_extent_delay_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
                break;
 
@@ -2001,7 +1972,7 @@ xfs_bmap_add_extent_delay_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, LEFT.br_startoff,
                                        LEFT.br_startblock,
                                        LEFT.br_blockcount +
@@ -2038,12 +2009,12 @@ xfs_bmap_add_extent_delay_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
 
                if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
@@ -2084,7 +2055,7 @@ xfs_bmap_add_extent_delay_real(
                                        RIGHT.br_blockcount, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, new->br_startoff,
                                        new->br_startblock,
                                        new->br_blockcount +
@@ -2122,12 +2093,12 @@ xfs_bmap_add_extent_delay_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
 
                if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
@@ -2191,12 +2162,12 @@ xfs_bmap_add_extent_delay_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
 
                if (xfs_bmap_needs_btree(bma->ip, XFS_DATA_FORK)) {
@@ -2212,9 +2183,8 @@ xfs_bmap_add_extent_delay_real(
                diff = (int)(temp + temp2 - startblockval(PREV.br_startblock) -
                        (bma->cur ? bma->cur->bc_private.b.allocated : 0));
                if (diff > 0) {
-                       error = xfs_icsb_modify_counters(bma->ip->i_mount,
-                                       XFS_SBS_FDBLOCKS,
-                                       -((int64_t)diff), 0);
+                       error = xfs_mod_fdblocks(bma->ip->i_mount,
+                                                -((int64_t)diff), false);
                        ASSERT(!error);
                        if (error)
                                goto done;
@@ -2265,9 +2235,8 @@ xfs_bmap_add_extent_delay_real(
                        temp += bma->cur->bc_private.b.allocated;
                ASSERT(temp <= da_old);
                if (temp < da_old)
-                       xfs_icsb_modify_counters(bma->ip->i_mount,
-                                       XFS_SBS_FDBLOCKS,
-                                       (int64_t)(da_old - temp), 0);
+                       xfs_mod_fdblocks(bma->ip->i_mount,
+                                       (int64_t)(da_old - temp), false);
        }
 
        /* clear out the allocated field, done with it now in any case. */
@@ -2309,6 +2278,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        /* 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;
 
        *logflagsp = 0;
 
@@ -2421,19 +2391,19 @@ xfs_bmap_add_extent_unwritten_real(
                                        RIGHT.br_startblock,
                                        RIGHT.br_blockcount, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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 +
@@ -2464,13 +2434,13 @@ xfs_bmap_add_extent_unwritten_real(
                                        PREV.br_startblock, PREV.br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2499,13 +2469,13 @@ xfs_bmap_add_extent_unwritten_real(
                                        RIGHT.br_startblock,
                                        RIGHT.br_blockcount, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_delete(cur, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        if ((error = xfs_btree_decrement(cur, 0, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2532,7 +2502,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        new->br_startblock, new->br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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)))
@@ -2569,7 +2539,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        PREV.br_startblock, PREV.br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2611,7 +2581,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        PREV.br_startblock, PREV.br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2621,7 +2591,7 @@ xfs_bmap_add_extent_unwritten_real(
                        cur->bc_rec.b = *new;
                        if ((error = xfs_btree_insert(cur, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
                break;
 
@@ -2651,7 +2621,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        PREV.br_startblock,
                                        PREV.br_blockcount, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2689,7 +2659,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        PREV.br_startblock, PREV.br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2699,11 +2669,11 @@ xfs_bmap_add_extent_unwritten_real(
                                        new->br_startblock, new->br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
                break;
 
@@ -2737,7 +2707,7 @@ xfs_bmap_add_extent_unwritten_real(
                                        PREV.br_startblock, PREV.br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, 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,
@@ -2749,7 +2719,7 @@ xfs_bmap_add_extent_unwritten_real(
                                new->br_startoff - PREV.br_startoff;
                        if ((error = xfs_btree_insert(cur, &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        /*
                         * Reset the cursor to the position of the new extent
                         * we are about to insert as we can't trust it after
@@ -2759,12 +2729,12 @@ xfs_bmap_add_extent_unwritten_real(
                                        new->br_startblock, new->br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
                break;
 
@@ -2944,8 +2914,8 @@ xfs_bmap_add_extent_hole_delay(
        }
        if (oldlen != newlen) {
                ASSERT(oldlen > newlen);
-               xfs_icsb_modify_counters(ip->i_mount, XFS_SBS_FDBLOCKS,
-                       (int64_t)(oldlen - newlen), 0);
+               xfs_mod_fdblocks(ip->i_mount, (int64_t)(oldlen - newlen),
+                                false);
                /*
                 * Nothing to do for disk quota accounting here.
                 */
@@ -2968,7 +2938,9 @@ xfs_bmap_add_extent_hole_real(
        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->tp ? bma->tp->t_mountp : NULL;
        ifp = XFS_IFORK_PTR(bma->ip, whichfork);
 
        ASSERT(bma->idx >= 0);
@@ -3056,15 +3028,15 @@ xfs_bmap_add_extent_hole_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_btree_delete(bma->cur, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_btree_decrement(bma->cur, 0, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, left.br_startoff,
                                        left.br_startblock,
                                        left.br_blockcount +
@@ -3097,7 +3069,7 @@ xfs_bmap_add_extent_hole_real(
                                        &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, left.br_startoff,
                                        left.br_startblock,
                                        left.br_blockcount +
@@ -3131,7 +3103,7 @@ xfs_bmap_add_extent_hole_real(
                                        right.br_blockcount, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        error = xfs_bmbt_update(bma->cur, new->br_startoff,
                                        new->br_startblock,
                                        new->br_blockcount +
@@ -3161,12 +3133,12 @@ xfs_bmap_add_extent_hole_real(
                                        new->br_blockcount, &i);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 0, 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);
                        if (error)
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
                break;
        }
@@ -3252,12 +3224,24 @@ xfs_bmap_extsize_align(
                align_alen += temp;
                align_off -= temp;
        }
+
+       /* Same adjustment for the end of the requested area. */
+       temp = (align_alen % extsz);
+       if (temp)
+               align_alen += extsz - temp;
+
        /*
-        * Same adjustment for the end of the requested area.
+        * For large extent hint sizes, the aligned extent might be larger than
+        * MAXEXTLEN. In that case, reduce the size by an extsz so that it pulls
+        * the length back under MAXEXTLEN. The outer allocation loops handle
+        * short allocation just fine, so it is safe to do this. We only want to
+        * do it when we are forced to, though, because it means more allocation
+        * operations are required.
         */
-       if ((temp = (align_alen % extsz))) {
-               align_alen += extsz - temp;
-       }
+       while (align_alen > MAXEXTLEN)
+               align_alen -= extsz;
+       ASSERT(align_alen <= MAXEXTLEN);
+
        /*
         * If the previous block overlaps with this proposed allocation
         * then move the start forward without adjusting the length.
@@ -3346,7 +3330,9 @@ xfs_bmap_extsize_align(
                        return -EINVAL;
        } else {
                ASSERT(orig_off >= align_off);
-               ASSERT(orig_end <= align_off + align_alen);
+               /* see MAXEXTLEN handling above */
+               ASSERT(orig_end <= align_off + align_alen ||
+                      align_alen + extsz > MAXEXTLEN);
        }
 
 #ifdef DEBUG
@@ -4127,13 +4113,6 @@ xfs_bmapi_reserve_delalloc(
        /* 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);
@@ -4160,18 +4139,15 @@ xfs_bmapi_reserve_delalloc(
        ASSERT(indlen > 0);
 
        if (rt) {
-               error = xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS,
-                                         -((int64_t)extsz), 0);
+               error = xfs_mod_frextents(mp, -((int64_t)extsz));
        } else {
-               error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
-                                                -((int64_t)alen), 0);
+               error = xfs_mod_fdblocks(mp, -((int64_t)alen), false);
        }
 
        if (error)
                goto out_unreserve_quota;
 
-       error = xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
-                                        -((int64_t)indlen), 0);
+       error = xfs_mod_fdblocks(mp, -((int64_t)indlen), false);
        if (error)
                goto out_unreserve_blocks;
 
@@ -4198,9 +4174,9 @@ xfs_bmapi_reserve_delalloc(
 
 out_unreserve_blocks:
        if (rt)
-               xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS, extsz, 0);
+               xfs_mod_frextents(mp, extsz);
        else
-               xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS, alen, 0);
+               xfs_mod_fdblocks(mp, alen, false);
 out_unreserve_quota:
        if (XFS_IS_QUOTA_ON(mp))
                xfs_trans_unreserve_quota_nblks(NULL, ip, (long)alen, 0, rt ?
@@ -4801,7 +4777,7 @@ xfs_bmap_del_extent(
                                        got.br_startblock, got.br_blockcount,
                                        &i)))
                                goto done;
-                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                }
                da_old = da_new = 0;
        } else {
@@ -4835,7 +4811,7 @@ xfs_bmap_del_extent(
                }
                if ((error = xfs_btree_delete(cur, &i)))
                        goto done;
-               XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                break;
 
        case 2:
@@ -4935,7 +4911,8 @@ xfs_bmap_del_extent(
                                                        got.br_startblock,
                                                        temp, &i)))
                                                goto done;
-                                       XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                                       XFS_WANT_CORRUPTED_GOTO(mp,
+                                                               i == 1, done);
                                        /*
                                         * Update the btree record back
                                         * to the original value.
@@ -4956,7 +4933,7 @@ xfs_bmap_del_extent(
                                        error = -ENOSPC;
                                        goto done;
                                }
-                               XFS_WANT_CORRUPTED_GOTO(i == 1, done);
+                               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
                        } else
                                flags |= xfs_ilog_fext(whichfork);
                        XFS_IFORK_NEXT_SET(ip, whichfork,
@@ -5012,10 +4989,8 @@ xfs_bmap_del_extent(
         * Nothing to do for disk quota accounting here.
         */
        ASSERT(da_old >= da_new);
-       if (da_old > da_new) {
-               xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
-                       (int64_t)(da_old - da_new), 0);
-       }
+       if (da_old > da_new)
+               xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false);
 done:
        *logflagsp = flags;
        return error;
@@ -5284,14 +5259,13 @@ xfs_bunmapi(
 
                                rtexts = XFS_FSB_TO_B(mp, del.br_blockcount);
                                do_div(rtexts, mp->m_sb.sb_rextsize);
-                               xfs_mod_incore_sb(mp, XFS_SBS_FREXTENTS,
-                                               (int64_t)rtexts, 0);
+                               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 {
-                               xfs_icsb_modify_counters(mp, XFS_SBS_FDBLOCKS,
-                                               (int64_t)del.br_blockcount, 0);
+                               xfs_mod_fdblocks(mp, (int64_t)del.br_blockcount,
+                                                false);
                                (void)xfs_trans_reserve_quota_nblks(NULL,
                                        ip, -((long)del.br_blockcount), 0,
                                        XFS_QMOPT_RES_REGBLKS);
@@ -5453,6 +5427,7 @@ xfs_bmse_merge(
        struct xfs_bmbt_irec            left;
        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);
@@ -5487,19 +5462,19 @@ xfs_bmse_merge(
                                   got.br_blockcount, &i);
        if (error)
                return error;
-       XFS_WANT_CORRUPTED_RETURN(i == 1);
+       XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
 
        error = xfs_btree_delete(cur, &i);
        if (error)
                return error;
-       XFS_WANT_CORRUPTED_RETURN(i == 1);
+       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);
        if (error)
                return error;
-       XFS_WANT_CORRUPTED_RETURN(i == 1);
+       XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
 
        left.br_blockcount = blockcount;
 
@@ -5518,50 +5493,92 @@ xfs_bmse_shift_one(
        int                             *current_ext,
        struct xfs_bmbt_rec_host        *gotp,
        struct xfs_btree_cur            *cur,
-       int                             *logflags)
+       int                             *logflags,
+       enum shift_direction            direction)
 {
        struct xfs_ifork                *ifp;
+       struct xfs_mount                *mp;
        xfs_fileoff_t                   startoff;
-       struct xfs_bmbt_rec_host        *leftp;
+       struct xfs_bmbt_rec_host        *adj_irecp;
        struct xfs_bmbt_irec            got;
-       struct xfs_bmbt_irec            left;
+       struct xfs_bmbt_irec            adj_irec;
        int                             error;
        int                             i;
+       int                             total_extents;
 
+       mp = ip->i_mount;
        ifp = XFS_IFORK_PTR(ip, whichfork);
+       total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
 
        xfs_bmbt_get_all(gotp, &got);
-       startoff = got.br_startoff - offset_shift_fsb;
 
        /* delalloc extents should be prevented by caller */
-       XFS_WANT_CORRUPTED_RETURN(!isnullstartblock(got.br_startblock));
+       XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
 
-       /*
-        * 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) {
-               /* grab the left extent and check for a large enough hole */
-               leftp = xfs_iext_get_ext(ifp, *current_ext - 1);
-               xfs_bmbt_get_all(leftp, &left);
+       if (direction == SHIFT_LEFT) {
+               startoff = got.br_startoff - offset_shift_fsb;
+
+               /*
+                * 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);
 
-               if (startoff < left.br_startoff + left.br_blockcount)
+               if (startoff <
+                   adj_irec.br_startoff + adj_irec.br_blockcount)
                        return -EINVAL;
 
                /* check whether to merge the extent or shift it down */
-               if (xfs_bmse_can_merge(&left, &got, offset_shift_fsb)) {
+               if (xfs_bmse_can_merge(&adj_irec, &got,
+                                      offset_shift_fsb)) {
                        return xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
-                                             *current_ext, gotp, leftp, cur,
-                                             logflags);
+                                             *current_ext, gotp, adj_irecp,
+                                             cur, logflags);
                }
-       } else if (got.br_startoff < offset_shift_fsb)
-               return -EINVAL;
-
+       } 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.
         */
-       (*current_ext)++;
+update_current_ext:
+       if (direction == SHIFT_LEFT)
+               (*current_ext)++;
+       else
+               (*current_ext)--;
        xfs_bmbt_set_startoff(gotp, startoff);
        *logflags |= XFS_ILOG_CORE;
        if (!cur) {
@@ -5573,18 +5590,18 @@ xfs_bmse_shift_one(
                                   got.br_blockcount, &i);
        if (error)
                return error;
-       XFS_WANT_CORRUPTED_RETURN(i == 1);
+       XFS_WANT_CORRUPTED_RETURN(mp, i == 1);
 
        got.br_startoff = startoff;
        return xfs_bmbt_update(cur, got.br_startoff, got.br_startblock,
-                               got.br_blockcount, got.br_state);
+                              got.br_blockcount, got.br_state);
 }
 
 /*
- * Shift extent records to the left to cover a hole.
+ * 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. @start_fsb specifies the file offset to start the shift and the
+ * @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
@@ -5594,12 +5611,13 @@ int
 xfs_bmap_shift_extents(
        struct xfs_trans        *tp,
        struct xfs_inode        *ip,
-       xfs_fileoff_t           start_fsb,
+       xfs_fileoff_t           *next_fsb,
        xfs_fileoff_t           offset_shift_fsb,
        int                     *done,
-       xfs_fileoff_t           *next_fsb,
+       xfs_fileoff_t           stop_fsb,
        xfs_fsblock_t           *firstblock,
        struct xfs_bmap_free    *flist,
+       enum shift_direction    direction,
        int                     num_exts)
 {
        struct xfs_btree_cur            *cur = NULL;
@@ -5609,10 +5627,11 @@ xfs_bmap_shift_extents(
        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                             total_extents;
 
        if (unlikely(XFS_TEST_ERROR(
            (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -5628,6 +5647,8 @@ xfs_bmap_shift_extents(
 
        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);
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
        if (!(ifp->if_flags & XFS_IFEXTENTS)) {
@@ -5644,44 +5665,84 @@ xfs_bmap_shift_extents(
                cur->bc_private.b.flags = 0;
        }
 
+       /*
+        * There may be delalloc extents in the data fork before the range we
+        * are collapsing out, so we cannot use the count of real extents here.
+        * Instead we have to calculate it from the incore fork.
+        */
+       total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+       if (total_extents == 0) {
+               *done = 1;
+               goto del_cursor;
+       }
+
+       /*
+        * 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);
+               *next_fsb = got.br_startoff;
+               if (stop_fsb > *next_fsb) {
+                       *done = 1;
+                       goto del_cursor;
+               }
+       }
+
+       /* 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)
-        * start_fsb lies in a hole beyond which there are no extents. Either
+        * *next_fsb lies in a hole beyond which there are no extents. Either
         * way, we are done.
         */
-       gotp = xfs_iext_bno_to_ext(ifp, start_fsb, &current_ext);
+       gotp = xfs_iext_bno_to_ext(ifp, *next_fsb, &current_ext);
        if (!gotp) {
                *done = 1;
                goto del_cursor;
        }
 
-       /*
-        * There may be delalloc extents in the data fork before the range we
-        * are collapsing out, so we cannot use the count of real extents here.
-        * Instead we have to calculate it from the incore fork.
-        */
-       total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
-       while (nexts++ < num_exts && current_ext < total_extents) {
+       /* some sanity checking before we finally start shifting extents */
+       if ((direction == SHIFT_LEFT && current_ext >= stop_extent) ||
+            (direction == SHIFT_RIGHT && 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);
+                                          &current_ext, gotp, cur, &logflags,
+                                          direction);
                if (error)
                        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 = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+                       stop_extent = total_extents;
+               }
 
-               /* update total extent count and grab the next record */
-               total_extents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
-               if (current_ext >= total_extents)
+               if (current_ext == stop_extent) {
+                       *done = 1;
+                       *next_fsb = NULLFSBLOCK;
                        break;
+               }
                gotp = xfs_iext_get_ext(ifp, current_ext);
        }
 
-       /* Check if we are done */
-       if (current_ext == total_extents) {
-               *done = 1;
-       } else if (next_fsb) {
+       if (!*done) {
                xfs_bmbt_get_all(gotp, &got);
                *next_fsb = got.br_startoff;
        }
@@ -5696,3 +5757,189 @@ del_cursor:
 
        return error;
 }
+
+/*
+ * Splits an extent into two extents at split_fsb block such that it is
+ * the first block of the current_ext. @current_ext is a target extent
+ * to be split. @split_fsb is a block where the extents is split.
+ * If split_fsb lies in a hole or the first block of extents, just return 0.
+ */
+STATIC int
+xfs_bmap_split_extent_at(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           split_fsb,
+       xfs_fsblock_t           *firstfsb,
+       struct xfs_bmap_free    *free_list)
+{
+       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;
+       struct xfs_ifork                *ifp;
+       xfs_fsblock_t                   gotblkcnt; /* new block count for got */
+       xfs_extnum_t                    current_ext;
+       int                             error = 0;
+       int                             logflags = 0;
+       int                             i = 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_split_extent_at",
+                                XFS_ERRLEVEL_LOW, mp);
+               return -EFSCORRUPTED;
+       }
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+       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;
+       }
+
+       /*
+        * 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.
+        */
+       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)
+               return 0;
+
+       gotblkcnt = split_fsb - got.br_startoff;
+       new.br_startoff = split_fsb;
+       new.br_startblock = got.br_startblock + gotblkcnt;
+       new.br_blockcount = got.br_blockcount - gotblkcnt;
+       new.br_state = got.br_state;
+
+       if (ifp->if_flags & XFS_IFBROOT) {
+               cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork);
+               cur->bc_private.b.firstblock = *firstfsb;
+               cur->bc_private.b.flist = free_list;
+               cur->bc_private.b.flags = 0;
+               error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
+                               got.br_startblock,
+                               got.br_blockcount,
+                               &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;
+
+       logflags = XFS_ILOG_CORE;
+       if (cur) {
+               error = xfs_bmbt_update(cur, got.br_startoff,
+                               got.br_startblock,
+                               got.br_blockcount,
+                               got.br_state);
+               if (error)
+                       goto del_cursor;
+       } else
+               logflags |= XFS_ILOG_DEXT;
+
+       /* Add new extent */
+       current_ext++;
+       xfs_iext_insert(ip, current_ext, 1, &new, 0);
+       XFS_IFORK_NEXT_SET(ip, whichfork,
+                          XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
+
+       if (cur) {
+               error = xfs_bmbt_lookup_eq(cur, new.br_startoff,
+                               new.br_startblock, new.br_blockcount,
+                               &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;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, del_cursor);
+       }
+
+       /*
+        * Convert to a btree if necessary.
+        */
+       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, firstfsb, free_list,
+                               &cur, 0, &tmp_logflags, whichfork);
+               logflags |= tmp_logflags;
+       }
+
+del_cursor:
+       if (cur) {
+               cur->bc_private.b.allocated = 0;
+               xfs_btree_del_cursor(cur,
+                               error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       }
+
+       if (logflags)
+               xfs_trans_log_inode(tp, ip, logflags);
+       return error;
+}
+
+int
+xfs_bmap_split_extent(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           split_fsb)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       struct xfs_bmap_free    free_list;
+       xfs_fsblock_t           firstfsb;
+       int                     committed;
+       int                     error;
+
+       tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
+       error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write,
+                       XFS_DIOSTRAT_SPACE_RES(mp, 0), 0);
+       if (error) {
+               xfs_trans_cancel(tp, 0);
+               return error;
+       }
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+       xfs_bmap_init(&free_list, &firstfsb);
+
+       error = xfs_bmap_split_extent_at(tp, ip, split_fsb,
+                       &firstfsb, &free_list);
+       if (error)
+               goto out;
+
+       error = xfs_bmap_finish(&tp, &free_list, &committed);
+       if (error)
+               goto out;
+
+       return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
+
+
+out:
+       xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+       return error;
+}