All of lore.kernel.org
 help / color / mirror / Atom feed
* b+tree for the incore extent list
@ 2017-10-31 14:22 Christoph Hellwig
  2017-10-31 14:22 ` [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent Christoph Hellwig
                   ` (18 more replies)
  0 siblings, 19 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Hi all,

this series first updates the incore extent list iteration to use
a cursor based scheme that hides the implementation details, and then
switch to use a b+tree to implement the in-core extent list.  This
reduces the need for a large contiguous allocation that the current
indirection array requires, and thus avoids stalls during workloads
using giant extent lists, especially on systems that are long running.

The algorithms also should be better in general, but due to the fact
the the operations on the on-disk b+tree have such a high overhead
not much that effect is seen on the usual benchmarks.

I also have a git tree available at:

    git://git.infradead.org/users/hch/xfs.git xfs-incore-btree

Gitweb:

    http://git.infradead.org/users/hch/xfs.git/shortlog/refs/heads/xfs-incore-btree

^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 14:22 ` [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real Christoph Hellwig
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

This prepares for getting rid of the current in-memory extent format.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
 fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
 fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
 3 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index f45f05c45e15..b2b6832b9e6b 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1259,14 +1259,14 @@ xfs_iread_extents(
 		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
 		for (j = 0; j < num_recs; j++, i++, frp++) {
 			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
-			trp->l0 = be64_to_cpu(frp->l0);
-			trp->l1 = be64_to_cpu(frp->l1);
-			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
+			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
 				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
 						 XFS_ERRLEVEL_LOW, mp);
 				error = -EFSCORRUPTED;
 				goto out_brelse;
 			}
+			trp->l0 = be64_to_cpu(frp->l0);
+			trp->l1 = be64_to_cpu(frp->l1);
 			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
 		}
 		xfs_trans_brelse(tp, bp);
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
index 6f891eeb88f6..2fbfe2a24b15 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.h
+++ b/fs/xfs/libxfs/xfs_bmap_btree.h
@@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
  * Check that the extent does not contain an invalid unwritten extent flag.
  */
 static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
-		struct xfs_bmbt_rec_host *ep)
+		struct xfs_bmbt_rec *ep)
 {
-	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
+	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
 		return true;
 	if (whichfork == XFS_DATA_FORK &&
 	    xfs_sb_version_hasextflgbit(&mp->m_sb))
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index bb63f38b97cc..abe601b48c9c 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -371,13 +371,13 @@ xfs_iformat_extents(
 		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
 		for (i = 0; i < nex; i++, dp++) {
 			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
-			ep->l0 = get_unaligned_be64(&dp->l0);
-			ep->l1 = get_unaligned_be64(&dp->l1);
-			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
+			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
 				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
 						 XFS_ERRLEVEL_LOW, mp);
 				return -EFSCORRUPTED;
 			}
+			ep->l0 = get_unaligned_be64(&dp->l0);
+			ep->l1 = get_unaligned_be64(&dp->l1);
 			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
 		}
 	}
@@ -764,8 +764,6 @@ xfs_iextents_copy(
 	for (i = 0; i < nrecs; i++) {
 		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
 
-		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
-
 		start_block = xfs_bmbt_get_startblock(ep);
 		if (isnullstartblock(start_block)) {
 			/*
@@ -779,6 +777,7 @@ xfs_iextents_copy(
 		/* Translate to on disk format */
 		put_unaligned_be64(ep->l0, &dp->l0);
 		put_unaligned_be64(ep->l1, &dp->l1);
+		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
 
 		dp++;
 		copied++;
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
  2017-10-31 14:22 ` [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:34   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 03/18] xfs: treat idx as a cursor " Christoph Hellwig
                   ` (16 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Two cases in xfs_bmap_add_extent_delay_real currently insert a new
extent before updating the existing one that is being split.  While
this works fine with a simple extent list, a more complex tree can't
easily cope with overlapping extent.  Reshuffle the code a bit to update
the slot of the existing delalloc extent to the new real extent before
inserting the shortened delalloc extent before or after it.  This
avoids the overlapping extents while still allowing to update the
br_startblock field of the delalloc extent with the updated indirect
block reservation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index b2b6832b9e6b..0eda6892b9d0 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1790,7 +1790,7 @@ xfs_bmap_add_extent_delay_real(
 		 * Filling in the first part of a previous delayed allocation.
 		 * The left neighbor is not contiguous.
 		 */
-		xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
+		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
 		(*nextents)++;
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1823,7 +1823,7 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_startoff = new_endoff;
 		PREV.br_blockcount = temp;
 		PREV.br_startblock = nullstartblock(da_new);
-		xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &PREV);
+		xfs_iext_insert(bma->ip, bma->idx + 1, 1, &PREV, state);
 		break;
 
 	case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -1866,7 +1866,7 @@ xfs_bmap_add_extent_delay_real(
 		 * Filling in the last part of a previous delayed allocation.
 		 * The right neighbor is not contiguous.
 		 */
-		xfs_iext_insert(bma->ip, bma->idx + 1, 1, new, state);
+		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
 		(*nextents)++;
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1898,7 +1898,7 @@ xfs_bmap_add_extent_delay_real(
 
 		PREV.br_startblock = nullstartblock(da_new);
 		PREV.br_blockcount = temp;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+		xfs_iext_insert(bma->ip, bma->idx, 1, &PREV, state);
 
 		bma->idx++;
 		break;
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 03/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_delay_real
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
  2017-10-31 14:22 ` [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent Christoph Hellwig
  2017-10-31 14:22 ` [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:35   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay Christoph Hellwig
                   ` (15 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Stop poking before and after the index and just increment or decrement
it while doing our operations on it to prepare for a new extent list
implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 0eda6892b9d0..83f5a503dce1 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1647,12 +1647,13 @@ xfs_bmap_add_extent_delay_real(
 		 * Filling in all of a previously delayed allocation extent.
 		 * The left and right neighbors are both contiguous with new.
 		 */
-		bma->idx--;
 		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
 
-		xfs_iext_remove(bma->ip, bma->idx + 1, 2, state);
+		xfs_iext_remove(bma->ip, bma->idx, 2, state);
+		bma->idx--;
+		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
 		(*nextents)--;
+
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
 		else {
@@ -1680,13 +1681,13 @@ xfs_bmap_add_extent_delay_real(
 		 * Filling in all of a previously delayed allocation extent.
 		 * The left neighbor is contiguous, the right is not.
 		 */
-		bma->idx--;
-
 		old = LEFT;
 		LEFT.br_blockcount += PREV.br_blockcount;
+
+		xfs_iext_remove(bma->ip, bma->idx, 1, state);
+		bma->idx--;
 		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
 
-		xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
 		else {
@@ -1708,9 +1709,12 @@ xfs_bmap_add_extent_delay_real(
 		 */
 		PREV.br_startblock = new->br_startblock;
 		PREV.br_blockcount += RIGHT.br_blockcount;
+
+		bma->idx++;
+		xfs_iext_remove(bma->ip, bma->idx, 1, state);
+		bma->idx--;
 		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 
-		xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
 		else {
@@ -1762,12 +1766,14 @@ xfs_bmap_add_extent_delay_real(
 				startblockval(PREV.br_startblock));
 
 		LEFT.br_blockcount += new->br_blockcount;
-		xfs_iext_update_extent(bma->ip, state, bma->idx - 1, &LEFT);
 
 		PREV.br_blockcount = temp = PREV.br_blockcount - new->br_blockcount;
 		PREV.br_startoff += new->br_blockcount;
 		PREV.br_startblock = nullstartblock(da_new);
+
 		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+		bma->idx--;
+		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
 
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -1782,7 +1788,6 @@ xfs_bmap_add_extent_delay_real(
 				goto done;
 		}
 
-		bma->idx--;
 		break;
 
 	case BMAP_LEFT_FILLING:
@@ -1835,7 +1840,6 @@ xfs_bmap_add_extent_delay_real(
 		RIGHT.br_startoff = new->br_startoff;
 		RIGHT.br_startblock = new->br_startblock;
 		RIGHT.br_blockcount += new->br_blockcount;
-		xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &RIGHT);
 
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -1856,9 +1860,10 @@ xfs_bmap_add_extent_delay_real(
 
 		PREV.br_blockcount = temp;
 		PREV.br_startblock = nullstartblock(da_new);
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 
+		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
 		bma->idx++;
+		xfs_iext_update_extent(bma->ip, state, bma->idx, &RIGHT);
 		break;
 
 	case BMAP_RIGHT_FILLING:
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (2 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 03/18] xfs: treat idx as a cursor " Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:35   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real Christoph Hellwig
                   ` (14 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Stop poking before and after the index and just increment or decrement
it while doing our operations on it to prepare for a new extent list
implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 83f5a503dce1..6358b30b70f9 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2583,7 +2583,6 @@ xfs_bmap_add_extent_hole_delay(
 		 * on the left and on the right.
 		 * Merge all three into a single extent record.
 		 */
-		--*idx;
 		temp = left.br_blockcount + new->br_blockcount +
 			right.br_blockcount;
 
@@ -2594,9 +2593,10 @@ xfs_bmap_add_extent_hole_delay(
 					 oldlen);
 		left.br_startblock = nullstartblock(newlen);
 		left.br_blockcount = temp;
-		xfs_iext_update_extent(ip, state, *idx, &left);
 
-		xfs_iext_remove(ip, *idx + 1, 1, state);
+		xfs_iext_remove(ip, *idx, 1, state);
+		--*idx;
+		xfs_iext_update_extent(ip, state, *idx, &left);
 		break;
 
 	case BMAP_LEFT_CONTIG:
@@ -2605,7 +2605,6 @@ xfs_bmap_add_extent_hole_delay(
 		 * on the left.
 		 * Merge the new allocation with the left neighbor.
 		 */
-		--*idx;
 		temp = left.br_blockcount + new->br_blockcount;
 
 		oldlen = startblockval(left.br_startblock) +
@@ -2614,6 +2613,8 @@ xfs_bmap_add_extent_hole_delay(
 					 oldlen);
 		left.br_blockcount = temp;
 		left.br_startblock = nullstartblock(newlen);
+
+		--*idx;
 		xfs_iext_update_extent(ip, state, *idx, &left);
 		break;
 
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (3 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:35   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real Christoph Hellwig
                   ` (13 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Stop poking before and after the index and just increment or decrement
it while doing our operations on it to prepare for a new extent list
implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 6358b30b70f9..7bd27c08981f 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2741,11 +2741,11 @@ xfs_bmap_add_extent_hole_real(
 		 * left and on the right.
 		 * Merge all three into a single extent record.
 		 */
-		--*idx;
 		left.br_blockcount += new->br_blockcount + right.br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &left);
 
-		xfs_iext_remove(ip, *idx + 1, 1, state);
+		xfs_iext_remove(ip, *idx, 1, state);
+		--*idx;
+		xfs_iext_update_extent(ip, state, *idx, &left);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
@@ -2777,10 +2777,10 @@ xfs_bmap_add_extent_hole_real(
 		 * on the left.
 		 * Merge the new allocation with the left neighbor.
 		 */
-		--*idx;
 		old = left;
-
 		left.br_blockcount += new->br_blockcount;
+
+		--*idx;
 		xfs_iext_update_extent(ip, state, *idx, &left);
 
 		if (cur == NULL) {
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (4 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:36   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_* Christoph Hellwig
                   ` (12 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Stop poking before and after the index and just increment or decrement
it while doing our operations on it to prepare for a new extent list
implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 7bd27c08981f..10878c495869 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -2151,12 +2151,11 @@ xfs_bmap_add_extent_unwritten_real(
 		 * Setting all of a previous oldext extent to newext.
 		 * The left and right neighbors are both contiguous with new.
 		 */
-		--*idx;
-
 		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &LEFT);
 
-		xfs_iext_remove(ip, *idx + 1, 2, state);
+		xfs_iext_remove(ip, *idx, 2, state);
+		--*idx;
+		xfs_iext_update_extent(ip, state, *idx, &LEFT);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) - 2);
 		if (cur == NULL)
@@ -2190,12 +2189,11 @@ xfs_bmap_add_extent_unwritten_real(
 		 * Setting all of a previous oldext extent to newext.
 		 * The left neighbor is contiguous, the right is not.
 		 */
-		--*idx;
-
 		LEFT.br_blockcount += PREV.br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &LEFT);
 
-		xfs_iext_remove(ip, *idx + 1, 1, state);
+		xfs_iext_remove(ip, *idx, 1, state);
+		--*idx;
+		xfs_iext_update_extent(ip, state, *idx, &LEFT);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
 		if (cur == NULL)
@@ -2225,9 +2223,12 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		PREV.br_blockcount += RIGHT.br_blockcount;
 		PREV.br_state = new->br_state;
+
+		++*idx;
+		xfs_iext_remove(ip, *idx, 1, state);
+		--*idx;
 		xfs_iext_update_extent(ip, state, *idx, &PREV);
 
-		xfs_iext_remove(ip, *idx + 1, 1, state);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
 		if (cur == NULL)
@@ -2279,15 +2280,15 @@ xfs_bmap_add_extent_unwritten_real(
 		 * The left neighbor is contiguous.
 		 */
 		LEFT.br_blockcount += new->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx - 1, &LEFT);
 
 		old = PREV;
 		PREV.br_startoff += new->br_blockcount;
 		PREV.br_startblock += new->br_blockcount;
 		PREV.br_blockcount -= new->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
 
+		xfs_iext_update_extent(ip, state, *idx, &PREV);
 		--*idx;
+		xfs_iext_update_extent(ip, state, *idx, &LEFT);
 
 		if (cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -2318,8 +2319,8 @@ xfs_bmap_add_extent_unwritten_real(
 		PREV.br_startoff += new->br_blockcount;
 		PREV.br_startblock += new->br_blockcount;
 		PREV.br_blockcount -= new->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
 
+		xfs_iext_update_extent(ip, state, *idx, &PREV);
 		xfs_iext_insert(ip, *idx, 1, new, state);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
@@ -2348,13 +2349,13 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		old = PREV;
 		PREV.br_blockcount -= new->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
-
-		++*idx;
 
 		RIGHT.br_startoff = new->br_startoff;
 		RIGHT.br_startblock = new->br_startblock;
 		RIGHT.br_blockcount += new->br_blockcount;
+
+		xfs_iext_update_extent(ip, state, *idx, &PREV);
+		++*idx;
 		xfs_iext_update_extent(ip, state, *idx, &RIGHT);
 
 		if (cur == NULL)
@@ -2384,8 +2385,8 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		old = PREV;
 		PREV.br_blockcount -= new->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
 
+		xfs_iext_update_extent(ip, state, *idx, &PREV);
 		++*idx;
 		xfs_iext_insert(ip, *idx, 1, new, state);
 
@@ -2420,7 +2421,6 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		old = PREV;
 		PREV.br_blockcount = new->br_startoff - PREV.br_startoff;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
 
 		r[0] = *new;
 		r[1].br_startoff = new_endoff;
@@ -2429,6 +2429,7 @@ xfs_bmap_add_extent_unwritten_real(
 		r[1].br_startblock = new->br_startblock + new->br_blockcount;
 		r[1].br_state = PREV.br_state;
 
+		xfs_iext_update_extent(ip, state, *idx, &PREV);
 		++*idx;
 		xfs_iext_insert(ip, *idx, 2, &r[0], state);
 
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_*
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (5 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:37   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents Christoph Hellwig
                   ` (11 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Stop poking before and after the index and just increment or decrement
it while doing our operations on it to prepare for a new extent list
implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 10878c495869..5ba0c0368629 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -4749,12 +4749,12 @@ xfs_bmap_del_extent_delay(
 						       del->br_blockcount);
 
 		got->br_startblock = nullstartblock((int)got_indlen);
-		xfs_iext_update_extent(ip, state, *idx, got);
 
 		new.br_startoff = del_endoff;
 		new.br_state = got->br_state;
 		new.br_startblock = nullstartblock((int)new_indlen);
 
+		xfs_iext_update_extent(ip, state, *idx, got);
 		++*idx;
 		xfs_iext_insert(ip, *idx, 1, &new, state);
 
@@ -4831,13 +4831,13 @@ xfs_bmap_del_extent_cow(
 		 * Deleting the middle of the extent.
 		 */
 		got->br_blockcount = del->br_startoff - got->br_startoff;
-		xfs_iext_update_extent(ip, state, *idx, got);
 
 		new.br_startoff = del_endoff;
 		new.br_blockcount = got_endoff - del_endoff;
 		new.br_state = got->br_state;
 		new.br_startblock = del->br_startblock + del->br_blockcount;
 
+		xfs_iext_update_extent(ip, state, *idx, got);
 		++*idx;
 		xfs_iext_insert(ip, *idx, 1, &new, state);
 		break;
@@ -5053,8 +5053,8 @@ xfs_bmap_del_extent_real(
 			flags |= xfs_ilog_fext(whichfork);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
-		xfs_iext_insert(ip, *idx + 1, 1, &new, state);
 		++*idx;
+		xfs_iext_insert(ip, *idx, 1, &new, state);
 		break;
 	}
 
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (6 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_* Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:37   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all Christoph Hellwig
                   ` (10 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Stop poking before and after the index and just increment or decrement
it while doing our operations on it to prepare for a new extent list
implementation.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 5ba0c0368629..14428d72cf33 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -5516,7 +5516,7 @@ xfs_bmse_merge(
 	struct xfs_inode		*ip,
 	int				whichfork,
 	xfs_fileoff_t			shift,		/* shift fsb */
-	int				current_ext,	/* idx of gotp */
+	int				*current_ext,	/* idx of gotp */
 	struct xfs_bmbt_irec		*got,		/* extent to shift */
 	struct xfs_bmbt_irec		*left,		/* preceding extent */
 	struct xfs_btree_cur		*cur,
@@ -5571,9 +5571,10 @@ xfs_bmse_merge(
 		return error;
 
 done:
+	xfs_iext_remove(ip, *current_ext, 1, 0);
+	--*current_ext;
 	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
-			current_ext - 1, &new);
-	xfs_iext_remove(ip, current_ext, 1, 0);
+			*current_ext, &new);
 
 	/* update reverse mapping. rmap functions merge the rmaps for us */
 	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, got);
@@ -5687,16 +5688,10 @@ xfs_bmap_collapse_extents(
 
 		if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) {
 			error = xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
-					current_ext, &got, &prev, cur,
+					&current_ext, &got, &prev, cur,
 					&logflags, dfops);
 			if (error)
 				goto del_cursor;
-
-			/* update got after merge */
-			if (!xfs_iext_get_extent(ifp, current_ext, &got)) {
-				*done = true;
-				goto del_cursor;
-			}
 			goto done;
 		}
 	} else {
@@ -5711,12 +5706,12 @@ xfs_bmap_collapse_extents(
 	if (error)
 		goto del_cursor;
 
+done:
 	if (!xfs_iext_get_extent(ifp, ++current_ext, &got)) {
 		 *done = true;
 		 goto del_cursor;
 	}
 
-done:
 	*next_fsb = got.br_startoff;
 del_cursor:
 	if (cur)
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (7 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 21:34   ` Darrick J. Wong
  2017-11-02 13:54   ` Brian Foster
  2017-10-31 14:22 ` [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy Christoph Hellwig
                   ` (9 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

To make life a little simpler make xfs_bmbt_set_all unaligned access
aware so that we can use it directly on the destination buffer.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap_btree.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 086e6fc8e4fc..89260972a0f6 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -199,14 +199,14 @@ xfs_bmbt_disk_set_all(
 	ASSERT(!(s->br_blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)));
 	ASSERT(!(s->br_startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)));
 
-	r->l0 = cpu_to_be64(
+	put_unaligned_be64(
 		((xfs_bmbt_rec_base_t)extent_flag << 63) |
 		 ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
-		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43));
-	r->l1 = cpu_to_be64(
+		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43), &r->l0);
+	put_unaligned_be64(
 		((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
 		 ((xfs_bmbt_rec_base_t)s->br_blockcount &
-		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21)));
+		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21)), &r->l1);
 }
 
 /*
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (8 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 21:41   ` Darrick J. Wong
  2017-11-02 13:54   ` Brian Foster
  2017-10-31 14:22 ` [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree Christoph Hellwig
                   ` (8 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

This actually makes the function very slightly less efficient for now as we
detour through the expanded irect format between the in-core extent format
and the on-disk one instead of just endian swapping them.  But with the
incore extent btree the in-core one will use a different format and the
representation will be entirely hidden.  It also happens to make the
function a whole more readable.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_inode_fork.c | 53 +++++++++++-------------------------------
 1 file changed, 13 insertions(+), 40 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index abe601b48c9c..7dd77b497fc2 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -725,9 +725,6 @@ xfs_iext_count(struct xfs_ifork *ifp)
 /*
  * Convert in-core extents to on-disk form
  *
- * For either the data or attr fork in extent format, we need to endian convert
- * the in-core extent as we place them into the on-disk inode.
- *
  * In the case of the data fork, the in-core and on-disk fork sizes can be
  * different due to delayed allocation extents. We only copy on-disk extents
  * here, so callers must always use the physical fork size to determine the
@@ -736,55 +733,31 @@ xfs_iext_count(struct xfs_ifork *ifp)
  */
 int
 xfs_iextents_copy(
-	xfs_inode_t		*ip,
-	xfs_bmbt_rec_t		*dp,
+	struct xfs_inode	*ip,
+	struct xfs_bmbt_rec	*dp,
 	int			whichfork)
 {
 	int			state = xfs_bmap_fork_to_state(whichfork);
-	int			copied;
-	int			i;
-	xfs_ifork_t		*ifp;
-	int			nrecs;
-	xfs_fsblock_t		start_block;
+	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
+	struct xfs_bmbt_irec	rec;
+	int			copied = 0, i = 0;
 
-	ifp = XFS_IFORK_PTR(ip, whichfork);
-	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
+	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
 	ASSERT(ifp->if_bytes > 0);
 
-	nrecs = xfs_iext_count(ifp);
-	ASSERT(nrecs > 0);
-
-	/*
-	 * There are some delayed allocation extents in the
-	 * inode, so copy the extents one at a time and skip
-	 * the delayed ones.  There must be at least one
-	 * non-delayed extent.
-	 */
-	copied = 0;
-	for (i = 0; i < nrecs; i++) {
-		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
-
-		start_block = xfs_bmbt_get_startblock(ep);
-		if (isnullstartblock(start_block)) {
-			/*
-			 * It's a delayed allocation extent, so skip it.
-			 */
+	while (xfs_iext_get_extent(ifp, i++, &rec)) {
+		if (isnullstartblock(rec.br_startblock))
 			continue;
-		}
-
+		xfs_bmbt_disk_set_all(dp, &rec);
 		trace_xfs_write_extent(ip, i, state, _RET_IP_);
-
-		/* Translate to on disk format */
-		put_unaligned_be64(ep->l0, &dp->l0);
-		put_unaligned_be64(ep->l1, &dp->l1);
 		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
-
+		copied += sizeof(struct xfs_bmbt_rec);
 		dp++;
-		copied++;
 	}
-	ASSERT(copied != 0);
 
-	return (copied * (uint)sizeof(xfs_bmbt_rec_t));
+	ASSERT(copied > 0);
+	ASSERT(copied <= ifp->if_bytes);
+	return copied;
 }
 
 /*
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (9 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 21:41   ` Darrick J. Wong
  2017-11-02 13:54   ` Brian Foster
  2017-10-31 14:22 ` [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction Christoph Hellwig
                   ` (7 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

This actually makes the function very slightly less efficient for now as we
detour through the expanded irect format between the in-core extent format
and the on-disk one instead of just endian swapping them.  But with the
incore extent btree the in-core one will use a different format and the
representation will be entirely hidden.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 20 ++++++++------------
 1 file changed, 8 insertions(+), 12 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 14428d72cf33..56482bf6280d 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -666,14 +666,13 @@ xfs_bmap_extents_to_btree(
 	xfs_bmbt_rec_t		*arp;		/* child record pointer */
 	struct xfs_btree_block	*block;		/* btree root block */
 	xfs_btree_cur_t		*cur;		/* bmap btree cursor */
-	xfs_bmbt_rec_host_t	*ep;		/* extent record pointer */
 	int			error;		/* error return value */
-	xfs_extnum_t		i, cnt;		/* extent record index */
 	xfs_ifork_t		*ifp;		/* inode fork pointer */
 	xfs_bmbt_key_t		*kp;		/* root block key pointer */
 	xfs_mount_t		*mp;		/* mount structure */
-	xfs_extnum_t		nextents;	/* number of file extents */
 	xfs_bmbt_ptr_t		*pp;		/* root block address pointer */
+	struct xfs_bmbt_irec	rec;
+	xfs_extnum_t		i = 0, cnt = 0;
 
 	mp = ip->i_mount;
 	ASSERT(whichfork != XFS_COW_FORK);
@@ -752,15 +751,12 @@ xfs_bmap_extents_to_btree(
 				XFS_BTNUM_BMAP, 0, 0, ip->i_ino,
 				XFS_BTREE_LONG_PTRS);
 
-	arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
-	nextents =  xfs_iext_count(ifp);
-	for (cnt = i = 0; i < nextents; i++) {
-		ep = xfs_iext_get_ext(ifp, i);
-		if (!isnullstartblock(xfs_bmbt_get_startblock(ep))) {
-			arp->l0 = cpu_to_be64(ep->l0);
-			arp->l1 = cpu_to_be64(ep->l1);
-			arp++; cnt++;
-		}
+	while (xfs_iext_get_extent(ifp, i++, &rec)) {
+		if (isnullstartblock(rec.br_startblock))
+			continue;
+		arp = XFS_BMBT_REC_ADDR(mp, ablock, 1 + cnt);
+		xfs_bmbt_disk_set_all(arp, &rec);
+		cnt++;
 	}
 	ASSERT(cnt == XFS_IFORK_NEXTENTS(ip, whichfork));
 	xfs_btree_set_numrecs(ablock, cnt);
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (10 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 22:02   ` Darrick J. Wong
  2017-11-02 17:14   ` Brian Foster
  2017-10-31 14:22 ` [PATCH 13/18] xfs: iterate backwards in xfs_reflink_cancel_cow_blocks Christoph Hellwig
                   ` (6 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Add a new xfs_iext_cursor structure to hide the direct extent map
index manipulations. In addition to the existing lookup/get/insert/
remove and update routines new primitives to get the first and last
extent cursor, as well as moving up and down by one extent are
provided.  Also new are convenience to increment/decrement the
cursor and retreive the new extent, as well as to peek into the
previous/next extent without updating the cursor and last but not
least a macro to iterate over all extents in a fork.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c       | 432 ++++++++++++++++++++---------------------
 fs/xfs/libxfs/xfs_bmap.h       |  12 +-
 fs/xfs/libxfs/xfs_inode_fork.c |  75 +++----
 fs/xfs/libxfs/xfs_inode_fork.h |  87 ++++++++-
 fs/xfs/libxfs/xfs_types.h      |   3 +
 fs/xfs/scrub/bmap.c            |   6 +-
 fs/xfs/scrub/dir.c             |  14 +-
 fs/xfs/xfs_bmap_util.c         |  12 +-
 fs/xfs/xfs_dir2_readdir.c      |   8 +-
 fs/xfs/xfs_dquot.c             |   4 +-
 fs/xfs/xfs_iomap.c             |  13 +-
 fs/xfs/xfs_reflink.c           |  56 +++---
 fs/xfs/xfs_trace.h             |  12 +-
 13 files changed, 401 insertions(+), 333 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 56482bf6280d..453dc1ae76ab 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -671,8 +671,9 @@ xfs_bmap_extents_to_btree(
 	xfs_bmbt_key_t		*kp;		/* root block key pointer */
 	xfs_mount_t		*mp;		/* mount structure */
 	xfs_bmbt_ptr_t		*pp;		/* root block address pointer */
+	struct xfs_iext_cursor	ext;
 	struct xfs_bmbt_irec	rec;
-	xfs_extnum_t		i = 0, cnt = 0;
+	xfs_extnum_t		cnt = 0;
 
 	mp = ip->i_mount;
 	ASSERT(whichfork != XFS_COW_FORK);
@@ -751,7 +752,7 @@ xfs_bmap_extents_to_btree(
 				XFS_BTNUM_BMAP, 0, 0, ip->i_ino,
 				XFS_BTREE_LONG_PTRS);
 
-	while (xfs_iext_get_extent(ifp, i++, &rec)) {
+	for_each_iext(ifp, &ext, &rec) {
 		if (isnullstartblock(rec.br_startblock))
 			continue;
 		arp = XFS_BMBT_REC_ADDR(mp, ablock, 1 + cnt);
@@ -827,6 +828,7 @@ xfs_bmap_local_to_extents(
 	xfs_alloc_arg_t	args;		/* allocation arguments */
 	xfs_buf_t	*bp;		/* buffer for extent block */
 	struct xfs_bmbt_irec rec;
+	struct xfs_iext_cursor ext;
 
 	/*
 	 * We don't want to deal with the case of keeping inode data inline yet.
@@ -893,7 +895,8 @@ xfs_bmap_local_to_extents(
 	rec.br_startblock = args.fsbno;
 	rec.br_blockcount = 1;
 	rec.br_state = XFS_EXT_NORM;
-	xfs_iext_insert(ip, 0, 1, &rec, 0);
+	xfs_iext_first(ifp, &ext);
+	xfs_iext_insert(ip, &ext, 1, &rec, 0);
 
 	XFS_IFORK_NEXT_SET(ip, whichfork, 1);
 	ip->i_d.di_nblocks = 1;
@@ -1173,6 +1176,7 @@ xfs_iread_extents(
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
 	xfs_extnum_t		nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
 	struct xfs_btree_block	*block = ifp->if_broot;
+	struct xfs_iext_cursor	ext;
 	xfs_fsblock_t		bno;
 	struct xfs_buf		*bp;
 	xfs_extnum_t		i, j;
@@ -1222,6 +1226,7 @@ xfs_iread_extents(
 	 * Here with bp and block set to the leftmost leaf node in the tree.
 	 */
 	i = 0;
+	xfs_iext_first(ifp, &ext);
 
 	/*
 	 * Loop over all leaf nodes.  Copy information to the extent records.
@@ -1263,7 +1268,8 @@ xfs_iread_extents(
 			}
 			trp->l0 = be64_to_cpu(frp->l0);
 			trp->l1 = be64_to_cpu(frp->l1);
-			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
+			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
+			xfs_iext_next(ifp, &ext);
 		}
 		xfs_trans_brelse(tp, bp);
 		bno = nextbno;
@@ -1311,7 +1317,7 @@ xfs_bmap_first_unused(
 {
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
 	struct xfs_bmbt_irec	got;
-	xfs_extnum_t		idx = 0;
+	struct xfs_iext_cursor	ext;
 	xfs_fileoff_t		lastaddr = 0;
 	xfs_fileoff_t		lowest, max;
 	int			error;
@@ -1332,7 +1338,7 @@ xfs_bmap_first_unused(
 	}
 
 	lowest = max = *first_unused;
-	while (xfs_iext_get_extent(ifp, idx++, &got)) {
+	for_each_iext(ifp, &ext, &got) {
 		/*
 		 * See if the hole before this extent will work.
 		 */
@@ -1362,7 +1368,7 @@ xfs_bmap_last_before(
 {
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
 	struct xfs_bmbt_irec	got;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 	int			error;
 
 	switch (XFS_IFORK_FORMAT(ip, whichfork)) {
@@ -1382,7 +1388,7 @@ xfs_bmap_last_before(
 			return error;
 	}
 
-	if (!xfs_iext_lookup_extent_before(ip, ifp, last_block, &idx, &got))
+	if (!xfs_iext_lookup_extent_before(ip, ifp, last_block, &ext, &got))
 		*last_block = 0;
 	return 0;
 }
@@ -1396,8 +1402,8 @@ xfs_bmap_last_extent(
 	int			*is_empty)
 {
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
+	struct xfs_iext_cursor	ext;
 	int			error;
-	int			nextents;
 
 	if (!(ifp->if_flags & XFS_IFEXTENTS)) {
 		error = xfs_iread_extents(tp, ip, whichfork);
@@ -1405,14 +1411,11 @@ xfs_bmap_last_extent(
 			return error;
 	}
 
-	nextents = xfs_iext_count(ifp);
-	if (nextents == 0) {
+	xfs_iext_last(ifp, &ext);
+	if (!xfs_iext_get_extent(ifp, &ext, rec))
 		*is_empty = 1;
-		return 0;
-	}
-
-	xfs_iext_get_extent(ifp, nextents - 1, rec);
-	*is_empty = 0;
+	else
+		*is_empty = 0;
 	return 0;
 }
 
@@ -1500,6 +1503,7 @@ xfs_bmap_one_block(
 	xfs_ifork_t	*ifp;		/* inode fork pointer */
 	int		rval;		/* return value */
 	xfs_bmbt_irec_t	s;		/* internal version of extent */
+	struct xfs_iext_cursor ext;
 
 #ifndef DEBUG
 	if (whichfork == XFS_DATA_FORK)
@@ -1511,7 +1515,8 @@ xfs_bmap_one_block(
 		return 0;
 	ifp = XFS_IFORK_PTR(ip, whichfork);
 	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
-	xfs_iext_get_extent(ifp, 0, &s);
+	xfs_iext_first(ifp, &ext);
+	xfs_iext_get_extent(ifp, &ext, &s);
 	rval = s.br_startoff == 0 && s.br_blockcount == 1;
 	if (rval && whichfork == XFS_DATA_FORK)
 		ASSERT(XFS_ISIZE(ip) == ip->i_mount->m_sb.sb_blocksize);
@@ -1553,8 +1558,6 @@ xfs_bmap_add_extent_delay_real(
 	nextents = (whichfork == XFS_COW_FORK ? &bma->ip->i_cnextents :
 						&bma->ip->i_d.di_nextents);
 
-	ASSERT(bma->idx >= 0);
-	ASSERT(bma->idx <= xfs_iext_count(ifp));
 	ASSERT(!isnullstartblock(new->br_startblock));
 	ASSERT(!bma->cur ||
 	       (bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
@@ -1568,7 +1571,7 @@ xfs_bmap_add_extent_delay_real(
 	/*
 	 * Set up a bunch of variables to make the tests simpler.
 	 */
-	xfs_iext_get_extent(ifp, bma->idx, &PREV);
+	xfs_iext_get_extent(ifp, &bma->ext, &PREV);
 	new_endoff = new->br_startoff + new->br_blockcount;
 	ASSERT(isnullstartblock(PREV.br_startblock));
 	ASSERT(PREV.br_startoff <= new->br_startoff);
@@ -1590,10 +1593,8 @@ xfs_bmap_add_extent_delay_real(
 	 * Check and set flags if this segment has a left neighbor.
 	 * Don't set contiguous if the combined extent would be too large.
 	 */
-	if (bma->idx > 0) {
+	if (xfs_iext_peek_prev_extent(ifp, &bma->ext, &LEFT)) {
 		state |= BMAP_LEFT_VALID;
-		xfs_iext_get_extent(ifp, bma->idx - 1, &LEFT);
-
 		if (isnullstartblock(LEFT.br_startblock))
 			state |= BMAP_LEFT_DELAY;
 	}
@@ -1610,10 +1611,8 @@ xfs_bmap_add_extent_delay_real(
 	 * Don't set contiguous if the combined extent would be too large.
 	 * Also check for all-three-contiguous being too large.
 	 */
-	if (bma->idx < xfs_iext_count(ifp) - 1) {
+	if (xfs_iext_peek_next_extent(ifp, &bma->ext, &RIGHT)) {
 		state |= BMAP_RIGHT_VALID;
-		xfs_iext_get_extent(ifp, bma->idx + 1, &RIGHT);
-
 		if (isnullstartblock(RIGHT.br_startblock))
 			state |= BMAP_RIGHT_DELAY;
 	}
@@ -1645,9 +1644,9 @@ xfs_bmap_add_extent_delay_real(
 		 */
 		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
 
-		xfs_iext_remove(bma->ip, bma->idx, 2, state);
-		bma->idx--;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
+		xfs_iext_remove(bma->ip, &bma->ext, 2, state);
+		xfs_iext_prev(ifp, &bma->ext);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
 		(*nextents)--;
 
 		if (bma->cur == NULL)
@@ -1680,9 +1679,9 @@ xfs_bmap_add_extent_delay_real(
 		old = LEFT;
 		LEFT.br_blockcount += PREV.br_blockcount;
 
-		xfs_iext_remove(bma->ip, bma->idx, 1, state);
-		bma->idx--;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
+		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
+		xfs_iext_prev(ifp, &bma->ext);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
 
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -1706,10 +1705,10 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_startblock = new->br_startblock;
 		PREV.br_blockcount += RIGHT.br_blockcount;
 
-		bma->idx++;
-		xfs_iext_remove(bma->ip, bma->idx, 1, state);
-		bma->idx--;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+		xfs_iext_next(ifp, &bma->ext);
+		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
+		xfs_iext_prev(ifp, &bma->ext);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
 
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -1733,7 +1732,7 @@ xfs_bmap_add_extent_delay_real(
 		 */
 		PREV.br_startblock = new->br_startblock;
 		PREV.br_state = new->br_state;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
 
 		(*nextents)++;
 		if (bma->cur == NULL)
@@ -1767,9 +1766,9 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_startoff += new->br_blockcount;
 		PREV.br_startblock = nullstartblock(da_new);
 
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
-		bma->idx--;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
+		xfs_iext_prev(ifp, &bma->ext);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
 
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -1783,7 +1782,6 @@ xfs_bmap_add_extent_delay_real(
 			if (error)
 				goto done;
 		}
-
 		break;
 
 	case BMAP_LEFT_FILLING:
@@ -1791,7 +1789,7 @@ xfs_bmap_add_extent_delay_real(
 		 * Filling in the first part of a previous delayed allocation.
 		 * The left neighbor is not contiguous.
 		 */
-		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, new);
 		(*nextents)++;
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1824,7 +1822,9 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_startoff = new_endoff;
 		PREV.br_blockcount = temp;
 		PREV.br_startblock = nullstartblock(da_new);
-		xfs_iext_insert(bma->ip, bma->idx + 1, 1, &PREV, state);
+		xfs_iext_next(ifp, &bma->ext);
+		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
+		xfs_iext_prev(ifp, &bma->ext);
 		break;
 
 	case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
@@ -1857,9 +1857,9 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_blockcount = temp;
 		PREV.br_startblock = nullstartblock(da_new);
 
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
-		bma->idx++;
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &RIGHT);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
+		xfs_iext_next(ifp, &bma->ext);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &RIGHT);
 		break;
 
 	case BMAP_RIGHT_FILLING:
@@ -1867,7 +1867,7 @@ xfs_bmap_add_extent_delay_real(
 		 * Filling in the last part of a previous delayed allocation.
 		 * The right neighbor is not contiguous.
 		 */
-		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, new);
 		(*nextents)++;
 		if (bma->cur == NULL)
 			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
@@ -1899,9 +1899,8 @@ xfs_bmap_add_extent_delay_real(
 
 		PREV.br_startblock = nullstartblock(da_new);
 		PREV.br_blockcount = temp;
-		xfs_iext_insert(bma->ip, bma->idx, 1, &PREV, state);
-
-		bma->idx++;
+		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
+		xfs_iext_next(ifp, &bma->ext);
 		break;
 
 	case 0:
@@ -1944,10 +1943,11 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_startblock =
 			nullstartblock(xfs_bmap_worst_indlen(bma->ip,
 					PREV.br_blockcount));
-		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
+		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
 
 		/* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
-		xfs_iext_insert(bma->ip, bma->idx + 1, 2, &LEFT, state);
+		xfs_iext_next(ifp, &bma->ext);
+		xfs_iext_insert(bma->ip, &bma->ext, 2, &LEFT, state);
 		(*nextents)++;
 
 		if (bma->cur == NULL)
@@ -1975,7 +1975,6 @@ xfs_bmap_add_extent_delay_real(
 
 		da_new = startblockval(PREV.br_startblock) +
 			 startblockval(RIGHT.br_startblock);
-		bma->idx++;
 		break;
 
 	case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
@@ -2039,7 +2038,7 @@ xfs_bmap_add_extent_unwritten_real(
 	struct xfs_trans	*tp,
 	xfs_inode_t		*ip,	/* incore inode pointer */
 	int			whichfork,
-	xfs_extnum_t		*idx,	/* extent number to update/insert */
+	struct xfs_iext_cursor	*ext,
 	xfs_btree_cur_t		**curp,	/* if *curp is null, not a btree */
 	xfs_bmbt_irec_t		*new,	/* new data to add to file extents */
 	xfs_fsblock_t		*first,	/* pointer to firstblock variable */
@@ -2063,8 +2062,6 @@ xfs_bmap_add_extent_unwritten_real(
 	cur = *curp;
 	ifp = XFS_IFORK_PTR(ip, whichfork);
 
-	ASSERT(*idx >= 0);
-	ASSERT(*idx <= xfs_iext_count(ifp));
 	ASSERT(!isnullstartblock(new->br_startblock));
 
 	XFS_STATS_INC(mp, xs_add_exlist);
@@ -2077,7 +2074,7 @@ xfs_bmap_add_extent_unwritten_real(
 	 * Set up a bunch of variables to make the tests simpler.
 	 */
 	error = 0;
-	xfs_iext_get_extent(ifp, *idx, &PREV);
+	xfs_iext_get_extent(ifp, ext, &PREV);
 	ASSERT(new->br_state != PREV.br_state);
 	new_endoff = new->br_startoff + new->br_blockcount;
 	ASSERT(PREV.br_startoff <= new->br_startoff);
@@ -2096,10 +2093,8 @@ xfs_bmap_add_extent_unwritten_real(
 	 * Check and set flags if this segment has a left neighbor.
 	 * Don't set contiguous if the combined extent would be too large.
 	 */
-	if (*idx > 0) {
+	if (xfs_iext_peek_prev_extent(ifp, ext, &LEFT)) {
 		state |= BMAP_LEFT_VALID;
-		xfs_iext_get_extent(ifp, *idx - 1, &LEFT);
-
 		if (isnullstartblock(LEFT.br_startblock))
 			state |= BMAP_LEFT_DELAY;
 	}
@@ -2116,9 +2111,8 @@ xfs_bmap_add_extent_unwritten_real(
 	 * Don't set contiguous if the combined extent would be too large.
 	 * Also check for all-three-contiguous being too large.
 	 */
-	if (*idx < xfs_iext_count(ifp) - 1) {
+	if (xfs_iext_peek_next_extent(ifp, ext, &RIGHT)) {
 		state |= BMAP_RIGHT_VALID;
-		xfs_iext_get_extent(ifp, *idx + 1, &RIGHT);
 		if (isnullstartblock(RIGHT.br_startblock))
 			state |= BMAP_RIGHT_DELAY;
 	}
@@ -2149,9 +2143,9 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
 
-		xfs_iext_remove(ip, *idx, 2, state);
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &LEFT);
+		xfs_iext_remove(ip, ext, 2, state);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &LEFT);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) - 2);
 		if (cur == NULL)
@@ -2187,9 +2181,9 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		LEFT.br_blockcount += PREV.br_blockcount;
 
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &LEFT);
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &LEFT);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
 		if (cur == NULL)
@@ -2220,10 +2214,10 @@ xfs_bmap_add_extent_unwritten_real(
 		PREV.br_blockcount += RIGHT.br_blockcount;
 		PREV.br_state = new->br_state;
 
-		++*idx;
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
@@ -2254,7 +2248,7 @@ xfs_bmap_add_extent_unwritten_real(
 		 * the new one.
 		 */
 		PREV.br_state = new->br_state;
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
 
 		if (cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -2282,9 +2276,9 @@ xfs_bmap_add_extent_unwritten_real(
 		PREV.br_startblock += new->br_blockcount;
 		PREV.br_blockcount -= new->br_blockcount;
 
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &LEFT);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &LEFT);
 
 		if (cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -2316,8 +2310,8 @@ xfs_bmap_add_extent_unwritten_real(
 		PREV.br_startblock += new->br_blockcount;
 		PREV.br_blockcount -= new->br_blockcount;
 
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
-		xfs_iext_insert(ip, *idx, 1, new, state);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
+		xfs_iext_insert(ip, ext, 1, new, state);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 		if (cur == NULL)
@@ -2350,9 +2344,9 @@ xfs_bmap_add_extent_unwritten_real(
 		RIGHT.br_startblock = new->br_startblock;
 		RIGHT.br_blockcount += new->br_blockcount;
 
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
-		++*idx;
-		xfs_iext_update_extent(ip, state, *idx, &RIGHT);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &RIGHT);
 
 		if (cur == NULL)
 			rval = XFS_ILOG_DEXT;
@@ -2382,9 +2376,9 @@ xfs_bmap_add_extent_unwritten_real(
 		old = PREV;
 		PREV.br_blockcount -= new->br_blockcount;
 
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
-		++*idx;
-		xfs_iext_insert(ip, *idx, 1, new, state);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_insert(ip, ext, 1, new, state);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
@@ -2425,9 +2419,9 @@ xfs_bmap_add_extent_unwritten_real(
 		r[1].br_startblock = new->br_startblock + new->br_blockcount;
 		r[1].br_state = PREV.br_state;
 
-		xfs_iext_update_extent(ip, state, *idx, &PREV);
-		++*idx;
-		xfs_iext_insert(ip, *idx, 2, &r[0], state);
+		xfs_iext_update_extent(ip, state, ext, &PREV);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_insert(ip, ext, 2, &r[0], state);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 2);
@@ -2516,7 +2510,7 @@ STATIC void
 xfs_bmap_add_extent_hole_delay(
 	xfs_inode_t		*ip,	/* incore inode pointer */
 	int			whichfork,
-	xfs_extnum_t		*idx,	/* extent number to update/insert */
+	struct xfs_iext_cursor	*ext,
 	xfs_bmbt_irec_t		*new)	/* new data to add to file extents */
 {
 	xfs_ifork_t		*ifp;	/* inode fork pointer */
@@ -2533,10 +2527,8 @@ xfs_bmap_add_extent_hole_delay(
 	/*
 	 * Check and set flags if this segment has a left neighbor
 	 */
-	if (*idx > 0) {
+	if (xfs_iext_peek_prev_extent(ifp, ext, &left)) {
 		state |= BMAP_LEFT_VALID;
-		xfs_iext_get_extent(ifp, *idx - 1, &left);
-
 		if (isnullstartblock(left.br_startblock))
 			state |= BMAP_LEFT_DELAY;
 	}
@@ -2545,10 +2537,8 @@ xfs_bmap_add_extent_hole_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 (*idx < xfs_iext_count(ifp)) {
+	if (xfs_iext_get_extent(ifp, ext, &right)) {
 		state |= BMAP_RIGHT_VALID;
-		xfs_iext_get_extent(ifp, *idx, &right);
-
 		if (isnullstartblock(right.br_startblock))
 			state |= BMAP_RIGHT_DELAY;
 	}
@@ -2591,9 +2581,9 @@ xfs_bmap_add_extent_hole_delay(
 		left.br_startblock = nullstartblock(newlen);
 		left.br_blockcount = temp;
 
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &left);
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &left);
 		break;
 
 	case BMAP_LEFT_CONTIG:
@@ -2611,8 +2601,8 @@ xfs_bmap_add_extent_hole_delay(
 		left.br_blockcount = temp;
 		left.br_startblock = nullstartblock(newlen);
 
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &left);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &left);
 		break;
 
 	case BMAP_RIGHT_CONTIG:
@@ -2629,7 +2619,7 @@ xfs_bmap_add_extent_hole_delay(
 		right.br_startoff = new->br_startoff;
 		right.br_startblock = nullstartblock(newlen);
 		right.br_blockcount = temp;
-		xfs_iext_update_extent(ip, state, *idx, &right);
+		xfs_iext_update_extent(ip, state, ext, &right);
 		break;
 
 	case 0:
@@ -2639,7 +2629,7 @@ xfs_bmap_add_extent_hole_delay(
 		 * Insert a new entry.
 		 */
 		oldlen = newlen = 0;
-		xfs_iext_insert(ip, *idx, 1, new, state);
+		xfs_iext_insert(ip, ext, 1, new, state);
 		break;
 	}
 	if (oldlen != newlen) {
@@ -2660,7 +2650,7 @@ xfs_bmap_add_extent_hole_real(
 	struct xfs_trans	*tp,
 	struct xfs_inode	*ip,
 	int			whichfork,
-	xfs_extnum_t		*idx,
+	struct xfs_iext_cursor	*ext,
 	struct xfs_btree_cur	**curp,
 	struct xfs_bmbt_irec	*new,
 	xfs_fsblock_t		*first,
@@ -2678,8 +2668,6 @@ xfs_bmap_add_extent_hole_real(
 	int			state = xfs_bmap_fork_to_state(whichfork);
 	struct xfs_bmbt_irec	old;
 
-	ASSERT(*idx >= 0);
-	ASSERT(*idx <= xfs_iext_count(ifp));
 	ASSERT(!isnullstartblock(new->br_startblock));
 	ASSERT(!cur || !(cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
 
@@ -2688,9 +2676,8 @@ xfs_bmap_add_extent_hole_real(
 	/*
 	 * Check and set flags if this segment has a left neighbor.
 	 */
-	if (*idx > 0) {
+	if (xfs_iext_peek_prev_extent(ifp, ext, &left)) {
 		state |= BMAP_LEFT_VALID;
-		xfs_iext_get_extent(ifp, *idx - 1, &left);
 		if (isnullstartblock(left.br_startblock))
 			state |= BMAP_LEFT_DELAY;
 	}
@@ -2699,9 +2686,8 @@ xfs_bmap_add_extent_hole_real(
 	 * Check and set flags if this segment has a current value.
 	 * Not true if we're inserting into the "hole" at eof.
 	 */
-	if (*idx < xfs_iext_count(ifp)) {
+	if (xfs_iext_get_extent(ifp, ext, &right)) {
 		state |= BMAP_RIGHT_VALID;
-		xfs_iext_get_extent(ifp, *idx, &right);
 		if (isnullstartblock(right.br_startblock))
 			state |= BMAP_RIGHT_DELAY;
 	}
@@ -2740,9 +2726,9 @@ xfs_bmap_add_extent_hole_real(
 		 */
 		left.br_blockcount += new->br_blockcount + right.br_blockcount;
 
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &left);
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &left);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
@@ -2777,8 +2763,8 @@ xfs_bmap_add_extent_hole_real(
 		old = left;
 		left.br_blockcount += new->br_blockcount;
 
-		--*idx;
-		xfs_iext_update_extent(ip, state, *idx, &left);
+		xfs_iext_prev(ifp, ext);
+		xfs_iext_update_extent(ip, state, ext, &left);
 
 		if (cur == NULL) {
 			rval = xfs_ilog_fext(whichfork);
@@ -2805,7 +2791,7 @@ xfs_bmap_add_extent_hole_real(
 		right.br_startoff = new->br_startoff;
 		right.br_startblock = new->br_startblock;
 		right.br_blockcount += new->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &right);
+		xfs_iext_update_extent(ip, state, ext, &right);
 
 		if (cur == NULL) {
 			rval = xfs_ilog_fext(whichfork);
@@ -2827,7 +2813,7 @@ xfs_bmap_add_extent_hole_real(
 		 * real allocation.
 		 * Insert a new entry.
 		 */
-		xfs_iext_insert(ip, *idx, 1, new, state);
+		xfs_iext_insert(ip, ext, 1, new, state);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 		if (cur == NULL) {
@@ -3777,7 +3763,7 @@ xfs_bmapi_read(
 	struct xfs_bmbt_irec	got;
 	xfs_fileoff_t		obno;
 	xfs_fileoff_t		end;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 	int			error;
 	bool			eof = false;
 	int			n = 0;
@@ -3819,7 +3805,7 @@ xfs_bmapi_read(
 			return error;
 	}
 
-	if (!xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got))
+	if (!xfs_iext_lookup_extent(ip, ifp, bno, &ext, &got))
 		eof = true;
 	end = bno + len;
 	obno = bno;
@@ -3851,7 +3837,7 @@ xfs_bmapi_read(
 			break;
 
 		/* Else go on to the next record. */
-		if (!xfs_iext_get_extent(ifp, ++idx, &got))
+		if (!xfs_iext_next_extent(ifp, &ext, &got))
 			eof = true;
 	}
 	*nmap = n;
@@ -3879,7 +3865,7 @@ xfs_bmapi_reserve_delalloc(
 	xfs_filblks_t		len,
 	xfs_filblks_t		prealloc,
 	struct xfs_bmbt_irec	*got,
-	xfs_extnum_t		*lastx,
+	struct xfs_iext_cursor	*ext,
 	int			eof)
 {
 	struct xfs_mount	*mp = ip->i_mount;
@@ -3909,7 +3895,7 @@ xfs_bmapi_reserve_delalloc(
 	if (extsz) {
 		struct xfs_bmbt_irec	prev;
 
-		if (!xfs_iext_get_extent(ifp, *lastx - 1, &prev))
+		if (!xfs_iext_peek_prev_extent(ifp, ext, &prev))
 			prev.br_startoff = NULLFILEOFF;
 
 		error = xfs_bmap_extsize_align(mp, got, &prev, extsz, rt, eof,
@@ -3958,7 +3944,7 @@ xfs_bmapi_reserve_delalloc(
 	got->br_blockcount = alen;
 	got->br_state = XFS_EXT_NORM;
 
-	xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
+	xfs_bmap_add_extent_hole_delay(ip, whichfork, ext, got);
 
 	/*
 	 * Tag the inode if blocks were preallocated. Note that COW fork
@@ -4003,8 +3989,7 @@ xfs_bmapi_allocate(
 	if (bma->wasdel) {
 		bma->length = (xfs_extlen_t)bma->got.br_blockcount;
 		bma->offset = bma->got.br_startoff;
-		if (bma->idx)
-			xfs_iext_get_extent(ifp, bma->idx - 1, &bma->prev);
+		xfs_iext_peek_prev_extent(ifp, &bma->ext, &bma->prev);
 	} else {
 		bma->length = XFS_FILBLKS_MIN(bma->length, MAXEXTLEN);
 		if (!bma->eof)
@@ -4089,7 +4074,7 @@ xfs_bmapi_allocate(
 		error = xfs_bmap_add_extent_delay_real(bma, whichfork);
 	else
 		error = xfs_bmap_add_extent_hole_real(bma->tp, bma->ip,
-				whichfork, &bma->idx, &bma->cur, &bma->got,
+				whichfork, &bma->ext, &bma->cur, &bma->got,
 				bma->firstblock, bma->dfops, &bma->logflags);
 
 	bma->logflags |= tmp_logflags;
@@ -4101,7 +4086,7 @@ xfs_bmapi_allocate(
 	 * or xfs_bmap_add_extent_hole_real might have merged it into one of
 	 * the neighbouring ones.
 	 */
-	xfs_iext_get_extent(ifp, bma->idx, &bma->got);
+	xfs_iext_get_extent(ifp, &bma->ext, &bma->got);
 
 	ASSERT(bma->got.br_startoff <= bma->offset);
 	ASSERT(bma->got.br_startoff + bma->got.br_blockcount >=
@@ -4159,7 +4144,7 @@ xfs_bmapi_convert_unwritten(
 	}
 
 	error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, whichfork,
-			&bma->idx, &bma->cur, mval, bma->firstblock, bma->dfops,
+			&bma->ext, &bma->cur, mval, bma->firstblock, bma->dfops,
 			&tmp_logflags);
 	/*
 	 * Log the inode core unconditionally in the unwritten extent conversion
@@ -4182,7 +4167,7 @@ xfs_bmapi_convert_unwritten(
 	 * xfs_bmap_add_extent_unwritten_real might have merged it into one
 	 * of the neighbouring ones.
 	 */
-	xfs_iext_get_extent(ifp, bma->idx, &bma->got);
+	xfs_iext_get_extent(ifp, &bma->ext, &bma->got);
 
 	/*
 	 * We may have combined previously unwritten space with written space,
@@ -4301,9 +4286,9 @@ xfs_bmapi_write(
 	end = bno + len;
 	obno = bno;
 
-	if (!xfs_iext_lookup_extent(ip, ifp, bno, &bma.idx, &bma.got))
+	if (!xfs_iext_lookup_extent(ip, ifp, bno, &bma.ext, &bma.got))
 		eof = true;
-	if (!xfs_iext_get_extent(ifp, bma.idx - 1, &bma.prev))
+	if (!xfs_iext_peek_prev_extent(ifp, &bma.ext, &bma.prev))
 		bma.prev.br_startoff = NULLFILEOFF;
 	bma.tp = tp;
 	bma.ip = ip;
@@ -4408,7 +4393,7 @@ xfs_bmapi_write(
 
 		/* Else go on to the next record. */
 		bma.prev = bma.got;
-		if (!xfs_iext_get_extent(ifp, ++bma.idx, &bma.got))
+		if (!xfs_iext_next_extent(ifp, &bma.ext, &bma.got))
 			eof = true;
 	}
 	*nmap = n;
@@ -4481,7 +4466,7 @@ xfs_bmapi_remap(
 	struct xfs_btree_cur	*cur = NULL;
 	xfs_fsblock_t		firstblock = NULLFSBLOCK;
 	struct xfs_bmbt_irec	got;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 	int			logflags = 0, error;
 
 	ASSERT(len > 0);
@@ -4505,7 +4490,7 @@ xfs_bmapi_remap(
 			return error;
 	}
 
-	if (xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got)) {
+	if (xfs_iext_lookup_extent(ip, ifp, bno, &ext, &got)) {
 		/* make sure we only reflink into a hole. */
 		ASSERT(got.br_startoff > bno);
 		ASSERT(got.br_startoff - bno >= len);
@@ -4526,7 +4511,7 @@ xfs_bmapi_remap(
 	got.br_blockcount = len;
 	got.br_state = XFS_EXT_NORM;
 
-	error = xfs_bmap_add_extent_hole_real(tp, ip, XFS_DATA_FORK, &idx, &cur,
+	error = xfs_bmap_add_extent_hole_real(tp, ip, XFS_DATA_FORK, &ext, &cur,
 			&got, &firstblock, dfops, &logflags);
 	if (error)
 		goto error0;
@@ -4643,7 +4628,7 @@ int
 xfs_bmap_del_extent_delay(
 	struct xfs_inode	*ip,
 	int			whichfork,
-	xfs_extnum_t		*idx,
+	struct xfs_iext_cursor	*ext,
 	struct xfs_bmbt_irec	*got,
 	struct xfs_bmbt_irec	*del)
 {
@@ -4665,8 +4650,6 @@ xfs_bmap_del_extent_delay(
 	da_old = startblockval(got->br_startblock);
 	da_new = 0;
 
-	ASSERT(*idx >= 0);
-	ASSERT(*idx <= xfs_iext_count(ifp));
 	ASSERT(del->br_blockcount > 0);
 	ASSERT(got->br_startoff <= del->br_startoff);
 	ASSERT(got_endoff >= del_endoff);
@@ -4700,8 +4683,8 @@ xfs_bmap_del_extent_delay(
 		/*
 		 * Matches the whole extent.  Delete the entry.
 		 */
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
 		break;
 	case BMAP_LEFT_FILLING:
 		/*
@@ -4712,7 +4695,7 @@ xfs_bmap_del_extent_delay(
 		da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
 				got->br_blockcount), da_old);
 		got->br_startblock = nullstartblock((int)da_new);
-		xfs_iext_update_extent(ip, state, *idx, got);
+		xfs_iext_update_extent(ip, state, ext, got);
 		break;
 	case BMAP_RIGHT_FILLING:
 		/*
@@ -4722,7 +4705,7 @@ xfs_bmap_del_extent_delay(
 		da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
 				got->br_blockcount), da_old);
 		got->br_startblock = nullstartblock((int)da_new);
-		xfs_iext_update_extent(ip, state, *idx, got);
+		xfs_iext_update_extent(ip, state, ext, got);
 		break;
 	case 0:
 		/*
@@ -4750,9 +4733,9 @@ xfs_bmap_del_extent_delay(
 		new.br_state = got->br_state;
 		new.br_startblock = nullstartblock((int)new_indlen);
 
-		xfs_iext_update_extent(ip, state, *idx, got);
-		++*idx;
-		xfs_iext_insert(ip, *idx, 1, &new, state);
+		xfs_iext_update_extent(ip, state, ext, got);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_insert(ip, ext, 1, &new, state);
 
 		da_new = got_indlen + new_indlen - stolen;
 		del->br_blockcount -= stolen;
@@ -4771,7 +4754,7 @@ xfs_bmap_del_extent_delay(
 void
 xfs_bmap_del_extent_cow(
 	struct xfs_inode	*ip,
-	xfs_extnum_t		*idx,
+	struct xfs_iext_cursor	*ext,
 	struct xfs_bmbt_irec	*got,
 	struct xfs_bmbt_irec	*del)
 {
@@ -4786,8 +4769,6 @@ xfs_bmap_del_extent_cow(
 	del_endoff = del->br_startoff + del->br_blockcount;
 	got_endoff = got->br_startoff + got->br_blockcount;
 
-	ASSERT(*idx >= 0);
-	ASSERT(*idx <= xfs_iext_count(ifp));
 	ASSERT(del->br_blockcount > 0);
 	ASSERT(got->br_startoff <= del->br_startoff);
 	ASSERT(got_endoff >= del_endoff);
@@ -4803,8 +4784,8 @@ xfs_bmap_del_extent_cow(
 		/*
 		 * Matches the whole extent.  Delete the entry.
 		 */
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
 		break;
 	case BMAP_LEFT_FILLING:
 		/*
@@ -4813,14 +4794,14 @@ xfs_bmap_del_extent_cow(
 		got->br_startoff = del_endoff;
 		got->br_blockcount -= del->br_blockcount;
 		got->br_startblock = del->br_startblock + del->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, got);
+		xfs_iext_update_extent(ip, state, ext, got);
 		break;
 	case BMAP_RIGHT_FILLING:
 		/*
 		 * Deleting the last part of the extent.
 		 */
 		got->br_blockcount -= del->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, got);
+		xfs_iext_update_extent(ip, state, ext, got);
 		break;
 	case 0:
 		/*
@@ -4833,9 +4814,9 @@ xfs_bmap_del_extent_cow(
 		new.br_state = got->br_state;
 		new.br_startblock = del->br_startblock + del->br_blockcount;
 
-		xfs_iext_update_extent(ip, state, *idx, got);
-		++*idx;
-		xfs_iext_insert(ip, *idx, 1, &new, state);
+		xfs_iext_update_extent(ip, state, ext, got);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_insert(ip, ext, 1, &new, state);
 		break;
 	}
 }
@@ -4848,7 +4829,7 @@ STATIC int				/* error */
 xfs_bmap_del_extent_real(
 	xfs_inode_t		*ip,	/* incore inode pointer */
 	xfs_trans_t		*tp,	/* current transaction pointer */
-	xfs_extnum_t		*idx,	/* extent number to update/delete */
+	struct xfs_iext_cursor	*ext,
 	struct xfs_defer_ops	*dfops,	/* list of extents to be freed */
 	xfs_btree_cur_t		*cur,	/* if null, not a btree */
 	xfs_bmbt_irec_t		*del,	/* data to remove from extents */
@@ -4877,9 +4858,8 @@ xfs_bmap_del_extent_real(
 	XFS_STATS_INC(mp, xs_del_exlist);
 
 	ifp = XFS_IFORK_PTR(ip, whichfork);
-	ASSERT((*idx >= 0) && (*idx < xfs_iext_count(ifp)));
 	ASSERT(del->br_blockcount > 0);
-	xfs_iext_get_extent(ifp, *idx, &got);
+	xfs_iext_get_extent(ifp, ext, &got);
 	ASSERT(got.br_startoff <= del->br_startoff);
 	del_endoff = del->br_startoff + del->br_blockcount;
 	got_endoff = got.br_startoff + got.br_blockcount;
@@ -4944,9 +4924,8 @@ xfs_bmap_del_extent_real(
 		/*
 		 * Matches the whole extent.  Delete the entry.
 		 */
-		xfs_iext_remove(ip, *idx, 1, state);
-		--*idx;
-
+		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_prev(ifp, ext);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
 		flags |= XFS_ILOG_CORE;
@@ -4965,7 +4944,7 @@ xfs_bmap_del_extent_real(
 		got.br_startoff = del_endoff;
 		got.br_startblock = del_endblock;
 		got.br_blockcount -= del->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &got);
+		xfs_iext_update_extent(ip, state, ext, &got);
 		if (!cur) {
 			flags |= xfs_ilog_fext(whichfork);
 			break;
@@ -4979,7 +4958,7 @@ xfs_bmap_del_extent_real(
 		 * Deleting the last part of the extent.
 		 */
 		got.br_blockcount -= del->br_blockcount;
-		xfs_iext_update_extent(ip, state, *idx, &got);
+		xfs_iext_update_extent(ip, state, ext, &got);
 		if (!cur) {
 			flags |= xfs_ilog_fext(whichfork);
 			break;
@@ -4995,7 +4974,7 @@ xfs_bmap_del_extent_real(
 		old = got;
 
 		got.br_blockcount = del->br_startoff - got.br_startoff;
-		xfs_iext_update_extent(ip, state, *idx, &got);
+		xfs_iext_update_extent(ip, state, ext, &got);
 
 		new.br_startoff = del_endoff;
 		new.br_blockcount = got_endoff - del_endoff;
@@ -5039,7 +5018,7 @@ xfs_bmap_del_extent_real(
 				 * Reset the extent record back
 				 * to the original value.
 				 */
-				xfs_iext_update_extent(ip, state, *idx, &old);
+				xfs_iext_update_extent(ip, state, ext, &old);
 				flags = 0;
 				error = -ENOSPC;
 				goto done;
@@ -5049,8 +5028,8 @@ xfs_bmap_del_extent_real(
 			flags |= xfs_ilog_fext(whichfork);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
-		++*idx;
-		xfs_iext_insert(ip, *idx, 1, &new, state);
+		xfs_iext_next(ifp, ext);
+		xfs_iext_insert(ip, ext, 1, &new, state);
 		break;
 	}
 
@@ -5113,7 +5092,6 @@ __xfs_bunmapi(
 	xfs_bmbt_irec_t		got;		/* current extent record */
 	xfs_ifork_t		*ifp;		/* inode fork pointer */
 	int			isrt;		/* freeing in rt area */
-	xfs_extnum_t		lastx;		/* last extent index used */
 	int			logflags;	/* transaction logging flags */
 	xfs_extlen_t		mod;		/* rt extent offset */
 	xfs_mount_t		*mp;		/* mount structure */
@@ -5125,6 +5103,8 @@ __xfs_bunmapi(
 	xfs_fileoff_t		max_len;
 	xfs_agnumber_t		prev_agno = NULLAGNUMBER, agno;
 	xfs_fileoff_t		end;
+	struct xfs_iext_cursor	ext;
+	bool			done = false;
 
 	trace_xfs_bunmap(ip, start, len, flags, _RET_IP_);
 
@@ -5167,7 +5147,7 @@ __xfs_bunmapi(
 	isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip);
 	end = start + len;
 
-	if (!xfs_iext_lookup_extent_before(ip, ifp, &end, &lastx, &got)) {
+	if (!xfs_iext_lookup_extent_before(ip, ifp, &end, &ext, &got)) {
 		*rlen = 0;
 		return 0;
 	}
@@ -5194,16 +5174,16 @@ __xfs_bunmapi(
 	}
 
 	extno = 0;
-	while (end != (xfs_fileoff_t)-1 && end >= start && lastx >= 0 &&
+	while (end != (xfs_fileoff_t)-1 && end >= start &&
 	       (nexts == 0 || extno < nexts) && max_len > 0) {
 		/*
 		 * Is the found extent after a hole in which end lives?
 		 * Just back up to the previous extent, if so.
 		 */
-		if (got.br_startoff > end) {
-			if (--lastx < 0)
-				break;
-			xfs_iext_get_extent(ifp, lastx, &got);
+		if (got.br_startoff > end &&
+		    !xfs_iext_prev_extent(ifp, &ext, &got)) {
+			done = true;
+			break;
 		}
 		/*
 		 * Is the last block of this extent before the range
@@ -5266,10 +5246,10 @@ __xfs_bunmapi(
 				ASSERT(end >= mod);
 				end -= mod > del.br_blockcount ?
 					del.br_blockcount : mod;
-				if (end < got.br_startoff) {
-					if (--lastx >= 0)
-						xfs_iext_get_extent(ifp, lastx,
-								&got);
+				if (end < got.br_startoff &&
+				    !xfs_iext_prev_extent(ifp, &ext, &got)) {
+					done = true;
+					break;
 				}
 				continue;
 			}
@@ -5290,7 +5270,7 @@ __xfs_bunmapi(
 			}
 			del.br_state = XFS_EXT_UNWRITTEN;
 			error = xfs_bmap_add_extent_unwritten_real(tp, ip,
-					whichfork, &lastx, &cur, &del,
+					whichfork, &ext, &cur, &del,
 					firstblock, dfops, &logflags);
 			if (error)
 				goto error0;
@@ -5317,8 +5297,11 @@ __xfs_bunmapi(
 				 */
 				ASSERT(end >= del.br_blockcount);
 				end -= del.br_blockcount;
-				if (got.br_startoff > end && --lastx >= 0)
-					xfs_iext_get_extent(ifp, lastx, &got);
+				if (got.br_startoff > end &&
+				    !xfs_iext_prev_extent(ifp, &ext, &got)) {
+					done = true;
+					break;
+				}
 				continue;
 			} else if (del.br_state == XFS_EXT_UNWRITTEN) {
 				struct xfs_bmbt_irec	prev;
@@ -5329,8 +5312,8 @@ __xfs_bunmapi(
 				 * Unwrite the killed part of that one and
 				 * try again.
 				 */
-				ASSERT(lastx > 0);
-				xfs_iext_get_extent(ifp, lastx - 1, &prev);
+				if (!xfs_iext_prev_extent(ifp, &ext, &prev))
+					ASSERT(0);
 				ASSERT(prev.br_state == XFS_EXT_NORM);
 				ASSERT(!isnullstartblock(prev.br_startblock));
 				ASSERT(del.br_startblock ==
@@ -5342,9 +5325,8 @@ __xfs_bunmapi(
 					prev.br_startoff = start;
 				}
 				prev.br_state = XFS_EXT_UNWRITTEN;
-				lastx--;
 				error = xfs_bmap_add_extent_unwritten_real(tp,
-						ip, whichfork, &lastx, &cur,
+						ip, whichfork, &ext, &cur,
 						&prev, firstblock, dfops,
 						&logflags);
 				if (error)
@@ -5354,7 +5336,7 @@ __xfs_bunmapi(
 				ASSERT(del.br_state == XFS_EXT_NORM);
 				del.br_state = XFS_EXT_UNWRITTEN;
 				error = xfs_bmap_add_extent_unwritten_real(tp,
-						ip, whichfork, &lastx, &cur,
+						ip, whichfork, &ext, &cur,
 						&del, firstblock, dfops,
 						&logflags);
 				if (error)
@@ -5364,10 +5346,10 @@ __xfs_bunmapi(
 		}
 
 		if (wasdel) {
-			error = xfs_bmap_del_extent_delay(ip, whichfork, &lastx,
+			error = xfs_bmap_del_extent_delay(ip, whichfork, &ext,
 					&got, &del);
 		} else {
-			error = xfs_bmap_del_extent_real(ip, tp, &lastx, dfops,
+			error = xfs_bmap_del_extent_real(ip, tp, &ext, dfops,
 					cur, &del, &tmp_logflags, whichfork,
 					flags);
 			logflags |= tmp_logflags;
@@ -5383,15 +5365,16 @@ __xfs_bunmapi(
 		 * If not done go on to the next (previous) record.
 		 */
 		if (end != (xfs_fileoff_t)-1 && end >= start) {
-			if (lastx >= 0) {
-				xfs_iext_get_extent(ifp, lastx, &got);
-				if (got.br_startoff > end && --lastx >= 0)
-					xfs_iext_get_extent(ifp, lastx, &got);
+			if (!xfs_iext_get_extent(ifp, &ext, &got) ||
+			    (got.br_startoff > end &&
+			     !xfs_iext_prev_extent(ifp, &ext, &got))) {
+				done = true;
+				break;
 			}
 			extno++;
 		}
 	}
-	if (end == (xfs_fileoff_t)-1 || end < start || lastx < 0)
+	if (done || end == (xfs_fileoff_t)-1 || end < start)
 		*rlen = 0;
 	else
 		*rlen = end - start + 1;
@@ -5512,7 +5495,7 @@ xfs_bmse_merge(
 	struct xfs_inode		*ip,
 	int				whichfork,
 	xfs_fileoff_t			shift,		/* shift fsb */
-	int				*current_ext,	/* idx of gotp */
+	struct xfs_iext_cursor		*ext,
 	struct xfs_bmbt_irec		*got,		/* extent to shift */
 	struct xfs_bmbt_irec		*left,		/* preceding extent */
 	struct xfs_btree_cur		*cur,
@@ -5567,10 +5550,10 @@ xfs_bmse_merge(
 		return error;
 
 done:
-	xfs_iext_remove(ip, *current_ext, 1, 0);
-	--*current_ext;
-	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
-			*current_ext, &new);
+	xfs_iext_remove(ip, ext, 1, 0);
+	xfs_iext_prev(XFS_IFORK_PTR(ip, whichfork), ext);
+	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), ext,
+			&new);
 
 	/* update reverse mapping. rmap functions merge the rmaps for us */
 	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, got);
@@ -5585,7 +5568,7 @@ static int
 xfs_bmap_shift_update_extent(
 	struct xfs_inode	*ip,
 	int			whichfork,
-	xfs_extnum_t		idx,
+	struct xfs_iext_cursor	*ext,
 	struct xfs_bmbt_irec	*got,
 	struct xfs_btree_cur	*cur,
 	int			*logflags,
@@ -5613,7 +5596,7 @@ xfs_bmap_shift_update_extent(
 		*logflags |= XFS_ILOG_DEXT;
 	}
 
-	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), idx, got);
+	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), ext, got);
 
 	/* update reverse mapping */
 	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, &prev);
@@ -5638,7 +5621,7 @@ xfs_bmap_collapse_extents(
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
 	struct xfs_btree_cur	*cur = NULL;
 	struct xfs_bmbt_irec	got, prev;
-	xfs_extnum_t		current_ext;
+	struct xfs_iext_cursor	ext;
 	xfs_fileoff_t		new_startoff;
 	int			error = 0;
 	int			logflags = 0;
@@ -5669,14 +5652,14 @@ xfs_bmap_collapse_extents(
 		cur->bc_private.b.flags = 0;
 	}
 
-	if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &current_ext, &got)) {
+	if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &ext, &got)) {
 		*done = true;
 		goto del_cursor;
 	}
 	XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
 
 	new_startoff = got.br_startoff - offset_shift_fsb;
-	if (xfs_iext_get_extent(ifp, current_ext - 1, &prev)) {
+	if (xfs_iext_peek_prev_extent(ifp, &ext, &prev)) {
 		if (new_startoff < prev.br_startoff + prev.br_blockcount) {
 			error = -EINVAL;
 			goto del_cursor;
@@ -5684,8 +5667,8 @@ xfs_bmap_collapse_extents(
 
 		if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) {
 			error = xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
-					&current_ext, &got, &prev, cur,
-					&logflags, dfops);
+					&ext, &got, &prev, cur, &logflags,
+					dfops);
 			if (error)
 				goto del_cursor;
 			goto done;
@@ -5697,13 +5680,13 @@ xfs_bmap_collapse_extents(
 		}
 	}
 
-	error = xfs_bmap_shift_update_extent(ip, whichfork, current_ext, &got,
-			cur, &logflags, dfops, new_startoff);
+	error = xfs_bmap_shift_update_extent(ip, whichfork, &ext, &got, cur,
+			&logflags, dfops, new_startoff);
 	if (error)
 		goto del_cursor;
 
 done:
-	if (!xfs_iext_get_extent(ifp, ++current_ext, &got)) {
+	if (!xfs_iext_next_extent(ifp, &ext, &got)) {
 		 *done = true;
 		 goto del_cursor;
 	}
@@ -5734,7 +5717,7 @@ xfs_bmap_insert_extents(
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
 	struct xfs_btree_cur	*cur = NULL;
 	struct xfs_bmbt_irec	got, next;
-	xfs_extnum_t		current_ext;
+	struct xfs_iext_cursor	ext;
 	xfs_fileoff_t		new_startoff;
 	int			error = 0;
 	int			logflags = 0;
@@ -5766,15 +5749,14 @@ xfs_bmap_insert_extents(
 	}
 
 	if (*next_fsb == NULLFSBLOCK) {
-		current_ext = xfs_iext_count(ifp) - 1;
-		if (!xfs_iext_get_extent(ifp, current_ext, &got) ||
+		xfs_iext_last(ifp, &ext);
+		if (!xfs_iext_get_extent(ifp, &ext, &got) ||
 		    stop_fsb > got.br_startoff) {
 			*done = true;
 			goto del_cursor;
 		}
 	} else {
-		if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &current_ext,
-				&got)) {
+		if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &ext, &got)) {
 			*done = true;
 			goto del_cursor;
 		}
@@ -5787,7 +5769,7 @@ xfs_bmap_insert_extents(
 	}
 
 	new_startoff = got.br_startoff + offset_shift_fsb;
-	if (xfs_iext_get_extent(ifp, current_ext + 1, &next)) {
+	if (xfs_iext_peek_next_extent(ifp, &ext, &next)) {
 		if (new_startoff + got.br_blockcount > next.br_startoff) {
 			error = -EINVAL;
 			goto del_cursor;
@@ -5803,12 +5785,12 @@ xfs_bmap_insert_extents(
 			WARN_ON_ONCE(1);
 	}
 
-	error = xfs_bmap_shift_update_extent(ip, whichfork, current_ext, &got,
-			cur, &logflags, dfops, new_startoff);
+	error = xfs_bmap_shift_update_extent(ip, whichfork, &ext, &got, cur,
+			&logflags, dfops, new_startoff);
 	if (error)
 		goto del_cursor;
 
-	if (!xfs_iext_get_extent(ifp, --current_ext, &got) ||
+	if (!xfs_iext_prev_extent(ifp, &ext, &got) ||
 	    stop_fsb >= got.br_startoff + got.br_blockcount) {
 		*done = true;
 		goto del_cursor;
@@ -5825,10 +5807,10 @@ xfs_bmap_insert_extents(
 }
 
 /*
- * 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.
+ * Splits an extent into two extents at split_fsb block such that it is the
+ * first block of the current_ext. @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(
@@ -5845,7 +5827,7 @@ xfs_bmap_split_extent_at(
 	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;
+	struct xfs_iext_cursor		ext;
 	int				error = 0;
 	int				logflags = 0;
 	int				i = 0;
@@ -5873,7 +5855,7 @@ xfs_bmap_split_extent_at(
 	/*
 	 * If there are not extents, or split_fsb lies in a hole we are done.
 	 */
-	if (!xfs_iext_lookup_extent(ip, ifp, split_fsb, &current_ext, &got) ||
+	if (!xfs_iext_lookup_extent(ip, ifp, split_fsb, &ext, &got) ||
 	    got.br_startoff >= split_fsb)
 		return 0;
 
@@ -5895,8 +5877,8 @@ xfs_bmap_split_extent_at(
 	}
 
 	got.br_blockcount = gotblkcnt;
-	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
-			current_ext, &got);
+	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), &ext,
+			&got);
 
 	logflags = XFS_ILOG_CORE;
 	if (cur) {
@@ -5907,8 +5889,8 @@ xfs_bmap_split_extent_at(
 		logflags |= XFS_ILOG_DEXT;
 
 	/* Add new extent */
-	current_ext++;
-	xfs_iext_insert(ip, current_ext, 1, &new, 0);
+	xfs_iext_next(ifp, &ext);
+	xfs_iext_insert(ip, &ext, 1, &new, 0);
 	XFS_IFORK_NEXT_SET(ip, whichfork,
 			   XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index a8777682ba57..195f335f4615 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -43,7 +43,7 @@ struct xfs_bmalloca {
 	xfs_fsblock_t		blkno;	/* starting block of new extent */
 
 	struct xfs_btree_cur	*cur;	/* btree cursor */
-	xfs_extnum_t		idx;	/* current extent index */
+	struct xfs_iext_cursor	ext;
 	int			nallocs;/* number of extents alloc'd */
 	int			logflags;/* flags for transaction logging */
 
@@ -216,10 +216,11 @@ int	xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
 		xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
 		struct xfs_defer_ops *dfops, int *done);
 int	xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
-		xfs_extnum_t *idx, struct xfs_bmbt_irec *got,
+		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *got,
+		struct xfs_bmbt_irec *del);
+void	xfs_bmap_del_extent_cow(struct xfs_inode *ip,
+		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *got,
 		struct xfs_bmbt_irec *del);
-void	xfs_bmap_del_extent_cow(struct xfs_inode *ip, xfs_extnum_t *idx,
-		struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *del);
 uint	xfs_default_attroffset(struct xfs_inode *ip);
 int	xfs_bmap_collapse_extents(struct xfs_trans *tp, struct xfs_inode *ip,
 		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,
@@ -232,7 +233,8 @@ int	xfs_bmap_insert_extents(struct xfs_trans *tp, struct xfs_inode *ip,
 int	xfs_bmap_split_extent(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, xfs_extnum_t *lastx, int eof);
+		struct xfs_bmbt_irec *got, struct xfs_iext_cursor *cur,
+		int eof);
 
 enum xfs_bmap_intent_type {
 	XFS_BMAP_MAP = 1,
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 7dd77b497fc2..c9e10d4818b7 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -343,6 +343,7 @@ xfs_iformat_extents(
 	int			state = xfs_bmap_fork_to_state(whichfork);
 	int			nex = XFS_DFORK_NEXTENTS(dip, whichfork);
 	int			size = nex * sizeof(xfs_bmbt_rec_t);
+	struct xfs_iext_cursor	ext;
 	struct xfs_bmbt_rec	*dp;
 	int			i;
 
@@ -369,16 +370,21 @@ xfs_iformat_extents(
 	ifp->if_bytes = size;
 	if (size) {
 		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
+
+		xfs_iext_first(ifp, &ext);
 		for (i = 0; i < nex; i++, dp++) {
 			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
+
 			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
 				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
 						 XFS_ERRLEVEL_LOW, mp);
 				return -EFSCORRUPTED;
 			}
+
 			ep->l0 = get_unaligned_be64(&dp->l0);
 			ep->l1 = get_unaligned_be64(&dp->l1);
-			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
+			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
+			xfs_iext_next(ifp, &ext);
 		}
 	}
 	ifp->if_flags |= XFS_IFEXTENTS;
@@ -739,17 +745,18 @@ xfs_iextents_copy(
 {
 	int			state = xfs_bmap_fork_to_state(whichfork);
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
+	struct xfs_iext_cursor	ext;
 	struct xfs_bmbt_irec	rec;
-	int			copied = 0, i = 0;
+	int			copied = 0;
 
 	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
 	ASSERT(ifp->if_bytes > 0);
 
-	while (xfs_iext_get_extent(ifp, i++, &rec)) {
+	for_each_iext(ifp, &ext, &rec) {
 		if (isnullstartblock(rec.br_startblock))
 			continue;
 		xfs_bmbt_disk_set_all(dp, &rec);
-		trace_xfs_write_extent(ip, i, state, _RET_IP_);
+		trace_xfs_write_extent(ip, &ext, state, _RET_IP_);
 		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
 		copied += sizeof(struct xfs_bmbt_rec);
 		dp++;
@@ -894,7 +901,7 @@ xfs_iext_state_to_fork(
 void
 xfs_iext_insert(
 	xfs_inode_t	*ip,		/* incore inode pointer */
-	xfs_extnum_t	idx,		/* starting index of new items */
+	struct xfs_iext_cursor *cur,
 	xfs_extnum_t	count,		/* number of inserted items */
 	xfs_bmbt_irec_t	*new,		/* items to insert */
 	int		state)		/* type of extent conversion */
@@ -902,12 +909,12 @@ xfs_iext_insert(
 	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
 	xfs_extnum_t	i;		/* extent record index */
 
-	trace_xfs_iext_insert(ip, idx, new, state, _RET_IP_);
+	trace_xfs_iext_insert(ip, cur->idx, new, state, _RET_IP_);
 
 	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
-	xfs_iext_add(ifp, idx, count);
-	for (i = idx; i < idx + count; i++, new++)
-		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, i), new);
+	xfs_iext_add(ifp, cur->idx, count);
+	for (i = 0; i < count; i++, new++)
+		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx + i), new);
 }
 
 /*
@@ -1145,7 +1152,7 @@ xfs_iext_add_indirect_multi(
 void
 xfs_iext_remove(
 	xfs_inode_t	*ip,		/* incore inode pointer */
-	xfs_extnum_t	idx,		/* index to begin removing exts */
+	struct xfs_iext_cursor *cur,
 	int		ext_diff,	/* number of extents to remove */
 	int		state)		/* type of extent conversion */
 {
@@ -1153,7 +1160,7 @@ xfs_iext_remove(
 	xfs_extnum_t	nextents;	/* number of extents in file */
 	int		new_size;	/* size of extents after removal */
 
-	trace_xfs_iext_remove(ip, idx, state, _RET_IP_);
+	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
 
 	ASSERT(ext_diff > 0);
 	nextents = xfs_iext_count(ifp);
@@ -1162,11 +1169,11 @@ xfs_iext_remove(
 	if (new_size == 0) {
 		xfs_iext_destroy(ifp);
 	} else if (ifp->if_flags & XFS_IFEXTIREC) {
-		xfs_iext_remove_indirect(ifp, idx, ext_diff);
+		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
 	} else if (ifp->if_real_bytes) {
-		xfs_iext_remove_direct(ifp, idx, ext_diff);
+		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
 	} else {
-		xfs_iext_remove_inline(ifp, idx, ext_diff);
+		xfs_iext_remove_inline(ifp, cur->idx, ext_diff);
 	}
 	ifp->if_bytes = new_size;
 }
@@ -1913,26 +1920,26 @@ xfs_ifork_init_cow(
  * Lookup the extent covering bno.
  *
  * If there is an extent covering bno return the extent index, and store the
- * expanded extent structure in *gotp, and the extent index in *idx.
+ * expanded extent structure in *gotp, and the extent cursor in *cur.
  * If there is no extent covering bno, but there is an extent after it (e.g.
- * it lies in a hole) return that extent in *gotp and its index in *idx
+ * it lies in a hole) return that extent in *gotp and its cursor in *cur
  * instead.
- * If bno is beyond the last extent return false, and return the index after
- * the last valid index in *idxp.
+ * If bno is beyond the last extent return false, and return an invalid
+ * cursor value.
  */
 bool
 xfs_iext_lookup_extent(
 	struct xfs_inode	*ip,
 	struct xfs_ifork	*ifp,
 	xfs_fileoff_t		bno,
-	xfs_extnum_t		*idxp,
+	struct xfs_iext_cursor	*cur,
 	struct xfs_bmbt_irec	*gotp)
 {
 	struct xfs_bmbt_rec_host *ep;
 
 	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
 
-	ep = xfs_iext_bno_to_ext(ifp, bno, idxp);
+	ep = xfs_iext_bno_to_ext(ifp, bno, &cur->idx);
 	if (!ep)
 		return false;
 	xfs_bmbt_get_all(ep, gotp);
@@ -1948,31 +1955,31 @@ xfs_iext_lookup_extent_before(
 	struct xfs_inode	*ip,
 	struct xfs_ifork	*ifp,
 	xfs_fileoff_t		*end,
-	xfs_extnum_t		*idxp,
+	struct xfs_iext_cursor	*cur,
 	struct xfs_bmbt_irec	*gotp)
 {
-	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, idxp, gotp) &&
+	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
 	    gotp->br_startoff <= *end - 1)
 		return true;
-	if (!xfs_iext_get_extent(ifp, --*idxp, gotp))
+	if (!xfs_iext_prev_extent(ifp, cur, gotp))
 		return false;
 	*end = gotp->br_startoff + gotp->br_blockcount;
 	return true;
 }
 
 /*
- * Return true if there is an extent at index idx, and return the expanded
- * extent structure at idx in that case.  Else return false.
+ * Return true if there is an extent at cursor cur and return the expanded
+ * extent structure at cur in gotp in that case.  Else return false.
  */
 bool
 xfs_iext_get_extent(
 	struct xfs_ifork	*ifp,
-	xfs_extnum_t		idx,
+	struct xfs_iext_cursor	*cur,
 	struct xfs_bmbt_irec	*gotp)
 {
-	if (idx < 0 || idx >= xfs_iext_count(ifp))
+	if (cur->idx < 0 || cur->idx >= xfs_iext_count(ifp))
 		return false;
-	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), gotp);
+	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
 	return true;
 }
 
@@ -1980,15 +1987,15 @@ void
 xfs_iext_update_extent(
 	struct xfs_inode	*ip,
 	int			state,
-	xfs_extnum_t		idx,
+	struct xfs_iext_cursor	*cur,
 	struct xfs_bmbt_irec	*gotp)
 {
 	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
 
-	ASSERT(idx >= 0);
-	ASSERT(idx < xfs_iext_count(ifp));
+	ASSERT(cur->idx >= 0);
+	ASSERT(cur->idx < xfs_iext_count(ifp));
 
-	trace_xfs_bmap_pre_update(ip, idx, state, _RET_IP_);
-	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, idx), gotp);
-	trace_xfs_bmap_post_update(ip, idx, state, _RET_IP_);
+	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
+	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
+	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
 }
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index 113fd42ec36d..dc347dd9dc78 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -151,12 +151,13 @@ void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
 struct xfs_bmbt_rec_host *
 		xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
 xfs_extnum_t	xfs_iext_count(struct xfs_ifork *);
-void		xfs_iext_insert(struct xfs_inode *, xfs_extnum_t, xfs_extnum_t,
-				struct xfs_bmbt_irec *, int);
+void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
+			xfs_extnum_t, struct xfs_bmbt_irec *, int);
 void		xfs_iext_add(struct xfs_ifork *, xfs_extnum_t, int);
 void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
 					    xfs_extnum_t, int);
-void		xfs_iext_remove(struct xfs_inode *, xfs_extnum_t, int, int);
+void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
+			int, int);
 void		xfs_iext_remove_inline(struct xfs_ifork *, xfs_extnum_t, int);
 void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
 void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
@@ -182,15 +183,85 @@ void		xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
 
 bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
 			struct xfs_ifork *ifp, xfs_fileoff_t bno,
-			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
+			struct xfs_iext_cursor *cur,
+			 struct xfs_bmbt_irec *gotp);
 bool		xfs_iext_lookup_extent_before(struct xfs_inode *ip,
 			struct xfs_ifork *ifp, xfs_fileoff_t *end,
-			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
-
-bool		xfs_iext_get_extent(struct xfs_ifork *ifp, xfs_extnum_t idx,
+			struct xfs_iext_cursor *cur,
+			struct xfs_bmbt_irec *gotp);
+bool		xfs_iext_get_extent(struct xfs_ifork *ifp,
+			struct xfs_iext_cursor *cur,
 			struct xfs_bmbt_irec *gotp);
 void		xfs_iext_update_extent(struct xfs_inode *ip, int state,
-			xfs_extnum_t idx, struct xfs_bmbt_irec *gotp);
+			struct xfs_iext_cursor *cur,
+			struct xfs_bmbt_irec *gotp);
+
+static inline void xfs_iext_first(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur)
+{
+	cur->idx = 0;
+}
+
+static inline void xfs_iext_last(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur)
+{
+	cur->idx = xfs_iext_count(ifp) - 1;
+}
+
+static inline void xfs_iext_next(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur)
+{
+	cur->idx++;
+}
+
+static inline void xfs_iext_prev(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur)
+{
+	cur->idx--;
+}
+
+static inline bool xfs_iext_next_extent(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
+{
+	xfs_iext_next(ifp, cur);
+	return xfs_iext_get_extent(ifp, cur, gotp);
+}
+
+static inline bool xfs_iext_prev_extent(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
+{
+	xfs_iext_prev(ifp, cur);
+	return xfs_iext_get_extent(ifp, cur, gotp);
+}
+
+/*
+ * Return the extent after cur in gotp without updating the cursor.
+ */
+static inline bool xfs_iext_peek_next_extent(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
+{
+	struct xfs_iext_cursor ncur = *cur;
+
+	xfs_iext_next(ifp, &ncur);
+	return xfs_iext_get_extent(ifp, &ncur, gotp);
+}
+
+/*
+ * Return the extent before cur in gotp without updating the cursor.
+ */
+static inline bool xfs_iext_peek_prev_extent(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
+{
+	struct xfs_iext_cursor ncur = *cur;
+
+	xfs_iext_prev(ifp, &ncur);
+	return xfs_iext_get_extent(ifp, &ncur, gotp);
+}
+
+#define for_each_iext(ifp, ext, got)			\
+	for (xfs_iext_first((ifp), (ext));		\
+	     xfs_iext_get_extent((ifp), (ext), (got));	\
+	     xfs_iext_next((ifp), (ext)))
 
 extern struct kmem_zone	*xfs_ifork_zone;
 
diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
index f04dbfb2f50d..5da6382bdaf1 100644
--- a/fs/xfs/libxfs/xfs_types.h
+++ b/fs/xfs/libxfs/xfs_types.h
@@ -142,5 +142,8 @@ typedef uint32_t	xfs_dqid_t;
 #define	XFS_NBWORD	(1 << XFS_NBWORDLOG)
 #define	XFS_WORDMASK	((1 << XFS_WORDLOG) - 1)
 
+struct xfs_iext_cursor {
+	xfs_extnum_t		idx;
+};
 
 #endif	/* __XFS_TYPES_H__ */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 3c17b182616f..778b709dbd0c 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -237,7 +237,7 @@ xfs_scrub_bmap(
 	struct xfs_inode		*ip = sc->ip;
 	struct xfs_ifork		*ifp;
 	xfs_fileoff_t			endoff;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 	bool				found;
 	int				error = 0;
 
@@ -317,9 +317,9 @@ xfs_scrub_bmap(
 	/* Scrub extent records. */
 	info.lastoff = 0;
 	ifp = XFS_IFORK_PTR(ip, whichfork);
-	for (found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &irec);
+	for (found = xfs_iext_lookup_extent(ip, ifp, 0, &ext, &irec);
 	     found != 0;
-	     found = xfs_iext_get_extent(ifp, ++idx, &irec)) {
+	     found = xfs_iext_next_extent(ifp, &ext, &irec)) {
 		if (xfs_scrub_should_terminate(sc, &error))
 			break;
 		if (isnullstartblock(irec.br_startblock))
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 169fb10daaaa..46765102638c 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -614,7 +614,7 @@ xfs_scrub_directory_blocks(
 	xfs_fileoff_t			leaf_lblk;
 	xfs_fileoff_t			free_lblk;
 	xfs_fileoff_t			lblk;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 	xfs_dablk_t			dabno;
 	bool				found;
 	int				is_block = 0;
@@ -639,7 +639,7 @@ xfs_scrub_directory_blocks(
 		goto out;
 
 	/* Iterate all the data extents in the directory... */
-	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
 	while (found) {
 		/* Block directories only have a single block at offset 0. */
 		if (is_block &&
@@ -676,17 +676,17 @@ xfs_scrub_directory_blocks(
 		}
 		dabno = got.br_startoff + got.br_blockcount;
 		lblk = roundup(dabno, args.geo->fsbcount);
-		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
 	}
 
 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
 		goto out;
 
 	/* Look for a leaf1 block, which has free info. */
-	if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &idx, &got) &&
+	if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &ext, &got) &&
 	    got.br_startoff == leaf_lblk &&
 	    got.br_blockcount == args.geo->fsbcount &&
-	    !xfs_iext_get_extent(ifp, ++idx, &got)) {
+	    !xfs_iext_next_extent(ifp, &ext, &got)) {
 		if (is_block) {
 			xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
 			goto out;
@@ -702,7 +702,7 @@ xfs_scrub_directory_blocks(
 
 	/* Scan for free blocks */
 	lblk = free_lblk;
-	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
 	while (found) {
 		/*
 		 * Dirs can't have blocks mapped above 2^32.
@@ -740,7 +740,7 @@ xfs_scrub_directory_blocks(
 		}
 		dabno = got.br_startoff + got.br_blockcount;
 		lblk = roundup(dabno, args.geo->fsbcount);
-		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
+		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
 	}
 out:
 	return error;
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 170b74c7f2d5..b6b954d5cf54 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -229,15 +229,17 @@ xfs_bmap_count_leaves(
 	struct xfs_ifork	*ifp,
 	xfs_filblks_t		*count)
 {
+	struct xfs_iext_cursor	ext;
 	struct xfs_bmbt_irec	got;
-	xfs_extnum_t		numrecs = 0, i = 0;
+	xfs_extnum_t		numrecs = 0;
 
-	while (xfs_iext_get_extent(ifp, i++, &got)) {
+	for_each_iext(ifp, &ext, &got) {
 		if (!isnullstartblock(got.br_startblock)) {
 			*count += got.br_blockcount;
 			numrecs++;
 		}
 	}
+
 	return numrecs;
 }
 
@@ -525,7 +527,7 @@ xfs_getbmap(
 	struct xfs_ifork	*ifp;
 	struct xfs_bmbt_irec	got, rec;
 	xfs_filblks_t		len;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 
 	if (bmv->bmv_iflags & ~BMV_IF_VALID)
 		return -EINVAL;
@@ -629,7 +631,7 @@ xfs_getbmap(
 			goto out_unlock_ilock;
 	}
 
-	if (!xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got)) {
+	if (!xfs_iext_lookup_extent(ip, ifp, bno, &ext, &got)) {
 		/*
 		 * Report a whole-file hole if the delalloc flag is set to
 		 * stay compatible with the old implementation.
@@ -668,7 +670,7 @@ xfs_getbmap(
 				goto out_unlock_ilock;
 		} while (xfs_getbmap_next_rec(&rec, bno));
 
-		if (!xfs_iext_get_extent(ifp, ++idx, &got)) {
+		if (!xfs_iext_next_extent(ifp, &ext, &got)) {
 			xfs_fileoff_t	end = XFS_B_TO_FSB(mp, XFS_ISIZE(ip));
 
 			out[bmv->bmv_entries - 1].bmv_oflags |= BMV_OF_LAST;
diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c
index 238e3650a9d2..ad54fd775bda 100644
--- a/fs/xfs/xfs_dir2_readdir.c
+++ b/fs/xfs/xfs_dir2_readdir.c
@@ -266,7 +266,7 @@ xfs_dir2_leaf_readbuf(
 	xfs_dablk_t		next_ra;
 	xfs_dablk_t		map_off;
 	xfs_dablk_t		last_da;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 	int			ra_want;
 	int			error = 0;
 
@@ -283,7 +283,7 @@ xfs_dir2_leaf_readbuf(
 	 */
 	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
 	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *cur_off));
-	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &idx, &map))
+	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &ext, &map))
 		goto out;
 	if (map.br_startoff >= last_da)
 		goto out;
@@ -311,7 +311,7 @@ xfs_dir2_leaf_readbuf(
 	if (next_ra >= last_da)
 		goto out_no_ra;
 	if (map.br_blockcount < geo->fsbcount &&
-	    !xfs_iext_get_extent(ifp, ++idx, &map))
+	    !xfs_iext_next_extent(ifp, &ext, &map))
 		goto out_no_ra;
 	if (map.br_startoff >= last_da)
 		goto out_no_ra;
@@ -334,7 +334,7 @@ xfs_dir2_leaf_readbuf(
 			ra_want -= geo->fsbcount;
 			next_ra += geo->fsbcount;
 		}
-		if (!xfs_iext_get_extent(ifp, ++idx, &map)) {
+		if (!xfs_iext_next_extent(ifp, &ext, &map)) {
 			*ra_blk = last_da;
 			break;
 		}
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index cd82429d8df7..8338b894d54f 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -703,7 +703,7 @@ xfs_dq_get_next_id(
 	xfs_dqid_t		next_id = *id + 1; /* simple advance */
 	uint			lock_flags;
 	struct xfs_bmbt_irec	got;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	cur;
 	xfs_fsblock_t		start;
 	int			error = 0;
 
@@ -727,7 +727,7 @@ xfs_dq_get_next_id(
 			return error;
 	}
 
-	if (xfs_iext_lookup_extent(quotip, &quotip->i_df, start, &idx, &got)) {
+	if (xfs_iext_lookup_extent(quotip, &quotip->i_df, start, &cur, &got)) {
 		/* contiguous chunk, bump startoff for the id calculation */
 		if (got.br_startoff < start)
 			got.br_startoff = start;
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index f179bdf1644d..046ade883611 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -389,7 +389,7 @@ xfs_iomap_prealloc_size(
 	struct xfs_inode	*ip,
 	loff_t			offset,
 	loff_t			count,
-	xfs_extnum_t		idx)
+	struct xfs_iext_cursor	*ext)
 {
 	struct xfs_mount	*mp = ip->i_mount;
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
@@ -414,7 +414,7 @@ xfs_iomap_prealloc_size(
 	 */
 	if ((mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) ||
 	    XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) ||
-	    !xfs_iext_get_extent(ifp, idx - 1, &prev) ||
+	    !xfs_iext_peek_prev_extent(ifp, ext, &prev) ||
 	    prev.br_startoff + prev.br_blockcount < offset_fsb)
 		return mp->m_writeio_blocks;
 
@@ -532,7 +532,7 @@ xfs_file_iomap_begin_delay(
 	xfs_fileoff_t		end_fsb;
 	int			error = 0, eof = 0;
 	struct xfs_bmbt_irec	got;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 	xfs_fsblock_t		prealloc_blocks = 0;
 
 	ASSERT(!XFS_IS_REALTIME_INODE(ip));
@@ -557,7 +557,7 @@ xfs_file_iomap_begin_delay(
 			goto out_unlock;
 	}
 
-	eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
+	eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got);
 	if (!eof && got.br_startoff <= offset_fsb) {
 		if (xfs_is_reflink_inode(ip)) {
 			bool		shared;
@@ -591,7 +591,8 @@ xfs_file_iomap_begin_delay(
 	end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
 
 	if (eof) {
-		prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count, idx);
+		prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count,
+				&ext);
 		if (prealloc_blocks) {
 			xfs_extlen_t	align;
 			xfs_off_t	end_offset;
@@ -613,7 +614,7 @@ xfs_file_iomap_begin_delay(
 
 retry:
 	error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
-			end_fsb - offset_fsb, prealloc_blocks, &got, &idx, eof);
+			end_fsb - offset_fsb, prealloc_blocks, &got, &ext, eof);
 	switch (error) {
 	case 0:
 		break;
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 1205747e1409..cadf0ff68003 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -273,7 +273,7 @@ xfs_reflink_reserve_cow(
 	struct xfs_bmbt_irec	got;
 	int			error = 0;
 	bool			eof = false, trimmed;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 
 	/*
 	 * Search the COW fork extent list first.  This serves two purposes:
@@ -284,7 +284,7 @@ xfs_reflink_reserve_cow(
 	 * tree.
 	 */
 
-	if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &idx, &got))
+	if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &ext, &got))
 		eof = true;
 	if (!eof && got.br_startoff <= imap->br_startoff) {
 		trace_xfs_reflink_cow_found(ip, imap);
@@ -312,7 +312,7 @@ xfs_reflink_reserve_cow(
 		return error;
 
 	error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, imap->br_startoff,
-			imap->br_blockcount, 0, &got, &idx, eof);
+			imap->br_blockcount, 0, &got, &ext, eof);
 	if (error == -ENOSPC || error == -EDQUOT)
 		trace_xfs_reflink_cow_enospc(ip, imap);
 	if (error)
@@ -359,16 +359,16 @@ xfs_reflink_convert_cow(
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
 	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
 	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + count);
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 	bool			found;
 	int			error = 0;
 
 	xfs_ilock(ip, XFS_ILOCK_EXCL);
 
 	/* Convert all the extents to real from unwritten. */
-	for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
+	for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got);
 	     found && got.br_startoff < end_fsb;
-	     found = xfs_iext_get_extent(ifp, ++idx, &got)) {
+	     found = xfs_iext_next_extent(ifp, &ext, &got)) {
 		error = xfs_reflink_convert_cow_extent(ip, &got, offset_fsb,
 				end_fsb - offset_fsb, &dfops);
 		if (error)
@@ -399,7 +399,7 @@ xfs_reflink_allocate_cow(
 	bool			trimmed;
 	xfs_filblks_t		resaligned;
 	xfs_extlen_t		resblks = 0;
-	xfs_extnum_t		idx;
+	struct xfs_iext_cursor	ext;
 
 retry:
 	ASSERT(xfs_is_reflink_inode(ip));
@@ -409,7 +409,7 @@ xfs_reflink_allocate_cow(
 	 * Even if the extent is not shared we might have a preallocation for
 	 * it in the COW fork.  If so use it.
 	 */
-	if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &idx, &got) &&
+	if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &ext, &got) &&
 	    got.br_startoff <= offset_fsb) {
 		*shared = true;
 
@@ -496,13 +496,13 @@ xfs_reflink_find_cow_mapping(
 	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
 	xfs_fileoff_t			offset_fsb;
 	struct xfs_bmbt_irec		got;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 
 	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
 	ASSERT(xfs_is_reflink_inode(ip));
 
 	offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
-	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
+	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
 		return false;
 	if (got.br_startoff > offset_fsb)
 		return false;
@@ -524,18 +524,18 @@ xfs_reflink_trim_irec_to_next_cow(
 {
 	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
 	struct xfs_bmbt_irec		got;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 
 	if (!xfs_is_reflink_inode(ip))
 		return;
 
 	/* Find the extent in the CoW fork. */
-	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
+	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
 		return;
 
 	/* This is the extent before; try sliding up one. */
 	if (got.br_startoff < offset_fsb) {
-		if (!xfs_iext_get_extent(ifp, idx + 1, &got))
+		if (!xfs_iext_next_extent(ifp, &ext, &got))
 			return;
 	}
 
@@ -562,14 +562,14 @@ xfs_reflink_cancel_cow_blocks(
 {
 	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
 	struct xfs_bmbt_irec		got, del;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 	xfs_fsblock_t			firstfsb;
 	struct xfs_defer_ops		dfops;
 	int				error = 0;
 
 	if (!xfs_is_reflink_inode(ip))
 		return 0;
-	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
+	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
 		return 0;
 
 	while (got.br_startoff < end_fsb) {
@@ -579,7 +579,7 @@ xfs_reflink_cancel_cow_blocks(
 
 		if (isnullstartblock(del.br_startblock)) {
 			error = xfs_bmap_del_extent_delay(ip, XFS_COW_FORK,
-					&idx, &got, &del);
+					&ext, &got, &del);
 			if (error)
 				break;
 		} else if (del.br_state == XFS_EXT_UNWRITTEN || cancel_real) {
@@ -610,10 +610,10 @@ xfs_reflink_cancel_cow_blocks(
 			}
 
 			/* Remove the mapping from the CoW fork. */
-			xfs_bmap_del_extent_cow(ip, &idx, &got, &del);
+			xfs_bmap_del_extent_cow(ip, &ext, &got, &del);
 		}
 
-		if (!xfs_iext_get_extent(ifp, ++idx, &got))
+		if (!xfs_iext_next_extent(ifp, &ext, &got))
 			break;
 	}
 
@@ -698,7 +698,7 @@ xfs_reflink_end_cow(
 	int				error;
 	unsigned int			resblks;
 	xfs_filblks_t			rlen;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 
 	trace_xfs_reflink_end_cow(ip, offset, count);
 
@@ -738,7 +738,7 @@ xfs_reflink_end_cow(
 	 * left by the time I/O completes for the loser of the race.  In that
 	 * case we are done.
 	 */
-	if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &idx, &got))
+	if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &ext, &got))
 		goto out_cancel;
 
 	/* Walk backwards until we're out of the I/O range... */
@@ -746,9 +746,9 @@ xfs_reflink_end_cow(
 		del = got;
 		xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb);
 
-		/* Extent delete may have bumped idx forward */
+		/* Extent delete may have bumped ext forward */
 		if (!del.br_blockcount) {
-			idx--;
+			xfs_iext_prev(ifp, &ext);
 			goto next_extent;
 		}
 
@@ -760,7 +760,7 @@ xfs_reflink_end_cow(
 		 * allocated but have not yet been involved in a write.
 		 */
 		if (got.br_state == XFS_EXT_UNWRITTEN) {
-			idx--;
+			xfs_iext_prev(ifp, &ext);
 			goto next_extent;
 		}
 
@@ -791,14 +791,14 @@ xfs_reflink_end_cow(
 			goto out_defer;
 
 		/* Remove the mapping from the CoW fork. */
-		xfs_bmap_del_extent_cow(ip, &idx, &got, &del);
+		xfs_bmap_del_extent_cow(ip, &ext, &got, &del);
 
 		xfs_defer_ijoin(&dfops, ip);
 		error = xfs_defer_finish(&tp, &dfops);
 		if (error)
 			goto out_defer;
 next_extent:
-		if (!xfs_iext_get_extent(ifp, idx, &got))
+		if (!xfs_iext_get_extent(ifp, &ext, &got))
 			break;
 	}
 
@@ -1428,7 +1428,7 @@ xfs_reflink_inode_has_shared_extents(
 	xfs_extlen_t			aglen;
 	xfs_agblock_t			rbno;
 	xfs_extlen_t			rlen;
-	xfs_extnum_t			idx;
+	struct xfs_iext_cursor		ext;
 	bool				found;
 	int				error;
 
@@ -1440,7 +1440,7 @@ xfs_reflink_inode_has_shared_extents(
 	}
 
 	*has_shared = false;
-	found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &got);
+	found = xfs_iext_lookup_extent(ip, ifp, 0, &ext, &got);
 	while (found) {
 		if (isnullstartblock(got.br_startblock) ||
 		    got.br_state != XFS_EXT_NORM)
@@ -1459,7 +1459,7 @@ xfs_reflink_inode_has_shared_extents(
 			return 0;
 		}
 next:
-		found = xfs_iext_get_extent(ifp, ++idx, &got);
+		found = xfs_iext_next_extent(ifp, &ext, &got);
 	}
 
 	return 0;
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 665ef6cca90c..667bfce802cd 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -258,9 +258,9 @@ TRACE_EVENT(xfs_iext_insert,
 );
 
 DECLARE_EVENT_CLASS(xfs_bmap_class,
-	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx, int state,
+	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state,
 		 unsigned long caller_ip),
-	TP_ARGS(ip, idx, state, caller_ip),
+	TP_ARGS(ip, cur, state, caller_ip),
 	TP_STRUCT__entry(
 		__field(dev_t, dev)
 		__field(xfs_ino_t, ino)
@@ -277,10 +277,10 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
 		struct xfs_bmbt_irec	r;
 
 		ifp = xfs_iext_state_to_fork(ip, state);
-		xfs_iext_get_extent(ifp, idx, &r);
+		xfs_iext_get_extent(ifp, cur, &r);
 		__entry->dev = VFS_I(ip)->i_sb->s_dev;
 		__entry->ino = ip->i_ino;
-		__entry->idx = idx;
+		__entry->idx = cur->idx;
 		__entry->startoff = r.br_startoff;
 		__entry->startblock = r.br_startblock;
 		__entry->blockcount = r.br_blockcount;
@@ -303,9 +303,9 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
 
 #define DEFINE_BMAP_EVENT(name) \
 DEFINE_EVENT(xfs_bmap_class, name, \
-	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx, int state, \
+	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state, \
 		 unsigned long caller_ip), \
-	TP_ARGS(ip, idx, state, caller_ip))
+	TP_ARGS(ip, cur, state, caller_ip))
 DEFINE_BMAP_EVENT(xfs_iext_remove);
 DEFINE_BMAP_EVENT(xfs_bmap_pre_update);
 DEFINE_BMAP_EVENT(xfs_bmap_post_update);
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 13/18] xfs: iterate backwards in xfs_reflink_cancel_cow_blocks
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (11 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 22:10   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow Christoph Hellwig
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

This follow the iteration oder for extent deletion in both the regular
reflink I/O completion path and normal block truncation, and makes
implementing the new incore extent list easier.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_reflink.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index cadf0ff68003..43bec0def51d 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -569,12 +569,20 @@ xfs_reflink_cancel_cow_blocks(
 
 	if (!xfs_is_reflink_inode(ip))
 		return 0;
-	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
+	if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &ext, &got))
 		return 0;
 
-	while (got.br_startoff < end_fsb) {
+	/* Walk backwards until we're out of the I/O range... */
+	while (got.br_startoff + got.br_blockcount > offset_fsb) {
 		del = got;
 		xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb);
+
+		/* Extent delete may have bumped ext forward */
+		if (!del.br_blockcount) {
+			xfs_iext_prev(ifp, &ext);
+			goto next_extent;
+		}
+
 		trace_xfs_reflink_cancel_cow(ip, &del);
 
 		if (isnullstartblock(del.br_startblock)) {
@@ -612,8 +620,8 @@ xfs_reflink_cancel_cow_blocks(
 			/* Remove the mapping from the CoW fork. */
 			xfs_bmap_del_extent_cow(ip, &ext, &got, &del);
 		}
-
-		if (!xfs_iext_next_extent(ifp, &ext, &got))
+next_extent:
+		if (!xfs_iext_get_extent(ifp, &ext, &got))
 			break;
 	}
 
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (12 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 13/18] xfs: iterate backwards in xfs_reflink_cancel_cow_blocks Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 22:20   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork Christoph Hellwig
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Instead of looking up extents to convert and calling xfs_bmapi_write on
each of them just let xfs_bmapi_write handle the full range.  To make
this robust add a new XFS_BMAPI_CONVERT_ONLY that only converts ranges
and never allocates blocks.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c |  3 ++-
 fs/xfs/libxfs/xfs_bmap.h |  3 +++
 fs/xfs/xfs_reflink.c     | 29 +++++++++++------------------
 3 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 453dc1ae76ab..e020bd3f8717 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -4330,7 +4330,8 @@ xfs_bmapi_write(
 		 * First, deal with the hole before the allocated space
 		 * that we found, if any.
 		 */
-		if (need_alloc || wasdelay) {
+		if ((need_alloc || wasdelay) &&
+		    !(flags & XFS_BMAPI_CONVERT_ONLY)) {
 			bma.eof = eof;
 			bma.conv = !!(flags & XFS_BMAPI_CONVERT);
 			bma.wasdel = wasdelay;
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index 195f335f4615..fba60ed7aea5 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -113,6 +113,9 @@ struct xfs_extent_free_item
 /* Only convert delalloc space, don't allocate entirely new extents */
 #define XFS_BMAPI_DELALLOC	0x400
 
+/* Only convert unwritten extents, don't allocate new hotels */
+#define XFS_BMAPI_CONVERT_ONLY	0x800
+
 #define XFS_BMAPI_FLAGS \
 	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
 	{ XFS_BMAPI_METADATA,	"METADATA" }, \
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 43bec0def51d..99c5d4dbc5d3 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -353,29 +353,22 @@ xfs_reflink_convert_cow(
 	xfs_off_t		offset,
 	xfs_off_t		count)
 {
-	struct xfs_bmbt_irec	got;
-	struct xfs_defer_ops	dfops;
 	struct xfs_mount	*mp = ip->i_mount;
-	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
 	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
 	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + count);
-	struct xfs_iext_cursor	ext;
-	bool			found;
-	int			error = 0;
-
-	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	xfs_filblks_t		count_fsb = end_fsb - offset_fsb;
+	struct xfs_bmbt_irec	imap;
+	struct xfs_defer_ops	dfops;
+	xfs_fsblock_t		first_block = NULLFSBLOCK;
+	int			nimaps = 1, error = 0;
 
-	/* Convert all the extents to real from unwritten. */
-	for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got);
-	     found && got.br_startoff < end_fsb;
-	     found = xfs_iext_next_extent(ifp, &ext, &got)) {
-		error = xfs_reflink_convert_cow_extent(ip, &got, offset_fsb,
-				end_fsb - offset_fsb, &dfops);
-		if (error)
-			break;
-	}
+	ASSERT(count != 0);
 
-	/* Finish up. */
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	error = xfs_bmapi_write(NULL, ip, offset_fsb, count_fsb,
+			XFS_BMAPI_COWFORK | XFS_BMAPI_CONVERT |
+			XFS_BMAPI_CONVERT_ONLY, &first_block, 0, &imap, &nimaps,
+			&dfops);
 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 	return error;
 }
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (13 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 22:35   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 16/18] xfs: use a b+tree for the in-core extent list Christoph Hellwig
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Supporting a small bit of data inside the inode fork blows up the fork size
a lot, removing the 32 bytes of inline data halves the effective size of
the inode fork (and it still has a lot of unused padding left), and the
performance of a single kmalloc doesn't show up compared to the size to read
an inode or create one.

It also simplifies the fork management code a lot.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_inode_fork.c | 185 +++--------------------------------------
 fs/xfs/libxfs/xfs_inode_fork.h |  11 ---
 fs/xfs/xfs_bmap_util.c         |  15 ----
 3 files changed, 13 insertions(+), 198 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index c9e10d4818b7..5ac341d2b093 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -269,19 +269,14 @@ xfs_init_local_fork(
 	if (zero_terminate)
 		mem_size++;
 
-	if (size == 0)
-		ifp->if_u1.if_data = NULL;
-	else if (mem_size <= sizeof(ifp->if_u2.if_inline_data))
-		ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
-	else {
+	if (size) {
 		real_size = roundup(mem_size, 4);
 		ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
-	}
-
-	if (size) {
 		memcpy(ifp->if_u1.if_data, data, size);
 		if (zero_terminate)
 			ifp->if_u1.if_data[size] = '\0';
+	} else {
+		ifp->if_u1.if_data = NULL;
 	}
 
 	ifp->if_bytes = size;
@@ -292,13 +287,6 @@ xfs_init_local_fork(
 
 /*
  * The file is in-lined in the on-disk inode.
- * If it fits into if_inline_data, then copy
- * it there, otherwise allocate a buffer for it
- * and copy the data there.  Either way, set
- * if_data to point at the data.
- * If we allocate a buffer for the data, make
- * sure that its size is a multiple of 4 and
- * record the real size in i_real_bytes.
  */
 STATIC int
 xfs_iformat_local(
@@ -328,9 +316,7 @@ xfs_iformat_local(
 
 /*
  * The file consists of a set of extents all of which fit into the on-disk
- * inode.  If there are few enough extents to fit into the if_inline_ext, then
- * copy them there.  Otherwise allocate a buffer for them and copy them into it.
- * Either way, set if_extents to point at the extents.
+ * inode.
  */
 STATIC int
 xfs_iformat_extents(
@@ -362,8 +348,6 @@ xfs_iformat_extents(
 	ifp->if_real_bytes = 0;
 	if (nex == 0)
 		ifp->if_u1.if_extents = NULL;
-	else if (nex <= XFS_INLINE_EXTS)
-		ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
 	else
 		xfs_iext_add(ifp, 0, nex);
 
@@ -618,26 +602,9 @@ xfs_idata_realloc(
 	ASSERT(new_size >= 0);
 
 	if (new_size == 0) {
-		if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
-			kmem_free(ifp->if_u1.if_data);
-		}
+		kmem_free(ifp->if_u1.if_data);
 		ifp->if_u1.if_data = NULL;
 		real_size = 0;
-	} else if (new_size <= sizeof(ifp->if_u2.if_inline_data)) {
-		/*
-		 * If the valid extents/data can fit in if_inline_ext/data,
-		 * copy them from the malloc'd vector and free it.
-		 */
-		if (ifp->if_u1.if_data == NULL) {
-			ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
-		} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
-			ASSERT(ifp->if_real_bytes != 0);
-			memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
-			      new_size);
-			kmem_free(ifp->if_u1.if_data);
-			ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
-		}
-		real_size = 0;
 	} else {
 		/*
 		 * Stuck with malloc/realloc.
@@ -651,7 +618,7 @@ xfs_idata_realloc(
 			ASSERT(ifp->if_real_bytes == 0);
 			ifp->if_u1.if_data = kmem_alloc(real_size,
 							KM_SLEEP | KM_NOFS);
-		} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
+		} else {
 			/*
 			 * Only do the realloc if the underlying size
 			 * is really changing.
@@ -662,12 +629,6 @@ xfs_idata_realloc(
 							real_size,
 							KM_SLEEP | KM_NOFS);
 			}
-		} else {
-			ASSERT(ifp->if_real_bytes == 0);
-			ifp->if_u1.if_data = kmem_alloc(real_size,
-							KM_SLEEP | KM_NOFS);
-			memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
-				ifp->if_bytes);
 		}
 	}
 	ifp->if_real_bytes = real_size;
@@ -695,8 +656,7 @@ xfs_idestroy_fork(
 	 * so check and free it up if we do.
 	 */
 	if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
-		if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
-		    (ifp->if_u1.if_data != NULL)) {
+		if (ifp->if_u1.if_data != NULL) {
 			ASSERT(ifp->if_real_bytes != 0);
 			kmem_free(ifp->if_u1.if_data);
 			ifp->if_u1.if_data = NULL;
@@ -704,13 +664,11 @@ xfs_idestroy_fork(
 		}
 	} else if ((ifp->if_flags & XFS_IFEXTENTS) &&
 		   ((ifp->if_flags & XFS_IFEXTIREC) ||
-		    ((ifp->if_u1.if_extents != NULL) &&
-		     (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)))) {
+		    (ifp->if_u1.if_extents != NULL))) {
 		ASSERT(ifp->if_real_bytes != 0);
 		xfs_iext_destroy(ifp);
 	}
-	ASSERT(ifp->if_u1.if_extents == NULL ||
-	       ifp->if_u1.if_extents == ifp->if_u2.if_inline_ext);
+	ASSERT(ifp->if_u1.if_extents == NULL);
 	ASSERT(ifp->if_real_bytes == 0);
 	if (whichfork == XFS_ATTR_FORK) {
 		kmem_zone_free(xfs_ifork_zone, ip->i_afp);
@@ -943,28 +901,14 @@ xfs_iext_add(
 	ASSERT((idx >= 0) && (idx <= nextents));
 	byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
 	new_size = ifp->if_bytes + byte_diff;
+
 	/*
-	 * If the new number of extents (nextents + ext_diff)
-	 * fits inside the inode, then continue to use the inline
-	 * extent buffer.
-	 */
-	if (nextents + ext_diff <= XFS_INLINE_EXTS) {
-		if (idx < nextents) {
-			memmove(&ifp->if_u2.if_inline_ext[idx + ext_diff],
-				&ifp->if_u2.if_inline_ext[idx],
-				(nextents - idx) * sizeof(xfs_bmbt_rec_t));
-			memset(&ifp->if_u2.if_inline_ext[idx], 0, byte_diff);
-		}
-		ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
-		ifp->if_real_bytes = 0;
-	}
-	/*
-	 * Otherwise use a linear (direct) extent list.
+	 * Use a linear (direct) extent list.
 	 * If the extents are currently inside the inode,
 	 * xfs_iext_realloc_direct will switch us from
 	 * inline to direct extent allocation mode.
 	 */
-	else if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
+	if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
 		xfs_iext_realloc_direct(ifp, new_size);
 		if (idx < nextents) {
 			memmove(&ifp->if_u1.if_extents[idx + ext_diff],
@@ -1172,43 +1116,10 @@ xfs_iext_remove(
 		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
 	} else if (ifp->if_real_bytes) {
 		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
-	} else {
-		xfs_iext_remove_inline(ifp, cur->idx, ext_diff);
 	}
 	ifp->if_bytes = new_size;
 }
 
-/*
- * This removes ext_diff extents from the inline buffer, beginning
- * at extent index idx.
- */
-void
-xfs_iext_remove_inline(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	idx,		/* index to begin removing exts */
-	int		ext_diff)	/* number of extents to remove */
-{
-	int		nextents;	/* number of extents in file */
-
-	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
-	ASSERT(idx < XFS_INLINE_EXTS);
-	nextents = xfs_iext_count(ifp);
-	ASSERT(((nextents - ext_diff) > 0) &&
-		(nextents - ext_diff) < XFS_INLINE_EXTS);
-
-	if (idx + ext_diff < nextents) {
-		memmove(&ifp->if_u2.if_inline_ext[idx],
-			&ifp->if_u2.if_inline_ext[idx + ext_diff],
-			(nextents - (idx + ext_diff)) *
-			 sizeof(xfs_bmbt_rec_t));
-		memset(&ifp->if_u2.if_inline_ext[nextents - ext_diff],
-			0, ext_diff * sizeof(xfs_bmbt_rec_t));
-	} else {
-		memset(&ifp->if_u2.if_inline_ext[idx], 0,
-			ext_diff * sizeof(xfs_bmbt_rec_t));
-	}
-}
-
 /*
  * This removes ext_diff extents from a linear (direct) extent list,
  * beginning at extent index idx. If the extents are being removed
@@ -1351,16 +1262,7 @@ xfs_iext_realloc_direct(
 	/* Free extent records */
 	if (new_size == 0) {
 		xfs_iext_destroy(ifp);
-	}
-	/* Resize direct extent list and zero any new bytes */
-	else if (ifp->if_real_bytes) {
-		/* Check if extents will fit inside the inode */
-		if (new_size <= XFS_INLINE_EXTS * sizeof(xfs_bmbt_rec_t)) {
-			xfs_iext_direct_to_inline(ifp, new_size /
-				(uint)sizeof(xfs_bmbt_rec_t));
-			ifp->if_bytes = new_size;
-			return;
-		}
+	} else {
 		if (!is_power_of_2(new_size)){
 			rnew_size = roundup_pow_of_two(new_size);
 		}
@@ -1375,63 +1277,10 @@ xfs_iext_realloc_direct(
 				rnew_size - ifp->if_real_bytes);
 		}
 	}
-	/* Switch from the inline extent buffer to a direct extent list */
-	else {
-		if (!is_power_of_2(new_size)) {
-			rnew_size = roundup_pow_of_two(new_size);
-		}
-		xfs_iext_inline_to_direct(ifp, rnew_size);
-	}
 	ifp->if_real_bytes = rnew_size;
 	ifp->if_bytes = new_size;
 }
 
-/*
- * Switch from linear (direct) extent records to inline buffer.
- */
-void
-xfs_iext_direct_to_inline(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	nextents)	/* number of extents in file */
-{
-	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
-	ASSERT(nextents <= XFS_INLINE_EXTS);
-	/*
-	 * The inline buffer was zeroed when we switched
-	 * from inline to direct extent allocation mode,
-	 * so we don't need to clear it here.
-	 */
-	memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
-		nextents * sizeof(xfs_bmbt_rec_t));
-	kmem_free(ifp->if_u1.if_extents);
-	ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
-	ifp->if_real_bytes = 0;
-}
-
-/*
- * Switch from inline buffer to linear (direct) extent records.
- * new_size should already be rounded up to the next power of 2
- * by the caller (when appropriate), so use new_size as it is.
- * However, since new_size may be rounded up, we can't update
- * if_bytes here. It is the caller's responsibility to update
- * if_bytes upon return.
- */
-void
-xfs_iext_inline_to_direct(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	int		new_size)	/* number of extents in file */
-{
-	ifp->if_u1.if_extents = kmem_alloc(new_size, KM_NOFS);
-	memset(ifp->if_u1.if_extents, 0, new_size);
-	if (ifp->if_bytes) {
-		memcpy(ifp->if_u1.if_extents, ifp->if_u2.if_inline_ext,
-			ifp->if_bytes);
-		memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
-			sizeof(xfs_bmbt_rec_t));
-	}
-	ifp->if_real_bytes = new_size;
-}
-
 /*
  * Resize an extent indirection array to new_size bytes.
  */
@@ -1511,9 +1360,6 @@ xfs_iext_destroy(
 		xfs_iext_irec_remove_all(ifp);
 	} else if (ifp->if_real_bytes) {
 		kmem_free(ifp->if_u1.if_extents);
-	} else if (ifp->if_bytes) {
-		memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
-			sizeof(xfs_bmbt_rec_t));
 	}
 	ifp->if_u1.if_extents = NULL;
 	ifp->if_real_bytes = 0;
@@ -1708,8 +1554,6 @@ xfs_iext_irec_init(
 
 	if (nextents == 0) {
 		ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
-	} else if (!ifp->if_real_bytes) {
-		xfs_iext_inline_to_direct(ifp, XFS_IEXT_BUFSZ);
 	} else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
 		xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
 	}
@@ -1829,9 +1673,6 @@ xfs_iext_irec_compact(
 
 	if (nextents == 0) {
 		xfs_iext_destroy(ifp);
-	} else if (nextents <= XFS_INLINE_EXTS) {
-		xfs_iext_indirect_to_direct(ifp);
-		xfs_iext_direct_to_inline(ifp, nextents);
 	} else if (nextents <= XFS_LINEAR_EXTS) {
 		xfs_iext_indirect_to_direct(ifp);
 	} else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index dc347dd9dc78..508f13784334 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -51,8 +51,6 @@ typedef struct xfs_ext_irec {
  */
 #define	XFS_IEXT_BUFSZ		4096
 #define	XFS_LINEAR_EXTS		(XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t))
-#define	XFS_INLINE_EXTS		2
-#define	XFS_INLINE_DATA		32
 typedef struct xfs_ifork {
 	int			if_bytes;	/* bytes in if_u1 */
 	int			if_real_bytes;	/* bytes allocated in if_u1 */
@@ -64,12 +62,6 @@ typedef struct xfs_ifork {
 		xfs_ext_irec_t	*if_ext_irec;	/* irec map file exts */
 		char		*if_data;	/* inline file data */
 	} if_u1;
-	union {
-		xfs_bmbt_rec_host_t if_inline_ext[XFS_INLINE_EXTS];
-						/* very small file extents */
-		char		if_inline_data[XFS_INLINE_DATA];
-						/* very small file data */
-	} if_u2;
 } xfs_ifork_t;
 
 /*
@@ -158,12 +150,9 @@ void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
 					    xfs_extnum_t, int);
 void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
 			int, int);
-void		xfs_iext_remove_inline(struct xfs_ifork *, xfs_extnum_t, int);
 void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
 void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
 void		xfs_iext_realloc_direct(struct xfs_ifork *, int);
-void		xfs_iext_direct_to_inline(struct xfs_ifork *, xfs_extnum_t);
-void		xfs_iext_inline_to_direct(struct xfs_ifork *, int);
 void		xfs_iext_destroy(struct xfs_ifork *);
 struct xfs_bmbt_rec_host *
 		xfs_iext_bno_to_ext(struct xfs_ifork *, xfs_fileoff_t, int *);
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index b6b954d5cf54..c2f8e40a3e1f 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1709,7 +1709,6 @@ xfs_swap_extent_forks(
 	xfs_filblks_t		aforkblks = 0;
 	xfs_filblks_t		taforkblks = 0;
 	xfs_extnum_t		junk;
-	xfs_extnum_t		nextents;
 	uint64_t		tmp;
 	int			error;
 
@@ -1784,13 +1783,6 @@ xfs_swap_extent_forks(
 
 	switch (ip->i_d.di_format) {
 	case XFS_DINODE_FMT_EXTENTS:
-		/*
-		 * If the extents fit in the inode, fix the pointer.  Otherwise
-		 * it's already NULL or pointing to the extent.
-		 */
-		nextents = xfs_iext_count(&ip->i_df);
-		if (nextents <= XFS_INLINE_EXTS)
-			ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
 		(*src_log_flags) |= XFS_ILOG_DEXT;
 		break;
 	case XFS_DINODE_FMT_BTREE:
@@ -1802,13 +1794,6 @@ xfs_swap_extent_forks(
 
 	switch (tip->i_d.di_format) {
 	case XFS_DINODE_FMT_EXTENTS:
-		/*
-		 * If the extents fit in the inode, fix the pointer.  Otherwise
-		 * it's already NULL or pointing to the extent.
-		 */
-		nextents = xfs_iext_count(&tip->i_df);
-		if (nextents <= XFS_INLINE_EXTS)
-			tifp->if_u1.if_extents = tifp->if_u2.if_inline_ext;
 		(*target_log_flags) |= XFS_ILOG_DEXT;
 		break;
 	case XFS_DINODE_FMT_BTREE:
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 16/18] xfs: use a b+tree for the in-core extent list
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (14 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-11-01 18:47   ` Darrick J. Wong
  2017-11-02  0:14   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 17/18] xfs: remove the nr_extents argument to xfs_iext_insert Christoph Hellwig
                   ` (2 subsequent siblings)
  18 siblings, 2 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

Replace the current linear list and the indirection array for the in-core
extent list with a b+tree to avoid the need for larger memory allocations
for the indirection array when lots of extents are present.  The current
extent list implementations leads to heavy pressure on the memory
allocator when modifying files with a high extent count, and can lead
to high latencies because of that.

The replacement is a b+tree with a few quirks.  The leaf nodes directly
store the extent record in two u64 values.  The encoding is a little bit
different from the existing in-core extent records so that the start
offset and length which are required for lookups can be retreived with
simple mask operations.  The inner nodes store a 64-bit key containing
the start offset in the first half of the node, and the pointers to the
next lower level in the second half.  In either case we walk the node
from the beginninig to the end and do a linear search, as that is more
efficient for the low number of cache lines touched during a search
(2 for the inner nodes, 4 for the leaf nodes) than a binary search.
We store termination markers (zero length for the leaf nodes, an
otherwise impossible high bit for the inner nodes) to terminate the key
list / records instead of storing a count to use the available cache
lines as efficiently as possible.

One quirk of the algorithm is that while we normally split a node half and
half like usual btree implementations we just spill over entries added at
the very end of the list to a new node on its own.  This means we get a
100% fill grade for the common cases of bulk inseration at reading an
inode into memory, and when only sequentially appending to a file.  The
downside is a slightly higher chance of splits on the first random
inserations.

Both insert and removal manually recurse into the lower levels, but
the bulk deletion of the whole tree is still implemented as a recursive
function call, although one limited by the overall depth and with very
little stack usage in every iteration.

For the first few extents we dynamically grow the list from a single
extent to the next powers of two until we have a first full leaf block
and that building the actual tree.

The code started out based on the generic lib/btree.c code from Joern
Engel based on earlier work from Peter Zijlstra, but has since been
rewritten beyond recognition.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/Makefile                |    1 +
 fs/xfs/libxfs/xfs_bmap.c       |   19 +-
 fs/xfs/libxfs/xfs_bmap_btree.c |  103 +---
 fs/xfs/libxfs/xfs_bmap_btree.h |    7 +-
 fs/xfs/libxfs/xfs_format.h     |    4 -
 fs/xfs/libxfs/xfs_iext_tree.c  | 1043 ++++++++++++++++++++++++++++++++++++++++
 fs/xfs/libxfs/xfs_inode_fork.c | 1035 +--------------------------------------
 fs/xfs/libxfs/xfs_inode_fork.h |   84 +---
 fs/xfs/libxfs/xfs_types.h      |    3 +-
 fs/xfs/scrub/bmap.c            |    5 +-
 fs/xfs/xfs_inode.c             |    2 +-
 fs/xfs/xfs_inode_item.c        |    2 -
 fs/xfs/xfs_trace.h             |   51 +-
 13 files changed, 1103 insertions(+), 1256 deletions(-)
 create mode 100644 fs/xfs/libxfs/xfs_iext_tree.c

diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index a2a5d046793d..7ceb41a9786a 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -49,6 +49,7 @@ xfs-y				+= $(addprefix libxfs/, \
 				   xfs_dquot_buf.o \
 				   xfs_ialloc.o \
 				   xfs_ialloc_btree.o \
+				   xfs_iext_tree.o \
 				   xfs_inode_fork.o \
 				   xfs_inode_buf.o \
 				   xfs_log_rlimit.o \
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index e020bd3f8717..46bda00dca79 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -805,6 +805,8 @@ xfs_bmap_local_to_extents_empty(
 	xfs_bmap_forkoff_reset(ip, whichfork);
 	ifp->if_flags &= ~XFS_IFINLINE;
 	ifp->if_flags |= XFS_IFEXTENTS;
+	ifp->if_u1.if_root = NULL;
+	ifp->if_height = 0;
 	XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
 }
 
@@ -846,8 +848,7 @@ xfs_bmap_local_to_extents(
 
 	flags = 0;
 	error = 0;
-	ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) ==
-								XFS_IFINLINE);
+	ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS)) == XFS_IFINLINE);
 	memset(&args, 0, sizeof(args));
 	args.tp = tp;
 	args.mp = ip->i_mount;
@@ -891,6 +892,9 @@ xfs_bmap_local_to_extents(
 	xfs_bmap_local_to_extents_empty(ip, whichfork);
 	flags |= XFS_ILOG_CORE;
 
+	ifp->if_u1.if_root = NULL;
+	ifp->if_height = 0;
+
 	rec.br_startoff = 0;
 	rec.br_startblock = args.fsbno;
 	rec.br_blockcount = 1;
@@ -1177,6 +1181,7 @@ xfs_iread_extents(
 	xfs_extnum_t		nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
 	struct xfs_btree_block	*block = ifp->if_broot;
 	struct xfs_iext_cursor	ext;
+	struct xfs_bmbt_irec	new;
 	xfs_fsblock_t		bno;
 	struct xfs_buf		*bp;
 	xfs_extnum_t		i, j;
@@ -1193,7 +1198,8 @@ xfs_iread_extents(
 
 	ifp->if_bytes = 0;
 	ifp->if_real_bytes = 0;
-	xfs_iext_add(ifp, 0, nextents);
+	ifp->if_u1.if_root = NULL;
+	ifp->if_height = 0;
 
 	/*
 	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
@@ -1258,16 +1264,15 @@ xfs_iread_extents(
 		 * Copy records into the extent records.
 		 */
 		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
-		for (j = 0; j < num_recs; j++, i++, frp++) {
-			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
+		for (j = 0; j < num_recs; j++, frp++, i++) {
 			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
 				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
 						 XFS_ERRLEVEL_LOW, mp);
 				error = -EFSCORRUPTED;
 				goto out_brelse;
 			}
-			trp->l0 = be64_to_cpu(frp->l0);
-			trp->l1 = be64_to_cpu(frp->l1);
+			xfs_bmbt_disk_get_all(frp, &new);
+			xfs_iext_insert(ip, &ext, 1, &new, state);
 			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
 			xfs_iext_next(ifp, &ext);
 		}
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 89260972a0f6..c10aecaaae44 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -71,73 +71,21 @@ xfs_bmdr_to_bmbt(
 	memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
 }
 
-/*
- * Convert a compressed bmap extent record to an uncompressed form.
- * This code must be in sync with the routines xfs_bmbt_get_startoff,
- * xfs_bmbt_get_startblock and xfs_bmbt_get_blockcount.
- */
-STATIC void
-__xfs_bmbt_get_all(
-		uint64_t l0,
-		uint64_t l1,
-		xfs_bmbt_irec_t *s)
-{
-	int	ext_flag;
-	xfs_exntst_t st;
-
-	ext_flag = (int)(l0 >> (64 - BMBT_EXNTFLAG_BITLEN));
-	s->br_startoff = ((xfs_fileoff_t)l0 &
-			   xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
-	s->br_startblock = (((xfs_fsblock_t)l0 & xfs_mask64lo(9)) << 43) |
-			   (((xfs_fsblock_t)l1) >> 21);
-	s->br_blockcount = (xfs_filblks_t)(l1 & xfs_mask64lo(21));
-	/* This is xfs_extent_state() in-line */
-	if (ext_flag) {
-		ASSERT(s->br_blockcount != 0);	/* saved for DMIG */
-		st = XFS_EXT_UNWRITTEN;
-	} else
-		st = XFS_EXT_NORM;
-	s->br_state = st;
-}
-
 void
-xfs_bmbt_get_all(
-	xfs_bmbt_rec_host_t *r,
-	xfs_bmbt_irec_t *s)
-{
-	__xfs_bmbt_get_all(r->l0, r->l1, s);
-}
-
-/*
- * Extract the blockcount field from an in memory bmap extent record.
- */
-xfs_filblks_t
-xfs_bmbt_get_blockcount(
-	xfs_bmbt_rec_host_t	*r)
-{
-	return (xfs_filblks_t)(r->l1 & xfs_mask64lo(21));
-}
-
-/*
- * Extract the startblock field from an in memory bmap extent record.
- */
-xfs_fsblock_t
-xfs_bmbt_get_startblock(
-	xfs_bmbt_rec_host_t	*r)
-{
-	return (((xfs_fsblock_t)r->l0 & xfs_mask64lo(9)) << 43) |
-	       (((xfs_fsblock_t)r->l1) >> 21);
-}
-
-/*
- * Extract the startoff field from an in memory bmap extent record.
- */
-xfs_fileoff_t
-xfs_bmbt_get_startoff(
-	xfs_bmbt_rec_host_t	*r)
-{
-	return ((xfs_fileoff_t)r->l0 &
-		 xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
+xfs_bmbt_disk_get_all(
+	struct xfs_bmbt_rec	*rec,
+	struct xfs_bmbt_irec	*irec)
+{
+	uint64_t		l0 = get_unaligned_be64(&rec->l0);
+	uint64_t		l1 = get_unaligned_be64(&rec->l1);
+
+	irec->br_startoff = (l0 & xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
+	irec->br_startblock = ((l0 & xfs_mask64lo(9)) << 43) | (l1 >> 21);
+	irec->br_blockcount = l1 & xfs_mask64lo(21);
+	if (l0 >> (64 - BMBT_EXNTFLAG_BITLEN))
+		irec->br_state = XFS_EXT_UNWRITTEN;
+	else
+		irec->br_state = XFS_EXT_NORM;
 }
 
 /*
@@ -161,29 +109,6 @@ xfs_bmbt_disk_get_startoff(
 		 xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
 }
 
-/*
- * Set all the fields in a bmap extent record from the uncompressed form.
- */
-void
-xfs_bmbt_set_all(
-	struct xfs_bmbt_rec_host *r,
-	struct xfs_bmbt_irec	*s)
-{
-	int			extent_flag = (s->br_state != XFS_EXT_NORM);
-
-	ASSERT(s->br_state == XFS_EXT_NORM || s->br_state == XFS_EXT_UNWRITTEN);
-	ASSERT(!(s->br_startoff & xfs_mask64hi(64-BMBT_STARTOFF_BITLEN)));
-	ASSERT(!(s->br_blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)));
-	ASSERT(!(s->br_startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)));
-
-	r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
-		 ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
-		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43);
-	r->l1 = ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
-		 ((xfs_bmbt_rec_base_t)s->br_blockcount &
-		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21));
-}
-
 /*
  * Set all the fields in a bmap extent record from the uncompressed form.
  */
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
index 2fbfe2a24b15..714bfbaf9b2d 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.h
+++ b/fs/xfs/libxfs/xfs_bmap_btree.h
@@ -98,16 +98,11 @@ struct xfs_trans;
  */
 extern void xfs_bmdr_to_bmbt(struct xfs_inode *, xfs_bmdr_block_t *, int,
 			struct xfs_btree_block *, int);
-extern void xfs_bmbt_get_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
-extern xfs_filblks_t xfs_bmbt_get_blockcount(xfs_bmbt_rec_host_t *r);
-extern xfs_fsblock_t xfs_bmbt_get_startblock(xfs_bmbt_rec_host_t *r);
-extern xfs_fileoff_t xfs_bmbt_get_startoff(xfs_bmbt_rec_host_t *r);
 
 void xfs_bmbt_disk_set_all(struct xfs_bmbt_rec *r, struct xfs_bmbt_irec *s);
 extern xfs_filblks_t xfs_bmbt_disk_get_blockcount(xfs_bmbt_rec_t *r);
 extern xfs_fileoff_t xfs_bmbt_disk_get_startoff(xfs_bmbt_rec_t *r);
-
-extern void xfs_bmbt_set_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
+extern void xfs_bmbt_disk_get_all(xfs_bmbt_rec_t *r, xfs_bmbt_irec_t *s);
 
 extern void xfs_bmbt_to_bmdr(struct xfs_mount *, struct xfs_btree_block *, int,
 			xfs_bmdr_block_t *, int);
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 6470dfa768ee..28d5391d2272 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -1553,10 +1553,6 @@ typedef struct xfs_bmbt_rec {
 typedef uint64_t	xfs_bmbt_rec_base_t;	/* use this for casts */
 typedef xfs_bmbt_rec_t xfs_bmdr_rec_t;
 
-typedef struct xfs_bmbt_rec_host {
-	uint64_t		l0, l1;
-} xfs_bmbt_rec_host_t;
-
 /*
  * Values and macros for delayed-allocation startblock fields.
  */
diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
new file mode 100644
index 000000000000..acb66c0677e7
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_iext_tree.c
@@ -0,0 +1,1043 @@
+/*
+ * Copyright (c) 2017 Christoph Hellwig.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/cache.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include "xfs.h"
+#include "xfs_format.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_inode.h"
+#include "xfs_inode_fork.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_trace.h"
+
+/*
+ * In-core extent record layout:
+ *
+ * +-------+----------------------------+
+ * | 00:51 | all 52 bits of startoff    |
+ * | 52:63 | low 12 bits of startblock  |
+ * +-------+----------------------------+
+ * | 00:20 | all 21 bits of length      |
+ * |    21 | unwritten extent bit       |
+ * | 22:63 | high 42 bits of startblock |
+ * +-------+----------------------------+
+ */
+#define XFS_IEXT_STARTOFF_MASK		xfs_mask64lo(52)
+#define XFS_IEXT_LENGTH_MASK		xfs_mask64lo(21)
+#define XFS_IEXT_STARTBLOCK_MASK	xfs_mask64lo(54)
+
+struct xfs_iext_rec {
+	uint64_t			lo;
+	uint64_t			hi;
+};
+
+/*
+ * Given that the length can't be a zero, only an empty hi value indicates an
+ * unused record.
+ */
+static bool xfs_iext_rec_is_empty(struct xfs_iext_rec *rec)
+{
+	return rec->hi == 0;
+}
+
+static inline void xfs_iext_rec_clear(struct xfs_iext_rec *rec)
+{
+	rec->lo = 0;
+	rec->hi = 0;
+}
+
+static void
+xfs_iext_set(
+	struct xfs_iext_rec	*rec,
+	struct xfs_bmbt_irec	*irec)
+{
+	ASSERT((irec->br_startoff & ~XFS_IEXT_STARTOFF_MASK) == 0);
+	ASSERT((irec->br_blockcount & ~XFS_IEXT_LENGTH_MASK) == 0);
+	ASSERT((irec->br_startblock & ~XFS_IEXT_STARTBLOCK_MASK) == 0);
+
+	rec->lo = irec->br_startoff & XFS_IEXT_STARTOFF_MASK;
+	rec->hi = irec->br_blockcount & XFS_IEXT_LENGTH_MASK;
+
+	rec->lo |= (irec->br_startblock << 52);
+	rec->hi |= ((irec->br_startblock & ~xfs_mask64lo(12)) << (22 - 12));
+
+	if (irec->br_state == XFS_EXT_UNWRITTEN)
+		rec->hi |= (1 << 21);
+}
+
+static void
+xfs_iext_get(
+	struct xfs_bmbt_irec	*irec,
+	struct xfs_iext_rec	*rec)
+{
+	irec->br_startoff = rec->lo & XFS_IEXT_STARTOFF_MASK;
+	irec->br_blockcount = rec->hi & XFS_IEXT_LENGTH_MASK;
+
+	irec->br_startblock = rec->lo >> 52;
+	irec->br_startblock |= (rec->hi & xfs_mask64hi(42)) >> (22 - 12);
+
+	if (rec->hi & (1 << 21))
+		irec->br_state = XFS_EXT_UNWRITTEN;
+	else
+		irec->br_state = XFS_EXT_NORM;
+}
+
+enum {
+	NODE_SIZE	= L1_CACHE_BYTES * 4,
+	KEYS_PER_NODE	= NODE_SIZE / (sizeof(uint64_t) + sizeof(void *)),
+	RECS_PER_LEAF	= (NODE_SIZE - sizeof(uint64_t) - sizeof(uint64_t)) /
+				sizeof(struct xfs_iext_rec),
+};
+
+/*
+ * In-core extent btree block layout:
+ *
+ * There are two types of blocks in the btree: leaf and inner (non-leaf) blocks.
+ *
+ * The leaf blocks are made up by %KEYS_PER_NODE extent records, which each
+ * contain the startoffset, blockcount, startblock and unwritten extent flag.
+ * See above for the exact format, followed by pointers to the previous and next
+ * leaf blocks (if there are any).
+ *
+ * The inner (non-leaf) blocks first contain KEYS_PER_NODE lookup keys, followed
+ * by an equal number of pointers to the btree blocks at the next lower level.
+ *
+ * Note that we currently always allocate 64-bits worth for pointers in the
+ * inner nodes or the link pointers in the leaf nodes even on 32-bit
+ * architectures, so that we can use consistent addressing using and array of
+ * 64-bit unsigned integers.  If someone still cares for 32-bit architectures
+ * this could be optimized a bit better for them.
+ *
+ *		+-------+-------+-------+-------+-------+----------+----------+
+ * Leaf:	| rec 1 | rec 2 | rec 3 | rec 4 | rec N | prev-ptr | next-ptr |
+ *		+-------+-------+-------+-------+-------+----------+----------+
+ *
+ *		+-------+-------+-------+-------+-------+-------+------+-------+
+ * Inner:	| key 1 | key 2 | key 3 | key N | ptr 1 | ptr 2 | ptr3 | ptr N |
+ *		+-------+-------+-------+-------+-------+-------+------+-------+
+ */
+struct xfs_iext_node {
+	uint64_t		keys[KEYS_PER_NODE];
+#define XFS_IEXT_KEY_INVALID	(1ULL << 63)
+	void			*ptrs[KEYS_PER_NODE];
+};
+
+struct xfs_iext_leaf {
+	struct xfs_iext_rec	recs[RECS_PER_LEAF];
+	struct xfs_iext_leaf	*prev;
+	struct xfs_iext_leaf	*next;
+};
+
+inline xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp)
+{
+	return ifp->if_bytes / sizeof(struct xfs_iext_rec);
+}
+
+static inline int xfs_iext_max_recs(struct xfs_ifork *ifp)
+{
+	if (ifp->if_height == 1)
+		return xfs_iext_count(ifp);
+	return RECS_PER_LEAF;
+}
+
+static inline struct xfs_iext_rec *cur_rec(struct xfs_iext_cursor *cur)
+{
+	return &cur->leaf->recs[cur->pos];
+}
+
+static noinline bool xfs_iext_valid(struct xfs_ifork *ifp,
+		struct xfs_iext_cursor *cur)
+{
+	if (cur->pos < 0)
+		return false;
+	if (cur->pos >= xfs_iext_max_recs(ifp))
+		return false;
+	if (!cur->leaf)
+		return false;
+	if (xfs_iext_rec_is_empty(cur_rec(cur)))
+		return false;
+	return true;
+}
+
+static void *
+xfs_iext_find_first_leaf(
+	struct xfs_ifork	*ifp)
+{
+	struct xfs_iext_node	*node = ifp->if_u1.if_root;
+	int			height;
+
+	if (!ifp->if_height)
+		return NULL;
+
+	for (height = ifp->if_height; height > 1; height--) {
+		node = node->ptrs[0];
+		ASSERT(node);
+	}
+
+	return node;
+}
+
+static void *
+xfs_iext_find_last_leaf(
+	struct xfs_ifork	*ifp)
+{
+	struct xfs_iext_node	*node = ifp->if_u1.if_root;
+	int			height, i;
+
+	if (!ifp->if_height)
+		return NULL;
+
+	for (height = ifp->if_height; height > 1; height--) {
+		for (i = 1; i < KEYS_PER_NODE; i++)
+			if (!node->ptrs[i])
+				break;
+		node = node->ptrs[i - 1];
+		ASSERT(node);
+	}
+
+	return node;
+}
+
+void
+xfs_iext_first(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	cur->pos = 0;
+	cur->leaf = xfs_iext_find_first_leaf(ifp);
+}
+
+void
+xfs_iext_last(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	int			i;
+
+	cur->leaf = xfs_iext_find_last_leaf(ifp);
+	if (!cur->leaf) {
+		cur->pos = 0;
+		return;
+	}
+
+	for (i = 1; i < xfs_iext_max_recs(ifp); i++) {
+		if (xfs_iext_rec_is_empty(&cur->leaf->recs[i]))
+			break;
+	}
+	cur->pos = i - 1;
+}
+
+void
+xfs_iext_next(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	if (!cur->leaf) {
+		ASSERT(cur->pos <= 0 || cur->pos >= RECS_PER_LEAF);
+		xfs_iext_first(ifp, cur);
+		return;
+	}
+
+	ASSERT(cur->pos >= 0);
+	ASSERT(cur->pos < xfs_iext_max_recs(ifp));
+
+	cur->pos++;
+	if (!xfs_iext_valid(ifp, cur) && ifp->if_height > 1 &&
+	    cur->leaf && cur->leaf->next) {
+		cur->leaf = cur->leaf->next;
+		cur->pos = 0;
+	}
+}
+
+void
+xfs_iext_prev(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	if (!cur->leaf) {
+		ASSERT(cur->pos <= 0 || cur->pos >= RECS_PER_LEAF);
+		xfs_iext_last(ifp, cur);
+		return;
+	}
+
+	ASSERT(cur->pos >= 0);
+	ASSERT(cur->pos <= RECS_PER_LEAF);
+
+recurse:
+	do {
+		cur->pos--;
+		if (xfs_iext_valid(ifp, cur))
+			return;
+	} while (cur->pos > 0);
+
+	if (ifp->if_height > 1 && cur->leaf->prev) {
+		cur->leaf = cur->leaf->prev;
+		cur->pos = RECS_PER_LEAF;
+		goto recurse;
+	}
+}
+
+static inline int
+xfs_iext_key_cmp(
+	struct xfs_iext_node	*node,
+	int			n,
+	xfs_fileoff_t		offset)
+{
+	if (node->keys[n] > offset)
+		return 1;
+	if (node->keys[n] < offset)
+		return -1;
+	return 0;
+}
+
+static inline int
+xfs_iext_rec_cmp(
+	struct xfs_iext_rec	*rec,
+	xfs_fileoff_t		offset)
+{
+	uint64_t		rec_offset = rec->lo & XFS_IEXT_STARTOFF_MASK;
+	u32			rec_len = rec->hi & XFS_IEXT_LENGTH_MASK;
+
+	if (rec_offset > offset)
+		return 1;
+	if (rec_offset + rec_len <= offset)
+		return -1;
+	return 0;
+}
+
+static void *
+xfs_iext_find_level(
+	struct xfs_ifork	*ifp,
+	xfs_fileoff_t		offset,
+	int			level)
+{
+	struct xfs_iext_node	*node = ifp->if_u1.if_root;
+	int			height, i;
+
+	if (!ifp->if_height)
+		return NULL;
+
+	for (height = ifp->if_height; height > level; height--) {
+		for (i = 1; i < KEYS_PER_NODE; i++)
+			if (xfs_iext_key_cmp(node, i, offset) > 0)
+				break;
+
+		node = node->ptrs[i - 1];
+		if (!node)
+			break;
+	}
+
+	return node;
+}
+
+static int
+xfs_iext_node_pos(
+	struct xfs_iext_node	*node,
+	xfs_fileoff_t		offset)
+{
+	int			i;
+
+	for (i = 1; i < KEYS_PER_NODE; i++) {
+		if (xfs_iext_key_cmp(node, i, offset) > 0)
+			break;
+	}
+
+	return i - 1;
+}
+
+static int
+xfs_iext_node_insert_pos(
+	struct xfs_iext_node	*node,
+	xfs_fileoff_t		offset)
+{
+	int			i;
+
+	for (i = 0; i < KEYS_PER_NODE; i++) {
+		if (xfs_iext_key_cmp(node, i, offset) > 0)
+			return i;
+	}
+
+	return KEYS_PER_NODE;
+}
+
+static int
+xfs_iext_node_nr_entries(
+	struct xfs_iext_node	*node,
+	int			start)
+{
+	int			i;
+
+	for (i = start; i < KEYS_PER_NODE; i++) {
+		if (node->keys[i] == XFS_IEXT_KEY_INVALID)
+			break;
+	}
+
+	return i;
+}
+
+static int
+xfs_iext_leaf_nr_entries(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_leaf	*leaf,
+	int			start)
+{
+	int			i;
+
+	for (i = start; i < xfs_iext_max_recs(ifp); i++) {
+		if (xfs_iext_rec_is_empty(&leaf->recs[i]))
+			break;
+	}
+
+	return i;
+}
+
+static inline uint64_t
+xfs_iext_leaf_key(
+	struct xfs_iext_leaf	*leaf,
+	int			n)
+{
+	return leaf->recs[n].lo & XFS_IEXT_STARTOFF_MASK;
+}
+
+static void
+xfs_iext_grow(
+	struct xfs_ifork	*ifp)
+{
+	struct xfs_iext_node	*node = kmem_zalloc(NODE_SIZE, KM_NOFS);
+	int			i;
+
+	if (ifp->if_height == 1) {
+		struct xfs_iext_leaf *prev = ifp->if_u1.if_root;
+
+		node->keys[0] = xfs_iext_leaf_key(prev, 0);
+		node->ptrs[0] = prev;
+	} else  {
+		struct xfs_iext_node *prev = ifp->if_u1.if_root;
+
+		ASSERT(ifp->if_height > 1);
+
+		node->keys[0] = prev->keys[0];
+		node->ptrs[0] = prev;
+	}
+
+	for (i = 1; i < KEYS_PER_NODE; i++)
+		node->keys[i] = XFS_IEXT_KEY_INVALID;
+
+	ifp->if_u1.if_root = node;
+	ifp->if_height++;
+}
+
+static void
+xfs_iext_update_node(
+	struct xfs_ifork	*ifp,
+	xfs_fileoff_t		old_offset,
+	xfs_fileoff_t		new_offset,
+	int			level,
+	void			*ptr)
+{
+	struct xfs_iext_node	*node = ifp->if_u1.if_root;
+	int			height, i;
+
+	for (height = ifp->if_height; height > level; height--) {
+		for (i = 0; i < KEYS_PER_NODE; i++) {
+			if (i > 0 && xfs_iext_key_cmp(node, i, old_offset) > 0)
+				break;
+			if (node->keys[i] == old_offset)
+				node->keys[i] = new_offset;
+		}
+		node = node->ptrs[i - 1];
+		ASSERT(node);
+	}
+
+	ASSERT(node == ptr);
+}
+
+static struct xfs_iext_node *
+xfs_iext_split_node(
+	struct xfs_iext_node	**nodep,
+	int			*pos,
+	int			*nr_entries)
+{
+	struct xfs_iext_node	*node = *nodep;
+	struct xfs_iext_node	*new = kmem_zalloc(NODE_SIZE, KM_NOFS);
+	const int		nr_move = KEYS_PER_NODE / 2;
+	int			nr_keep = nr_move + (KEYS_PER_NODE & 1);
+	int			i = 0;
+
+	/* for sequential append operations just spill over into the new node */
+	if (*pos == KEYS_PER_NODE) {
+		*nodep = new;
+		*pos = 0;
+		*nr_entries = 0;
+		goto done;
+	}
+
+
+	for (i = 0; i < nr_move; i++) {
+		new->keys[i] = node->keys[nr_keep + i];
+		new->ptrs[i] = node->ptrs[nr_keep + i];
+
+		node->keys[nr_keep + i] = XFS_IEXT_KEY_INVALID;
+		node->ptrs[nr_keep + i] = NULL;
+	}
+
+	if (*pos >= nr_keep) {
+		*nodep = new;
+		*pos -= nr_keep;
+		*nr_entries = nr_move;
+	} else {
+		*nr_entries = nr_keep;
+	}
+done:
+	for (; i < KEYS_PER_NODE; i++)
+		new->keys[i] = XFS_IEXT_KEY_INVALID;
+	return new;
+}
+
+static void
+xfs_iext_insert_node(
+	struct xfs_ifork	*ifp,
+	uint64_t		offset,
+	void			*ptr,
+	int			level)
+{
+	struct xfs_iext_node	*node, *new;
+	int			i, pos, nr_entries;
+
+again:
+	if (ifp->if_height < level)
+		xfs_iext_grow(ifp);
+
+	new = NULL;
+	node = xfs_iext_find_level(ifp, offset, level);
+	pos = xfs_iext_node_insert_pos(node, offset);
+	nr_entries = xfs_iext_node_nr_entries(node, pos);
+
+	ASSERT(pos >= nr_entries || xfs_iext_key_cmp(node, pos, offset) != 0);
+	ASSERT(nr_entries <= KEYS_PER_NODE);
+
+	if (nr_entries == KEYS_PER_NODE)
+		new = xfs_iext_split_node(&node, &pos, &nr_entries);
+
+	if (node != new && pos == 0 && nr_entries > 0)
+		xfs_iext_update_node(ifp, node->keys[0], offset, level, node);
+
+	for (i = nr_entries; i > pos; i--) {
+		node->keys[i] = node->keys[i - 1];
+		node->ptrs[i] = node->ptrs[i - 1];
+	}
+	node->keys[pos] = offset;
+	node->ptrs[pos] = ptr;
+
+	if (new) {
+		offset = new->keys[0];
+		ptr = new;
+		level++;
+		goto again;
+	}
+}
+
+static struct xfs_iext_leaf *
+xfs_iext_split_leaf(
+	struct xfs_iext_cursor	*cur,
+	int			*nr_entries)
+{
+	struct xfs_iext_leaf	*leaf = cur->leaf;
+	struct xfs_iext_leaf	*new = kmem_zalloc(NODE_SIZE, KM_NOFS);
+	const int		nr_move = RECS_PER_LEAF / 2;
+	int			nr_keep = nr_move + (RECS_PER_LEAF & 1);
+	int			i;
+
+	/* for sequential append operations just spill over into the new node */
+	if (cur->pos == KEYS_PER_NODE) {
+		cur->leaf = new;
+		cur->pos = 0;
+		*nr_entries = 0;
+		goto done;
+	}
+
+	if (nr_keep & 1)
+		nr_keep++;
+
+	for (i = 0; i < nr_move; i++) {
+		new->recs[i] = leaf->recs[nr_keep + i];
+		xfs_iext_rec_clear(&leaf->recs[nr_keep + i]);
+	}
+
+	if (cur->pos >= nr_keep) {
+		cur->leaf = new;
+		cur->pos -= nr_keep;
+		*nr_entries = nr_move;
+	} else {
+		*nr_entries = nr_keep;
+	}
+done:
+	if (leaf->next)
+		leaf->next->prev = new;
+	new->next = leaf->next;
+	new->prev = leaf;
+	leaf->next = new;
+	return new;
+}
+
+static void
+xfs_iext_alloc_root(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	ASSERT(ifp->if_bytes == 0);
+
+	ifp->if_u1.if_root = kmem_zalloc(sizeof(struct xfs_iext_rec), KM_NOFS);
+	ifp->if_height = 1;
+
+	/* now that we have a node step into it */
+	cur->leaf = ifp->if_u1.if_root;
+	cur->pos = 0;
+}
+
+static void
+xfs_iext_realloc_root(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	size_t new_size = ifp->if_bytes + sizeof(struct xfs_iext_rec);
+	void *new;
+
+	/* account for the prev/next pointers */
+	if (new_size / sizeof(struct xfs_iext_rec) == RECS_PER_LEAF)
+		new_size = NODE_SIZE;
+
+	new = kmem_realloc(ifp->if_u1.if_root, new_size, KM_NOFS);
+	memset(new + ifp->if_bytes, 0, new_size - ifp->if_bytes);
+	ifp->if_u1.if_root = new;
+	cur->leaf = new;
+}
+
+static void
+__xfs_iext_insert(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur,
+	struct xfs_bmbt_irec	*irec)
+{
+	xfs_fileoff_t		offset = irec->br_startoff;
+	struct xfs_iext_leaf	*new = NULL;
+	int			nr_entries, i;
+
+	if (ifp->if_height == 0)
+		xfs_iext_alloc_root(ifp, cur);
+	else if (ifp->if_height == 1)
+		xfs_iext_realloc_root(ifp, cur);
+
+	nr_entries = xfs_iext_leaf_nr_entries(ifp, cur->leaf, cur->pos);
+	ASSERT(nr_entries <= RECS_PER_LEAF);
+	ASSERT(cur->pos >= nr_entries ||
+	       xfs_iext_rec_cmp(cur_rec(cur), irec->br_startoff) != 0);
+
+	if (nr_entries == RECS_PER_LEAF)
+		new = xfs_iext_split_leaf(cur, &nr_entries);
+
+	if (cur->leaf != new && cur->pos == 0 && nr_entries > 0) {
+		xfs_iext_update_node(ifp, xfs_iext_leaf_key(cur->leaf, 0), offset, 1,
+				cur->leaf);
+	}
+
+	for (i = nr_entries; i > cur->pos; i--)
+		cur->leaf->recs[i] = cur->leaf->recs[i - 1];
+	xfs_iext_set(cur_rec(cur), irec);
+	ifp->if_bytes += sizeof(struct xfs_iext_rec);
+
+	if (new)
+		xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
+}
+
+void
+xfs_iext_insert(
+	struct xfs_inode	*ip,
+	struct xfs_iext_cursor	*cur,
+	xfs_extnum_t		nr_extents,
+	struct xfs_bmbt_irec	*new,
+	int			state)
+{
+	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
+	int			i;
+
+	ASSERT(nr_extents > 0);
+
+	for (i = nr_extents - 1; i >= 0; i--) {
+		__xfs_iext_insert(ifp, cur, new + i);
+		trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
+	}
+}
+
+static struct xfs_iext_node *
+xfs_iext_rebalance_node(
+	struct xfs_iext_node	*parent,
+	int			*pos,
+	struct xfs_iext_node	*node,
+	int			nr_entries)
+{
+	if (nr_entries == 0)
+		return node;
+
+	if (*pos > 0) {
+		struct xfs_iext_node *prev = parent->ptrs[*pos - 1];
+		int nr_prev = xfs_iext_node_nr_entries(prev, 0), i;
+
+		if (nr_prev + nr_entries <= KEYS_PER_NODE) {
+			for (i = 0; i < nr_entries; i++) {
+				prev->keys[nr_prev + i] = node->keys[i];
+				prev->ptrs[nr_prev + i] = node->ptrs[i];
+			}
+			return node;
+		}
+	}
+
+	if (*pos + 1 < xfs_iext_node_nr_entries(parent, *pos)) {
+		struct xfs_iext_node *next = parent->ptrs[*pos + 1];
+		int nr_next = xfs_iext_node_nr_entries(next, 0), i;
+
+		if (nr_entries + nr_next <= KEYS_PER_NODE) {
+			for (i = 0; i < nr_next; i++) {
+				node->keys[nr_entries + i] = next->keys[i];
+				node->ptrs[nr_entries + i] = next->ptrs[i];
+			}
+
+			++*pos;
+			return next;
+		}
+	}
+
+	return NULL;
+}
+
+static void
+xfs_iext_remove_node(
+	struct xfs_ifork	*ifp,
+	xfs_fileoff_t		offset,
+	void			*victim)
+{
+	struct xfs_iext_node	*node, *parent;
+	int			level = 2, pos, nr_entries, i;
+
+	ASSERT(level <= ifp->if_height);
+	node = xfs_iext_find_level(ifp, offset, level);
+	pos = xfs_iext_node_pos(node, offset);
+again:
+	ASSERT(node->ptrs[pos]);
+	ASSERT(node->ptrs[pos] == victim);
+	kmem_free(victim);
+
+	nr_entries = xfs_iext_node_nr_entries(node, pos) - 1;
+	offset = node->keys[0];
+	for (i = pos; i < nr_entries; i++) {
+		node->keys[i] = node->keys[i + 1];
+		node->ptrs[i] = node->ptrs[i + 1];
+	}
+	node->keys[nr_entries] = XFS_IEXT_KEY_INVALID;
+	node->ptrs[nr_entries] = NULL;
+
+	if (pos == 0 && nr_entries > 0) {
+		xfs_iext_update_node(ifp, offset, node->keys[0], level,
+				node);
+		offset = node->keys[0];
+	}
+
+	if (nr_entries >= KEYS_PER_NODE / 2)
+		return;
+
+	if (level < ifp->if_height) {
+		level++;
+		parent = xfs_iext_find_level(ifp, offset, level);
+		pos = xfs_iext_node_pos(parent, offset);
+
+		ASSERT(pos != KEYS_PER_NODE);
+		ASSERT(parent->ptrs[pos] == node);
+
+		node = xfs_iext_rebalance_node(parent, &pos, node, nr_entries);
+		if (node) {
+			offset = node->keys[0];
+			victim = node;
+			node = parent;
+			goto again;
+		}
+	} else if (nr_entries == 1) {
+		ASSERT(node == ifp->if_u1.if_root);
+		ifp->if_u1.if_root = node->ptrs[0];
+		ifp->if_height--;
+		kmem_free(node);
+	}
+}
+
+static void
+xfs_iext_rebalance_leaf(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur,
+	struct xfs_iext_leaf	*leaf,
+	xfs_fileoff_t		offset,
+	int			fill)
+{
+	if (leaf->prev) {
+		int nr_prev = xfs_iext_leaf_nr_entries(ifp, leaf->prev, 0), i;
+
+		if (nr_prev + fill <= RECS_PER_LEAF) {
+			for (i = 0; i < fill; i++)
+				leaf->prev->recs[nr_prev + i] = leaf->recs[i];
+
+			if (cur->leaf == leaf) {
+				cur->leaf = leaf->prev;
+				cur->pos += nr_prev;
+			}
+			goto remove_node;
+		}
+	}
+
+	if (leaf->next) {
+		int nr_next = xfs_iext_leaf_nr_entries(ifp, leaf->next, 0), i;
+
+		if (fill + nr_next <= RECS_PER_LEAF) {
+			for (i = 0; i < nr_next; i++)
+				leaf->recs[fill + i] = leaf->next->recs[i];
+
+			if (cur->leaf == leaf->next) {
+				cur->leaf = leaf;
+				cur->pos += fill;
+			}
+
+			offset = xfs_iext_leaf_key(leaf->next, 0);
+			leaf = leaf->next;
+			goto remove_node;
+		}
+	}
+
+	return;
+remove_node:
+	if (leaf->prev)
+		leaf->prev->next = leaf->next;
+	if (leaf->next)
+		leaf->next->prev = leaf->prev;
+	xfs_iext_remove_node(ifp, offset, leaf);
+}
+
+static void
+xfs_iext_free_last_leaf(
+	struct xfs_ifork	*ifp)
+{
+	ifp->if_u1.if_root = NULL;
+	ifp->if_height--;
+	kmem_free(ifp->if_u1.if_root);
+}
+
+static void
+__xfs_iext_remove(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur)
+{
+	struct xfs_iext_leaf	*leaf = cur->leaf;
+	xfs_fileoff_t		offset = xfs_iext_leaf_key(leaf, 0);
+	int			i, nr_entries;
+
+	ASSERT(ifp->if_height > 0);
+	ASSERT(ifp->if_u1.if_root != NULL);
+	ASSERT(xfs_iext_valid(ifp, cur));
+
+	nr_entries = xfs_iext_leaf_nr_entries(ifp, leaf, cur->pos) - 1;
+	for (i = cur->pos; i < nr_entries; i++)
+		leaf->recs[i] = leaf->recs[i + 1];
+	xfs_iext_rec_clear(&leaf->recs[nr_entries]);
+	ifp->if_bytes -= sizeof(struct xfs_iext_rec);
+
+	if (cur->pos == 0 && nr_entries > 0) {
+		xfs_iext_update_node(ifp, offset, xfs_iext_leaf_key(leaf, 0), 1,
+				leaf);
+		offset = xfs_iext_leaf_key(leaf, 0);
+	} else if (cur->pos == nr_entries) {
+		if (ifp->if_height > 1 && leaf->next)
+			cur->leaf = leaf->next;
+		else
+			cur->leaf = NULL;
+		cur->pos = 0;
+	}
+
+	if (nr_entries >= RECS_PER_LEAF / 2)
+		return;
+
+	if (ifp->if_height > 1)
+		xfs_iext_rebalance_leaf(ifp, cur, leaf, offset, nr_entries);
+	else if (nr_entries == 0)
+		xfs_iext_free_last_leaf(ifp);
+}
+
+void
+xfs_iext_remove(
+	struct xfs_inode	*ip,
+	struct xfs_iext_cursor	*cur,
+	int			nr_extents,
+	int			state)
+{
+	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
+	int			i;
+
+	ASSERT(nr_extents > 0);
+
+	for (i = 0; i < nr_extents; i++) {
+		trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
+		__xfs_iext_remove(ifp, cur);
+	}
+}
+
+/*
+ * Lookup the extent covering bno.
+ *
+ * If there is an extent covering bno return the extent index, and store the
+ * expanded extent structure in *gotp, and the extent cursor in *cur.
+ * If there is no extent covering bno, but there is an extent after it (e.g.
+ * it lies in a hole) return that extent in *gotp and its cursor in *cur
+ * instead.
+ * If bno is beyond the last extent return false, and return an invalid
+ * cursor value.
+ */
+bool
+xfs_iext_lookup_extent(
+	struct xfs_inode	*ip,
+	struct xfs_ifork	*ifp,
+	xfs_fileoff_t		offset,
+	struct xfs_iext_cursor	*cur,
+	struct xfs_bmbt_irec	*gotp)
+{
+	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
+
+	cur->leaf = xfs_iext_find_level(ifp, offset, 1);
+	if (!cur->leaf) {
+		cur->pos = 0;
+		return false;
+	}
+
+	for (cur->pos = 0; cur->pos < xfs_iext_max_recs(ifp); cur->pos++) {
+		struct xfs_iext_rec *rec = cur_rec(cur);
+
+		if (xfs_iext_rec_is_empty(rec))
+			break;
+		if (xfs_iext_rec_cmp(rec, offset) >= 0)
+			goto found;
+	}
+
+	/* Try looking in the next node for an entry > offset */
+	if (ifp->if_height == 1 || !cur->leaf->next)
+		return false;
+	cur->leaf = cur->leaf->next;
+	cur->pos = 0;
+	if (!xfs_iext_valid(ifp, cur))
+		return false;
+found:
+	xfs_iext_get(gotp, cur_rec(cur));
+	return true;
+}
+
+/*
+ * Returns the last extent before end, and if this extent doesn't cover
+ * end, update end to the end of the extent.
+ */
+bool
+xfs_iext_lookup_extent_before(
+	struct xfs_inode	*ip,
+	struct xfs_ifork	*ifp,
+	xfs_fileoff_t		*end,
+	struct xfs_iext_cursor	*cur,
+	struct xfs_bmbt_irec	*gotp)
+{
+	/* could be optimized to not even look up the next on a match.. */
+	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
+	    gotp->br_startoff <= *end - 1)
+		return true;
+	if (!xfs_iext_prev_extent(ifp, cur, gotp))
+		return false;
+	*end = gotp->br_startoff + gotp->br_blockcount;
+	return true;
+}
+
+void
+xfs_iext_update_extent(
+	struct xfs_inode	*ip,
+	int			state,
+	struct xfs_iext_cursor	*cur,
+	struct xfs_bmbt_irec	*new)
+{
+	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
+
+	if (cur->pos == 0) {
+		struct xfs_bmbt_irec	old;
+
+		xfs_iext_get(&old, cur_rec(cur));
+		if (new->br_startoff != old.br_startoff) {
+			xfs_iext_update_node(ifp, old.br_startoff,
+					new->br_startoff, 1, cur->leaf);
+		}
+	}
+
+	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
+	xfs_iext_set(cur_rec(cur), new);
+	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
+}
+
+/*
+ * Return true if there is an extent at cursor cur and return the expanded
+ * extent structure at cur in gotp in that case.  Else return false.
+ */
+bool
+xfs_iext_get_extent(
+	struct xfs_ifork	*ifp,
+	struct xfs_iext_cursor	*cur,
+	struct xfs_bmbt_irec	*gotp)
+{
+	if (!xfs_iext_valid(ifp, cur))
+		return false;
+	xfs_iext_get(gotp, cur_rec(cur));
+	return true;
+}
+
+/*
+ * This is a recursive function, because of that we need to be extremely
+ * careful with stack usage.
+ */
+static void
+xfs_iext_destroy_node(
+	struct xfs_iext_node	*node,
+	int			level)
+{
+	int			i;
+
+	if (level > 1) {
+		for (i = 0; i < KEYS_PER_NODE; i++) {
+			if (node->keys[i] == XFS_IEXT_KEY_INVALID)
+				break;
+			xfs_iext_destroy_node(node->ptrs[i], level - 1);
+		}
+	}
+
+	kmem_free(node);
+}
+
+void
+xfs_iext_destroy(
+	struct xfs_ifork	*ifp)
+{
+	xfs_iext_destroy_node(ifp->if_u1.if_root, ifp->if_height);
+
+	ifp->if_bytes = 0;
+	ifp->if_height = 0;
+	ifp->if_u1.if_root = NULL;
+}
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 5ac341d2b093..2711ff6ab2b3 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -331,6 +331,7 @@ xfs_iformat_extents(
 	int			size = nex * sizeof(xfs_bmbt_rec_t);
 	struct xfs_iext_cursor	ext;
 	struct xfs_bmbt_rec	*dp;
+	struct xfs_bmbt_irec	new;
 	int			i;
 
 	/*
@@ -346,27 +347,22 @@ xfs_iformat_extents(
 	}
 
 	ifp->if_real_bytes = 0;
-	if (nex == 0)
-		ifp->if_u1.if_extents = NULL;
-	else
-		xfs_iext_add(ifp, 0, nex);
-
-	ifp->if_bytes = size;
+	ifp->if_bytes = 0;
+	ifp->if_u1.if_root = NULL;
+	ifp->if_height = 0;
 	if (size) {
 		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
 
 		xfs_iext_first(ifp, &ext);
 		for (i = 0; i < nex; i++, dp++) {
-			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
-
 			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
 				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
 						 XFS_ERRLEVEL_LOW, mp);
 				return -EFSCORRUPTED;
 			}
 
-			ep->l0 = get_unaligned_be64(&dp->l0);
-			ep->l1 = get_unaligned_be64(&dp->l1);
+			xfs_bmbt_disk_get_all(dp, &new);
+			xfs_iext_insert(ip, &ext, 1, &new, state);
 			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
 			xfs_iext_next(ifp, &ext);
 		}
@@ -435,6 +431,10 @@ xfs_iformat_btree(
 	ifp->if_flags &= ~XFS_IFEXTENTS;
 	ifp->if_flags |= XFS_IFBROOT;
 
+	ifp->if_real_bytes = 0;
+	ifp->if_bytes = 0;
+	ifp->if_u1.if_root = NULL;
+	ifp->if_height = 0;
 	return 0;
 }
 
@@ -662,14 +662,12 @@ xfs_idestroy_fork(
 			ifp->if_u1.if_data = NULL;
 			ifp->if_real_bytes = 0;
 		}
-	} else if ((ifp->if_flags & XFS_IFEXTENTS) &&
-		   ((ifp->if_flags & XFS_IFEXTIREC) ||
-		    (ifp->if_u1.if_extents != NULL))) {
-		ASSERT(ifp->if_real_bytes != 0);
+	} else if ((ifp->if_flags & XFS_IFEXTENTS) && ifp->if_height) {
 		xfs_iext_destroy(ifp);
 	}
-	ASSERT(ifp->if_u1.if_extents == NULL);
+
 	ASSERT(ifp->if_real_bytes == 0);
+
 	if (whichfork == XFS_ATTR_FORK) {
 		kmem_zone_free(xfs_ifork_zone, ip->i_afp);
 		ip->i_afp = NULL;
@@ -679,13 +677,6 @@ xfs_idestroy_fork(
 	}
 }
 
-/* Count number of incore extents based on if_bytes */
-xfs_extnum_t
-xfs_iext_count(struct xfs_ifork *ifp)
-{
-	return ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
-}
-
 /*
  * Convert in-core extents to on-disk form
  *
@@ -780,7 +771,6 @@ xfs_iflush_fork(
 		       !(iip->ili_fields & extflag[whichfork]));
 		if ((iip->ili_fields & extflag[whichfork]) &&
 		    (ifp->if_bytes > 0)) {
-			ASSERT(xfs_iext_get_ext(ifp, 0));
 			ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
 			(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
 				whichfork);
@@ -812,33 +802,6 @@ xfs_iflush_fork(
 	}
 }
 
-/*
- * Return a pointer to the extent record at file index idx.
- */
-xfs_bmbt_rec_host_t *
-xfs_iext_get_ext(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	idx)		/* index of target extent */
-{
-	ASSERT(idx >= 0);
-	ASSERT(idx < xfs_iext_count(ifp));
-
-	if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
-		return ifp->if_u1.if_ext_irec->er_extbuf;
-	} else if (ifp->if_flags & XFS_IFEXTIREC) {
-		xfs_ext_irec_t	*erp;		/* irec pointer */
-		int		erp_idx = 0;	/* irec index */
-		xfs_extnum_t	page_idx = idx;	/* ext index in target list */
-
-		erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0);
-		return &erp->er_extbuf[page_idx];
-	} else if (ifp->if_bytes) {
-		return &ifp->if_u1.if_extents[idx];
-	} else {
-		return NULL;
-	}
-}
-
 /* Convert bmap state flags to an inode fork. */
 struct xfs_ifork *
 xfs_iext_state_to_fork(
@@ -852,894 +815,6 @@ xfs_iext_state_to_fork(
 	return &ip->i_df;
 }
 
-/*
- * Insert new item(s) into the extent records for incore inode
- * fork 'ifp'.  'count' new items are inserted at index 'idx'.
- */
-void
-xfs_iext_insert(
-	xfs_inode_t	*ip,		/* incore inode pointer */
-	struct xfs_iext_cursor *cur,
-	xfs_extnum_t	count,		/* number of inserted items */
-	xfs_bmbt_irec_t	*new,		/* items to insert */
-	int		state)		/* type of extent conversion */
-{
-	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
-	xfs_extnum_t	i;		/* extent record index */
-
-	trace_xfs_iext_insert(ip, cur->idx, new, state, _RET_IP_);
-
-	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
-	xfs_iext_add(ifp, cur->idx, count);
-	for (i = 0; i < count; i++, new++)
-		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx + i), new);
-}
-
-/*
- * This is called when the amount of space required for incore file
- * extents needs to be increased. The ext_diff parameter stores the
- * number of new extents being added and the idx parameter contains
- * the extent index where the new extents will be added. If the new
- * extents are being appended, then we just need to (re)allocate and
- * initialize the space. Otherwise, if the new extents are being
- * inserted into the middle of the existing entries, a bit more work
- * is required to make room for the new extents to be inserted. The
- * caller is responsible for filling in the new extent entries upon
- * return.
- */
-void
-xfs_iext_add(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	idx,		/* index to begin adding exts */
-	int		ext_diff)	/* number of extents to add */
-{
-	int		byte_diff;	/* new bytes being added */
-	int		new_size;	/* size of extents after adding */
-	xfs_extnum_t	nextents;	/* number of extents in file */
-
-	nextents = xfs_iext_count(ifp);
-	ASSERT((idx >= 0) && (idx <= nextents));
-	byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
-	new_size = ifp->if_bytes + byte_diff;
-
-	/*
-	 * Use a linear (direct) extent list.
-	 * If the extents are currently inside the inode,
-	 * xfs_iext_realloc_direct will switch us from
-	 * inline to direct extent allocation mode.
-	 */
-	if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
-		xfs_iext_realloc_direct(ifp, new_size);
-		if (idx < nextents) {
-			memmove(&ifp->if_u1.if_extents[idx + ext_diff],
-				&ifp->if_u1.if_extents[idx],
-				(nextents - idx) * sizeof(xfs_bmbt_rec_t));
-			memset(&ifp->if_u1.if_extents[idx], 0, byte_diff);
-		}
-	}
-	/* Indirection array */
-	else {
-		xfs_ext_irec_t	*erp;
-		int		erp_idx = 0;
-		int		page_idx = idx;
-
-		ASSERT(nextents + ext_diff > XFS_LINEAR_EXTS);
-		if (ifp->if_flags & XFS_IFEXTIREC) {
-			erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 1);
-		} else {
-			xfs_iext_irec_init(ifp);
-			ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-			erp = ifp->if_u1.if_ext_irec;
-		}
-		/* Extents fit in target extent page */
-		if (erp && erp->er_extcount + ext_diff <= XFS_LINEAR_EXTS) {
-			if (page_idx < erp->er_extcount) {
-				memmove(&erp->er_extbuf[page_idx + ext_diff],
-					&erp->er_extbuf[page_idx],
-					(erp->er_extcount - page_idx) *
-					sizeof(xfs_bmbt_rec_t));
-				memset(&erp->er_extbuf[page_idx], 0, byte_diff);
-			}
-			erp->er_extcount += ext_diff;
-			xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
-		}
-		/* Insert a new extent page */
-		else if (erp) {
-			xfs_iext_add_indirect_multi(ifp,
-				erp_idx, page_idx, ext_diff);
-		}
-		/*
-		 * If extent(s) are being appended to the last page in
-		 * the indirection array and the new extent(s) don't fit
-		 * in the page, then erp is NULL and erp_idx is set to
-		 * the next index needed in the indirection array.
-		 */
-		else {
-			uint	count = ext_diff;
-
-			while (count) {
-				erp = xfs_iext_irec_new(ifp, erp_idx);
-				erp->er_extcount = min(count, XFS_LINEAR_EXTS);
-				count -= erp->er_extcount;
-				if (count)
-					erp_idx++;
-			}
-		}
-	}
-	ifp->if_bytes = new_size;
-}
-
-/*
- * This is called when incore extents are being added to the indirection
- * array and the new extents do not fit in the target extent list. The
- * erp_idx parameter contains the irec index for the target extent list
- * in the indirection array, and the idx parameter contains the extent
- * index within the list. The number of extents being added is stored
- * in the count parameter.
- *
- *    |-------|   |-------|
- *    |       |   |       |    idx - number of extents before idx
- *    |  idx  |   | count |
- *    |       |   |       |    count - number of extents being inserted at idx
- *    |-------|   |-------|
- *    | count |   | nex2  |    nex2 - number of extents after idx + count
- *    |-------|   |-------|
- */
-void
-xfs_iext_add_indirect_multi(
-	xfs_ifork_t	*ifp,			/* inode fork pointer */
-	int		erp_idx,		/* target extent irec index */
-	xfs_extnum_t	idx,			/* index within target list */
-	int		count)			/* new extents being added */
-{
-	int		byte_diff;		/* new bytes being added */
-	xfs_ext_irec_t	*erp;			/* pointer to irec entry */
-	xfs_extnum_t	ext_diff;		/* number of extents to add */
-	xfs_extnum_t	ext_cnt;		/* new extents still needed */
-	xfs_extnum_t	nex2;			/* extents after idx + count */
-	xfs_bmbt_rec_t	*nex2_ep = NULL;	/* temp list for nex2 extents */
-	int		nlists;			/* number of irec's (lists) */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	erp = &ifp->if_u1.if_ext_irec[erp_idx];
-	nex2 = erp->er_extcount - idx;
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-
-	/*
-	 * Save second part of target extent list
-	 * (all extents past */
-	if (nex2) {
-		byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
-		nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_NOFS);
-		memmove(nex2_ep, &erp->er_extbuf[idx], byte_diff);
-		erp->er_extcount -= nex2;
-		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -nex2);
-		memset(&erp->er_extbuf[idx], 0, byte_diff);
-	}
-
-	/*
-	 * Add the new extents to the end of the target
-	 * list, then allocate new irec record(s) and
-	 * extent buffer(s) as needed to store the rest
-	 * of the new extents.
-	 */
-	ext_cnt = count;
-	ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS - erp->er_extcount);
-	if (ext_diff) {
-		erp->er_extcount += ext_diff;
-		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
-		ext_cnt -= ext_diff;
-	}
-	while (ext_cnt) {
-		erp_idx++;
-		erp = xfs_iext_irec_new(ifp, erp_idx);
-		ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS);
-		erp->er_extcount = ext_diff;
-		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
-		ext_cnt -= ext_diff;
-	}
-
-	/* Add nex2 extents back to indirection array */
-	if (nex2) {
-		xfs_extnum_t	ext_avail;
-		int		i;
-
-		byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
-		ext_avail = XFS_LINEAR_EXTS - erp->er_extcount;
-		i = 0;
-		/*
-		 * If nex2 extents fit in the current page, append
-		 * nex2_ep after the new extents.
-		 */
-		if (nex2 <= ext_avail) {
-			i = erp->er_extcount;
-		}
-		/*
-		 * Otherwise, check if space is available in the
-		 * next page.
-		 */
-		else if ((erp_idx < nlists - 1) &&
-			 (nex2 <= (ext_avail = XFS_LINEAR_EXTS -
-			  ifp->if_u1.if_ext_irec[erp_idx+1].er_extcount))) {
-			erp_idx++;
-			erp++;
-			/* Create a hole for nex2 extents */
-			memmove(&erp->er_extbuf[nex2], erp->er_extbuf,
-				erp->er_extcount * sizeof(xfs_bmbt_rec_t));
-		}
-		/*
-		 * Final choice, create a new extent page for
-		 * nex2 extents.
-		 */
-		else {
-			erp_idx++;
-			erp = xfs_iext_irec_new(ifp, erp_idx);
-		}
-		memmove(&erp->er_extbuf[i], nex2_ep, byte_diff);
-		kmem_free(nex2_ep);
-		erp->er_extcount += nex2;
-		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2);
-	}
-}
-
-/*
- * This is called when the amount of space required for incore file
- * extents needs to be decreased. The ext_diff parameter stores the
- * number of extents to be removed and the idx parameter contains
- * the extent index where the extents will be removed from.
- *
- * If the amount of space needed has decreased below the linear
- * limit, XFS_IEXT_BUFSZ, then switch to using the contiguous
- * extent array.  Otherwise, use kmem_realloc() to adjust the
- * size to what is needed.
- */
-void
-xfs_iext_remove(
-	xfs_inode_t	*ip,		/* incore inode pointer */
-	struct xfs_iext_cursor *cur,
-	int		ext_diff,	/* number of extents to remove */
-	int		state)		/* type of extent conversion */
-{
-	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
-	xfs_extnum_t	nextents;	/* number of extents in file */
-	int		new_size;	/* size of extents after removal */
-
-	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
-
-	ASSERT(ext_diff > 0);
-	nextents = xfs_iext_count(ifp);
-	new_size = (nextents - ext_diff) * sizeof(xfs_bmbt_rec_t);
-
-	if (new_size == 0) {
-		xfs_iext_destroy(ifp);
-	} else if (ifp->if_flags & XFS_IFEXTIREC) {
-		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
-	} else if (ifp->if_real_bytes) {
-		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
-	}
-	ifp->if_bytes = new_size;
-}
-
-/*
- * This removes ext_diff extents from a linear (direct) extent list,
- * beginning at extent index idx. If the extents are being removed
- * from the end of the list (ie. truncate) then we just need to re-
- * allocate the list to remove the extra space. Otherwise, if the
- * extents are being removed from the middle of the existing extent
- * entries, then we first need to move the extent records beginning
- * at idx + ext_diff up in the list to overwrite the records being
- * removed, then remove the extra space via kmem_realloc.
- */
-void
-xfs_iext_remove_direct(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	idx,		/* index to begin removing exts */
-	int		ext_diff)	/* number of extents to remove */
-{
-	xfs_extnum_t	nextents;	/* number of extents in file */
-	int		new_size;	/* size of extents after removal */
-
-	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
-	new_size = ifp->if_bytes -
-		(ext_diff * sizeof(xfs_bmbt_rec_t));
-	nextents = xfs_iext_count(ifp);
-
-	if (new_size == 0) {
-		xfs_iext_destroy(ifp);
-		return;
-	}
-	/* Move extents up in the list (if needed) */
-	if (idx + ext_diff < nextents) {
-		memmove(&ifp->if_u1.if_extents[idx],
-			&ifp->if_u1.if_extents[idx + ext_diff],
-			(nextents - (idx + ext_diff)) *
-			 sizeof(xfs_bmbt_rec_t));
-	}
-	memset(&ifp->if_u1.if_extents[nextents - ext_diff],
-		0, ext_diff * sizeof(xfs_bmbt_rec_t));
-	/*
-	 * Reallocate the direct extent list. If the extents
-	 * will fit inside the inode then xfs_iext_realloc_direct
-	 * will switch from direct to inline extent allocation
-	 * mode for us.
-	 */
-	xfs_iext_realloc_direct(ifp, new_size);
-	ifp->if_bytes = new_size;
-}
-
-/*
- * This is called when incore extents are being removed from the
- * indirection array and the extents being removed span multiple extent
- * buffers. The idx parameter contains the file extent index where we
- * want to begin removing extents, and the count parameter contains
- * how many extents need to be removed.
- *
- *    |-------|   |-------|
- *    | nex1  |   |       |    nex1 - number of extents before idx
- *    |-------|   | count |
- *    |       |   |       |    count - number of extents being removed at idx
- *    | count |   |-------|
- *    |       |   | nex2  |    nex2 - number of extents after idx + count
- *    |-------|   |-------|
- */
-void
-xfs_iext_remove_indirect(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	idx,		/* index to begin removing extents */
-	int		count)		/* number of extents to remove */
-{
-	xfs_ext_irec_t	*erp;		/* indirection array pointer */
-	int		erp_idx = 0;	/* indirection array index */
-	xfs_extnum_t	ext_cnt;	/* extents left to remove */
-	xfs_extnum_t	ext_diff;	/* extents to remove in current list */
-	xfs_extnum_t	nex1;		/* number of extents before idx */
-	xfs_extnum_t	nex2;		/* extents after idx + count */
-	int		page_idx = idx;	/* index in target extent list */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	erp = xfs_iext_idx_to_irec(ifp,  &page_idx, &erp_idx, 0);
-	ASSERT(erp != NULL);
-	nex1 = page_idx;
-	ext_cnt = count;
-	while (ext_cnt) {
-		nex2 = MAX((erp->er_extcount - (nex1 + ext_cnt)), 0);
-		ext_diff = MIN(ext_cnt, (erp->er_extcount - nex1));
-		/*
-		 * Check for deletion of entire list;
-		 * xfs_iext_irec_remove() updates extent offsets.
-		 */
-		if (ext_diff == erp->er_extcount) {
-			xfs_iext_irec_remove(ifp, erp_idx);
-			ext_cnt -= ext_diff;
-			nex1 = 0;
-			if (ext_cnt) {
-				ASSERT(erp_idx < ifp->if_real_bytes /
-					XFS_IEXT_BUFSZ);
-				erp = &ifp->if_u1.if_ext_irec[erp_idx];
-				nex1 = 0;
-				continue;
-			} else {
-				break;
-			}
-		}
-		/* Move extents up (if needed) */
-		if (nex2) {
-			memmove(&erp->er_extbuf[nex1],
-				&erp->er_extbuf[nex1 + ext_diff],
-				nex2 * sizeof(xfs_bmbt_rec_t));
-		}
-		/* Zero out rest of page */
-		memset(&erp->er_extbuf[nex1 + nex2], 0, (XFS_IEXT_BUFSZ -
-			((nex1 + nex2) * sizeof(xfs_bmbt_rec_t))));
-		/* Update remaining counters */
-		erp->er_extcount -= ext_diff;
-		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -ext_diff);
-		ext_cnt -= ext_diff;
-		nex1 = 0;
-		erp_idx++;
-		erp++;
-	}
-	ifp->if_bytes -= count * sizeof(xfs_bmbt_rec_t);
-	xfs_iext_irec_compact(ifp);
-}
-
-/*
- * Create, destroy, or resize a linear (direct) block of extents.
- */
-void
-xfs_iext_realloc_direct(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	int		new_size)	/* new size of extents after adding */
-{
-	int		rnew_size;	/* real new size of extents */
-
-	rnew_size = new_size;
-
-	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC) ||
-		((new_size >= 0) && (new_size <= XFS_IEXT_BUFSZ) &&
-		 (new_size != ifp->if_real_bytes)));
-
-	/* Free extent records */
-	if (new_size == 0) {
-		xfs_iext_destroy(ifp);
-	} else {
-		if (!is_power_of_2(new_size)){
-			rnew_size = roundup_pow_of_two(new_size);
-		}
-		if (rnew_size != ifp->if_real_bytes) {
-			ifp->if_u1.if_extents =
-				kmem_realloc(ifp->if_u1.if_extents,
-						rnew_size, KM_NOFS);
-		}
-		if (rnew_size > ifp->if_real_bytes) {
-			memset(&ifp->if_u1.if_extents[ifp->if_bytes /
-				(uint)sizeof(xfs_bmbt_rec_t)], 0,
-				rnew_size - ifp->if_real_bytes);
-		}
-	}
-	ifp->if_real_bytes = rnew_size;
-	ifp->if_bytes = new_size;
-}
-
-/*
- * Resize an extent indirection array to new_size bytes.
- */
-STATIC void
-xfs_iext_realloc_indirect(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	int		new_size)	/* new indirection array size */
-{
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	ASSERT(ifp->if_real_bytes);
-	ASSERT((new_size >= 0) &&
-	       (new_size != ((ifp->if_real_bytes / XFS_IEXT_BUFSZ) *
-			     sizeof(xfs_ext_irec_t))));
-	if (new_size == 0) {
-		xfs_iext_destroy(ifp);
-	} else {
-		ifp->if_u1.if_ext_irec =
-			kmem_realloc(ifp->if_u1.if_ext_irec, new_size, KM_NOFS);
-	}
-}
-
-/*
- * Switch from indirection array to linear (direct) extent allocations.
- */
-STATIC void
-xfs_iext_indirect_to_direct(
-	 xfs_ifork_t	*ifp)		/* inode fork pointer */
-{
-	xfs_bmbt_rec_host_t *ep;	/* extent record pointer */
-	xfs_extnum_t	nextents;	/* number of extents in file */
-	int		size;		/* size of file extents */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nextents = xfs_iext_count(ifp);
-	ASSERT(nextents <= XFS_LINEAR_EXTS);
-	size = nextents * sizeof(xfs_bmbt_rec_t);
-
-	xfs_iext_irec_compact_pages(ifp);
-	ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ);
-
-	ep = ifp->if_u1.if_ext_irec->er_extbuf;
-	kmem_free(ifp->if_u1.if_ext_irec);
-	ifp->if_flags &= ~XFS_IFEXTIREC;
-	ifp->if_u1.if_extents = ep;
-	ifp->if_bytes = size;
-	if (nextents < XFS_LINEAR_EXTS) {
-		xfs_iext_realloc_direct(ifp, size);
-	}
-}
-
-/*
- * Remove all records from the indirection array.
- */
-STATIC void
-xfs_iext_irec_remove_all(
-	struct xfs_ifork *ifp)
-{
-	int		nlists;
-	int		i;
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	for (i = 0; i < nlists; i++)
-		kmem_free(ifp->if_u1.if_ext_irec[i].er_extbuf);
-	kmem_free(ifp->if_u1.if_ext_irec);
-	ifp->if_flags &= ~XFS_IFEXTIREC;
-}
-
-/*
- * Free incore file extents.
- */
-void
-xfs_iext_destroy(
-	xfs_ifork_t	*ifp)		/* inode fork pointer */
-{
-	if (ifp->if_flags & XFS_IFEXTIREC) {
-		xfs_iext_irec_remove_all(ifp);
-	} else if (ifp->if_real_bytes) {
-		kmem_free(ifp->if_u1.if_extents);
-	}
-	ifp->if_u1.if_extents = NULL;
-	ifp->if_real_bytes = 0;
-	ifp->if_bytes = 0;
-}
-
-/*
- * Return a pointer to the extent record for file system block bno.
- */
-xfs_bmbt_rec_host_t *			/* pointer to found extent record */
-xfs_iext_bno_to_ext(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_fileoff_t	bno,		/* block number to search for */
-	xfs_extnum_t	*idxp)		/* index of target extent */
-{
-	xfs_bmbt_rec_host_t *base;	/* pointer to first extent */
-	xfs_filblks_t	blockcount = 0;	/* number of blocks in extent */
-	xfs_bmbt_rec_host_t *ep = NULL;	/* pointer to target extent */
-	xfs_ext_irec_t	*erp = NULL;	/* indirection array pointer */
-	int		high;		/* upper boundary in search */
-	xfs_extnum_t	idx = 0;	/* index of target extent */
-	int		low;		/* lower boundary in search */
-	xfs_extnum_t	nextents;	/* number of file extents */
-	xfs_fileoff_t	startoff = 0;	/* start offset of extent */
-
-	nextents = xfs_iext_count(ifp);
-	if (nextents == 0) {
-		*idxp = 0;
-		return NULL;
-	}
-	low = 0;
-	if (ifp->if_flags & XFS_IFEXTIREC) {
-		/* Find target extent list */
-		int	erp_idx = 0;
-		erp = xfs_iext_bno_to_irec(ifp, bno, &erp_idx);
-		base = erp->er_extbuf;
-		high = erp->er_extcount - 1;
-	} else {
-		base = ifp->if_u1.if_extents;
-		high = nextents - 1;
-	}
-	/* Binary search extent records */
-	while (low <= high) {
-		idx = (low + high) >> 1;
-		ep = base + idx;
-		startoff = xfs_bmbt_get_startoff(ep);
-		blockcount = xfs_bmbt_get_blockcount(ep);
-		if (bno < startoff) {
-			high = idx - 1;
-		} else if (bno >= startoff + blockcount) {
-			low = idx + 1;
-		} else {
-			/* Convert back to file-based extent index */
-			if (ifp->if_flags & XFS_IFEXTIREC) {
-				idx += erp->er_extoff;
-			}
-			*idxp = idx;
-			return ep;
-		}
-	}
-	/* Convert back to file-based extent index */
-	if (ifp->if_flags & XFS_IFEXTIREC) {
-		idx += erp->er_extoff;
-	}
-	if (bno >= startoff + blockcount) {
-		if (++idx == nextents) {
-			ep = NULL;
-		} else {
-			ep = xfs_iext_get_ext(ifp, idx);
-		}
-	}
-	*idxp = idx;
-	return ep;
-}
-
-/*
- * Return a pointer to the indirection array entry containing the
- * extent record for filesystem block bno. Store the index of the
- * target irec in *erp_idxp.
- */
-xfs_ext_irec_t *			/* pointer to found extent record */
-xfs_iext_bno_to_irec(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_fileoff_t	bno,		/* block number to search for */
-	int		*erp_idxp)	/* irec index of target ext list */
-{
-	xfs_ext_irec_t	*erp = NULL;	/* indirection array pointer */
-	xfs_ext_irec_t	*erp_next;	/* next indirection array entry */
-	int		erp_idx;	/* indirection array index */
-	int		nlists;		/* number of extent irec's (lists) */
-	int		high;		/* binary search upper limit */
-	int		low;		/* binary search lower limit */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	erp_idx = 0;
-	low = 0;
-	high = nlists - 1;
-	while (low <= high) {
-		erp_idx = (low + high) >> 1;
-		erp = &ifp->if_u1.if_ext_irec[erp_idx];
-		erp_next = erp_idx < nlists - 1 ? erp + 1 : NULL;
-		if (bno < xfs_bmbt_get_startoff(erp->er_extbuf)) {
-			high = erp_idx - 1;
-		} else if (erp_next && bno >=
-			   xfs_bmbt_get_startoff(erp_next->er_extbuf)) {
-			low = erp_idx + 1;
-		} else {
-			break;
-		}
-	}
-	*erp_idxp = erp_idx;
-	return erp;
-}
-
-/*
- * Return a pointer to the indirection array entry containing the
- * extent record at file extent index *idxp. Store the index of the
- * target irec in *erp_idxp and store the page index of the target
- * extent record in *idxp.
- */
-xfs_ext_irec_t *
-xfs_iext_idx_to_irec(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	xfs_extnum_t	*idxp,		/* extent index (file -> page) */
-	int		*erp_idxp,	/* pointer to target irec */
-	int		realloc)	/* new bytes were just added */
-{
-	xfs_ext_irec_t	*prev;		/* pointer to previous irec */
-	xfs_ext_irec_t	*erp = NULL;	/* pointer to current irec */
-	int		erp_idx;	/* indirection array index */
-	int		nlists;		/* number of irec's (ex lists) */
-	int		high;		/* binary search upper limit */
-	int		low;		/* binary search lower limit */
-	xfs_extnum_t	page_idx = *idxp; /* extent index in target list */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	ASSERT(page_idx >= 0);
-	ASSERT(page_idx <= xfs_iext_count(ifp));
-	ASSERT(page_idx < xfs_iext_count(ifp) || realloc);
-
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	erp_idx = 0;
-	low = 0;
-	high = nlists - 1;
-
-	/* Binary search extent irec's */
-	while (low <= high) {
-		erp_idx = (low + high) >> 1;
-		erp = &ifp->if_u1.if_ext_irec[erp_idx];
-		prev = erp_idx > 0 ? erp - 1 : NULL;
-		if (page_idx < erp->er_extoff || (page_idx == erp->er_extoff &&
-		     realloc && prev && prev->er_extcount < XFS_LINEAR_EXTS)) {
-			high = erp_idx - 1;
-		} else if (page_idx > erp->er_extoff + erp->er_extcount ||
-			   (page_idx == erp->er_extoff + erp->er_extcount &&
-			    !realloc)) {
-			low = erp_idx + 1;
-		} else if (page_idx == erp->er_extoff + erp->er_extcount &&
-			   erp->er_extcount == XFS_LINEAR_EXTS) {
-			ASSERT(realloc);
-			page_idx = 0;
-			erp_idx++;
-			erp = erp_idx < nlists ? erp + 1 : NULL;
-			break;
-		} else {
-			page_idx -= erp->er_extoff;
-			break;
-		}
-	}
-	*idxp = page_idx;
-	*erp_idxp = erp_idx;
-	return erp;
-}
-
-/*
- * Allocate and initialize an indirection array once the space needed
- * for incore extents increases above XFS_IEXT_BUFSZ.
- */
-void
-xfs_iext_irec_init(
-	xfs_ifork_t	*ifp)		/* inode fork pointer */
-{
-	xfs_ext_irec_t	*erp;		/* indirection array pointer */
-	xfs_extnum_t	nextents;	/* number of extents in file */
-
-	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
-	nextents = xfs_iext_count(ifp);
-	ASSERT(nextents <= XFS_LINEAR_EXTS);
-
-	erp = kmem_alloc(sizeof(xfs_ext_irec_t), KM_NOFS);
-
-	if (nextents == 0) {
-		ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
-	} else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
-		xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
-	}
-	erp->er_extbuf = ifp->if_u1.if_extents;
-	erp->er_extcount = nextents;
-	erp->er_extoff = 0;
-
-	ifp->if_flags |= XFS_IFEXTIREC;
-	ifp->if_real_bytes = XFS_IEXT_BUFSZ;
-	ifp->if_bytes = nextents * sizeof(xfs_bmbt_rec_t);
-	ifp->if_u1.if_ext_irec = erp;
-
-	return;
-}
-
-/*
- * Allocate and initialize a new entry in the indirection array.
- */
-xfs_ext_irec_t *
-xfs_iext_irec_new(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	int		erp_idx)	/* index for new irec */
-{
-	xfs_ext_irec_t	*erp;		/* indirection array pointer */
-	int		i;		/* loop counter */
-	int		nlists;		/* number of irec's (ex lists) */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-
-	/* Resize indirection array */
-	xfs_iext_realloc_indirect(ifp, ++nlists *
-				  sizeof(xfs_ext_irec_t));
-	/*
-	 * Move records down in the array so the
-	 * new page can use erp_idx.
-	 */
-	erp = ifp->if_u1.if_ext_irec;
-	for (i = nlists - 1; i > erp_idx; i--) {
-		memmove(&erp[i], &erp[i-1], sizeof(xfs_ext_irec_t));
-	}
-	ASSERT(i == erp_idx);
-
-	/* Initialize new extent record */
-	erp = ifp->if_u1.if_ext_irec;
-	erp[erp_idx].er_extbuf = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
-	ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
-	memset(erp[erp_idx].er_extbuf, 0, XFS_IEXT_BUFSZ);
-	erp[erp_idx].er_extcount = 0;
-	erp[erp_idx].er_extoff = erp_idx > 0 ?
-		erp[erp_idx-1].er_extoff + erp[erp_idx-1].er_extcount : 0;
-	return (&erp[erp_idx]);
-}
-
-/*
- * Remove a record from the indirection array.
- */
-void
-xfs_iext_irec_remove(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	int		erp_idx)	/* irec index to remove */
-{
-	xfs_ext_irec_t	*erp;		/* indirection array pointer */
-	int		i;		/* loop counter */
-	int		nlists;		/* number of irec's (ex lists) */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	erp = &ifp->if_u1.if_ext_irec[erp_idx];
-	if (erp->er_extbuf) {
-		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1,
-			-erp->er_extcount);
-		kmem_free(erp->er_extbuf);
-	}
-	/* Compact extent records */
-	erp = ifp->if_u1.if_ext_irec;
-	for (i = erp_idx; i < nlists - 1; i++) {
-		memmove(&erp[i], &erp[i+1], sizeof(xfs_ext_irec_t));
-	}
-	/*
-	 * Manually free the last extent record from the indirection
-	 * array.  A call to xfs_iext_realloc_indirect() with a size
-	 * of zero would result in a call to xfs_iext_destroy() which
-	 * would in turn call this function again, creating a nasty
-	 * infinite loop.
-	 */
-	if (--nlists) {
-		xfs_iext_realloc_indirect(ifp,
-			nlists * sizeof(xfs_ext_irec_t));
-	} else {
-		kmem_free(ifp->if_u1.if_ext_irec);
-	}
-	ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
-}
-
-/*
- * This is called to clean up large amounts of unused memory allocated
- * by the indirection array.  Before compacting anything though, verify
- * that the indirection array is still needed and switch back to the
- * linear extent list (or even the inline buffer) if possible.  The
- * compaction policy is as follows:
- *
- *    Full Compaction: Extents fit into a single page (or inline buffer)
- * Partial Compaction: Extents occupy less than 50% of allocated space
- *      No Compaction: Extents occupy at least 50% of allocated space
- */
-void
-xfs_iext_irec_compact(
-	xfs_ifork_t	*ifp)		/* inode fork pointer */
-{
-	xfs_extnum_t	nextents;	/* number of extents in file */
-	int		nlists;		/* number of irec's (ex lists) */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	nextents = xfs_iext_count(ifp);
-
-	if (nextents == 0) {
-		xfs_iext_destroy(ifp);
-	} else if (nextents <= XFS_LINEAR_EXTS) {
-		xfs_iext_indirect_to_direct(ifp);
-	} else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
-		xfs_iext_irec_compact_pages(ifp);
-	}
-}
-
-/*
- * Combine extents from neighboring extent pages.
- */
-void
-xfs_iext_irec_compact_pages(
-	xfs_ifork_t	*ifp)		/* inode fork pointer */
-{
-	xfs_ext_irec_t	*erp, *erp_next;/* pointers to irec entries */
-	int		erp_idx = 0;	/* indirection array index */
-	int		nlists;		/* number of irec's (ex lists) */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	while (erp_idx < nlists - 1) {
-		erp = &ifp->if_u1.if_ext_irec[erp_idx];
-		erp_next = erp + 1;
-		if (erp_next->er_extcount <=
-		    (XFS_LINEAR_EXTS - erp->er_extcount)) {
-			memcpy(&erp->er_extbuf[erp->er_extcount],
-				erp_next->er_extbuf, erp_next->er_extcount *
-				sizeof(xfs_bmbt_rec_t));
-			erp->er_extcount += erp_next->er_extcount;
-			/*
-			 * Free page before removing extent record
-			 * so er_extoffs don't get modified in
-			 * xfs_iext_irec_remove.
-			 */
-			kmem_free(erp_next->er_extbuf);
-			erp_next->er_extbuf = NULL;
-			xfs_iext_irec_remove(ifp, erp_idx + 1);
-			nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-		} else {
-			erp_idx++;
-		}
-	}
-}
-
-/*
- * This is called to update the er_extoff field in the indirection
- * array when extents have been added or removed from one of the
- * extent lists. erp_idx contains the irec index to begin updating
- * at and ext_diff contains the number of extents that were added
- * or removed.
- */
-void
-xfs_iext_irec_update_extoffs(
-	xfs_ifork_t	*ifp,		/* inode fork pointer */
-	int		erp_idx,	/* irec index to update */
-	int		ext_diff)	/* number of new extents */
-{
-	int		i;		/* loop counter */
-	int		nlists;		/* number of irec's (ex lists */
-
-	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	for (i = erp_idx; i < nlists; i++) {
-		ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
-	}
-}
-
 /*
  * Initialize an inode's copy-on-write fork.
  */
@@ -1756,87 +831,3 @@ xfs_ifork_init_cow(
 	ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
 	ip->i_cnextents = 0;
 }
-
-/*
- * Lookup the extent covering bno.
- *
- * If there is an extent covering bno return the extent index, and store the
- * expanded extent structure in *gotp, and the extent cursor in *cur.
- * If there is no extent covering bno, but there is an extent after it (e.g.
- * it lies in a hole) return that extent in *gotp and its cursor in *cur
- * instead.
- * If bno is beyond the last extent return false, and return an invalid
- * cursor value.
- */
-bool
-xfs_iext_lookup_extent(
-	struct xfs_inode	*ip,
-	struct xfs_ifork	*ifp,
-	xfs_fileoff_t		bno,
-	struct xfs_iext_cursor	*cur,
-	struct xfs_bmbt_irec	*gotp)
-{
-	struct xfs_bmbt_rec_host *ep;
-
-	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
-
-	ep = xfs_iext_bno_to_ext(ifp, bno, &cur->idx);
-	if (!ep)
-		return false;
-	xfs_bmbt_get_all(ep, gotp);
-	return true;
-}
-
-/*
- * Returns the last extent before end, and if this extent doesn't cover
- * end, update end to the end of the extent.
- */
-bool
-xfs_iext_lookup_extent_before(
-	struct xfs_inode	*ip,
-	struct xfs_ifork	*ifp,
-	xfs_fileoff_t		*end,
-	struct xfs_iext_cursor	*cur,
-	struct xfs_bmbt_irec	*gotp)
-{
-	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
-	    gotp->br_startoff <= *end - 1)
-		return true;
-	if (!xfs_iext_prev_extent(ifp, cur, gotp))
-		return false;
-	*end = gotp->br_startoff + gotp->br_blockcount;
-	return true;
-}
-
-/*
- * Return true if there is an extent at cursor cur and return the expanded
- * extent structure at cur in gotp in that case.  Else return false.
- */
-bool
-xfs_iext_get_extent(
-	struct xfs_ifork	*ifp,
-	struct xfs_iext_cursor	*cur,
-	struct xfs_bmbt_irec	*gotp)
-{
-	if (cur->idx < 0 || cur->idx >= xfs_iext_count(ifp))
-		return false;
-	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
-	return true;
-}
-
-void
-xfs_iext_update_extent(
-	struct xfs_inode	*ip,
-	int			state,
-	struct xfs_iext_cursor	*cur,
-	struct xfs_bmbt_irec	*gotp)
-{
-	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
-
-	ASSERT(cur->idx >= 0);
-	ASSERT(cur->idx < xfs_iext_count(ifp));
-
-	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
-	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
-	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
-}
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index 508f13784334..015872caaab4 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -21,45 +21,18 @@
 struct xfs_inode_log_item;
 struct xfs_dinode;
 
-/*
- * The following xfs_ext_irec_t struct introduces a second (top) level
- * to the in-core extent allocation scheme. These structs are allocated
- * in a contiguous block, creating an indirection array where each entry
- * (irec) contains a pointer to a buffer of in-core extent records which
- * it manages. Each extent buffer is 4k in size, since 4k is the system
- * page size on Linux i386 and systems with larger page sizes don't seem
- * to gain much, if anything, by using their native page size as the
- * extent buffer size. Also, using 4k extent buffers everywhere provides
- * a consistent interface for CXFS across different platforms.
- *
- * There is currently no limit on the number of irec's (extent lists)
- * allowed, so heavily fragmented files may require an indirection array
- * which spans multiple system pages of memory. The number of extents
- * which would require this amount of contiguous memory is very large
- * and should not cause problems in the foreseeable future. However,
- * if the memory needed for the contiguous array ever becomes a problem,
- * it is possible that a third level of indirection may be required.
- */
-typedef struct xfs_ext_irec {
-	xfs_bmbt_rec_host_t *er_extbuf;	/* block of extent records */
-	xfs_extnum_t	er_extoff;	/* extent offset in file */
-	xfs_extnum_t	er_extcount;	/* number of extents in page/block */
-} xfs_ext_irec_t;
-
 /*
  * File incore extent information, present for each of data & attr forks.
  */
-#define	XFS_IEXT_BUFSZ		4096
-#define	XFS_LINEAR_EXTS		(XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t))
 typedef struct xfs_ifork {
 	int			if_bytes;	/* bytes in if_u1 */
 	int			if_real_bytes;	/* bytes allocated in if_u1 */
 	struct xfs_btree_block	*if_broot;	/* file's incore btree root */
 	short			if_broot_bytes;	/* bytes allocated for root */
 	unsigned char		if_flags;	/* per-fork flags */
+	int			if_height;	/* height of the extent tree */
 	union {
-		xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */
-		xfs_ext_irec_t	*if_ext_irec;	/* irec map file exts */
+		void		*if_root;	/* extent tree root */
 		char		*if_data;	/* inline file data */
 	} if_u1;
 } xfs_ifork_t;
@@ -70,7 +43,6 @@ typedef struct xfs_ifork {
 #define	XFS_IFINLINE	0x01	/* Inline data is read in */
 #define	XFS_IFEXTENTS	0x02	/* All extent pointers are read in */
 #define	XFS_IFBROOT	0x04	/* i_broot points to the bmap b-tree root */
-#define	XFS_IFEXTIREC	0x08	/* Indirection array of extent blocks */
 
 /*
  * Fork handling.
@@ -140,35 +112,12 @@ int		xfs_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *,
 				  int);
 void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
 
-struct xfs_bmbt_rec_host *
-		xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
-xfs_extnum_t	xfs_iext_count(struct xfs_ifork *);
+xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
 void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
 			xfs_extnum_t, struct xfs_bmbt_irec *, int);
-void		xfs_iext_add(struct xfs_ifork *, xfs_extnum_t, int);
-void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
-					    xfs_extnum_t, int);
 void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
 			int, int);
-void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
-void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
-void		xfs_iext_realloc_direct(struct xfs_ifork *, int);
 void		xfs_iext_destroy(struct xfs_ifork *);
-struct xfs_bmbt_rec_host *
-		xfs_iext_bno_to_ext(struct xfs_ifork *, xfs_fileoff_t, int *);
-struct xfs_ext_irec *
-		xfs_iext_bno_to_irec(struct xfs_ifork *, xfs_fileoff_t, int *);
-struct xfs_ext_irec *
-		xfs_iext_idx_to_irec(struct xfs_ifork *, xfs_extnum_t *, int *,
-				     int);
-void		xfs_iext_irec_init(struct xfs_ifork *);
-struct xfs_ext_irec *
-		xfs_iext_irec_new(struct xfs_ifork *, int);
-void		xfs_iext_irec_remove(struct xfs_ifork *, int);
-void		xfs_iext_irec_compact(struct xfs_ifork *);
-void		xfs_iext_irec_compact_pages(struct xfs_ifork *);
-void		xfs_iext_irec_compact_full(struct xfs_ifork *);
-void		xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
 
 bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
 			struct xfs_ifork *ifp, xfs_fileoff_t bno,
@@ -185,29 +134,10 @@ void		xfs_iext_update_extent(struct xfs_inode *ip, int state,
 			struct xfs_iext_cursor *cur,
 			struct xfs_bmbt_irec *gotp);
 
-static inline void xfs_iext_first(struct xfs_ifork *ifp,
-		struct xfs_iext_cursor *cur)
-{
-	cur->idx = 0;
-}
-
-static inline void xfs_iext_last(struct xfs_ifork *ifp,
-		struct xfs_iext_cursor *cur)
-{
-	cur->idx = xfs_iext_count(ifp) - 1;
-}
-
-static inline void xfs_iext_next(struct xfs_ifork *ifp,
-		struct xfs_iext_cursor *cur)
-{
-	cur->idx++;
-}
-
-static inline void xfs_iext_prev(struct xfs_ifork *ifp,
-		struct xfs_iext_cursor *cur)
-{
-	cur->idx--;
-}
+void		xfs_iext_first(struct xfs_ifork *, struct xfs_iext_cursor *);
+void		xfs_iext_last(struct xfs_ifork *, struct xfs_iext_cursor *);
+void		xfs_iext_next(struct xfs_ifork *, struct xfs_iext_cursor *);
+void		xfs_iext_prev(struct xfs_ifork *, struct xfs_iext_cursor *);
 
 static inline bool xfs_iext_next_extent(struct xfs_ifork *ifp,
 		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
index 5da6382bdaf1..983878019097 100644
--- a/fs/xfs/libxfs/xfs_types.h
+++ b/fs/xfs/libxfs/xfs_types.h
@@ -143,7 +143,8 @@ typedef uint32_t	xfs_dqid_t;
 #define	XFS_WORDMASK	((1 << XFS_WORDLOG) - 1)
 
 struct xfs_iext_cursor {
-	xfs_extnum_t		idx;
+	struct xfs_iext_leaf	*leaf;
+	int			pos;
 };
 
 #endif	/* __XFS_TYPES_H__ */
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 778b709dbd0c..72d753d898d3 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -168,7 +168,6 @@ xfs_scrub_bmapbt_rec(
 	struct xfs_scrub_btree		*bs,
 	union xfs_btree_rec		*rec)
 {
-	struct xfs_bmbt_rec_host	ihost;
 	struct xfs_bmbt_irec		irec;
 	struct xfs_scrub_bmap_info	*info = bs->private;
 	struct xfs_inode		*ip = bs->cur->bc_private.b.ip;
@@ -193,9 +192,7 @@ xfs_scrub_bmapbt_rec(
 	}
 
 	/* Set up the in-core record and scrub it. */
-	ihost.l0 = be64_to_cpu(rec->bmbt.l0);
-	ihost.l1 = be64_to_cpu(rec->bmbt.l1);
-	xfs_bmbt_get_all(&ihost, &irec);
+	xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
 	return xfs_scrub_bmap_extent(ip, bs->cur, info, &irec);
 }
 
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index a929ca72fa8e..5b5128ed0f18 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -933,7 +933,7 @@ xfs_ialloc(
 		ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
 		ip->i_df.if_flags = XFS_IFEXTENTS;
 		ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
-		ip->i_df.if_u1.if_extents = NULL;
+		ip->i_df.if_u1.if_root = NULL;
 		break;
 	default:
 		ASSERT(0);
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index eb6f4f7c9520..6ee5c3bf19ad 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -162,7 +162,6 @@ xfs_inode_item_format_data_fork(
 		    ip->i_df.if_bytes > 0) {
 			struct xfs_bmbt_rec *p;
 
-			ASSERT(ip->i_df.if_u1.if_extents != NULL);
 			ASSERT(xfs_iext_count(&ip->i_df) > 0);
 
 			p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IEXT);
@@ -252,7 +251,6 @@ xfs_inode_item_format_attr_fork(
 
 			ASSERT(xfs_iext_count(ip->i_afp) ==
 				ip->i_d.di_anextents);
-			ASSERT(ip->i_afp->if_u1.if_extents != NULL);
 
 			p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_EXT);
 			data_bytes = xfs_iextents_copy(ip, p, XFS_ATTR_FORK);
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 667bfce802cd..515ba042d75c 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -218,45 +218,6 @@ TRACE_EVENT(xfs_attr_list_node_descend,
 		   __entry->bt_before)
 );
 
-TRACE_EVENT(xfs_iext_insert,
-	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx,
-		 struct xfs_bmbt_irec *r, int state, unsigned long caller_ip),
-	TP_ARGS(ip, idx, r, state, caller_ip),
-	TP_STRUCT__entry(
-		__field(dev_t, dev)
-		__field(xfs_ino_t, ino)
-		__field(xfs_extnum_t, idx)
-		__field(xfs_fileoff_t, startoff)
-		__field(xfs_fsblock_t, startblock)
-		__field(xfs_filblks_t, blockcount)
-		__field(xfs_exntst_t, state)
-		__field(int, bmap_state)
-		__field(unsigned long, caller_ip)
-	),
-	TP_fast_assign(
-		__entry->dev = VFS_I(ip)->i_sb->s_dev;
-		__entry->ino = ip->i_ino;
-		__entry->idx = idx;
-		__entry->startoff = r->br_startoff;
-		__entry->startblock = r->br_startblock;
-		__entry->blockcount = r->br_blockcount;
-		__entry->state = r->br_state;
-		__entry->bmap_state = state;
-		__entry->caller_ip = caller_ip;
-	),
-	TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
-		  "offset %lld block %lld count %lld flag %d caller %ps",
-		  MAJOR(__entry->dev), MINOR(__entry->dev),
-		  __entry->ino,
-		  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
-		  (long)__entry->idx,
-		  __entry->startoff,
-		  (int64_t)__entry->startblock,
-		  __entry->blockcount,
-		  __entry->state,
-		  (char *)__entry->caller_ip)
-);
-
 DECLARE_EVENT_CLASS(xfs_bmap_class,
 	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state,
 		 unsigned long caller_ip),
@@ -264,7 +225,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
 	TP_STRUCT__entry(
 		__field(dev_t, dev)
 		__field(xfs_ino_t, ino)
-		__field(xfs_extnum_t, idx)
+		__field(void *, leaf);
+		__field(int, pos);
 		__field(xfs_fileoff_t, startoff)
 		__field(xfs_fsblock_t, startblock)
 		__field(xfs_filblks_t, blockcount)
@@ -280,7 +242,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
 		xfs_iext_get_extent(ifp, cur, &r);
 		__entry->dev = VFS_I(ip)->i_sb->s_dev;
 		__entry->ino = ip->i_ino;
-		__entry->idx = cur->idx;
+		__entry->leaf = cur->leaf;
+		__entry->pos = cur->pos;
 		__entry->startoff = r.br_startoff;
 		__entry->startblock = r.br_startblock;
 		__entry->blockcount = r.br_blockcount;
@@ -288,12 +251,13 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
 		__entry->bmap_state = state;
 		__entry->caller_ip = caller_ip;
 	),
-	TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
+	TP_printk("dev %d:%d ino 0x%llx state %s cur 0x%p/%d "
 		  "offset %lld block %lld count %lld flag %d caller %ps",
 		  MAJOR(__entry->dev), MINOR(__entry->dev),
 		  __entry->ino,
 		  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
-		  (long)__entry->idx,
+		  __entry->leaf,
+		  __entry->pos,
 		  __entry->startoff,
 		  (int64_t)__entry->startblock,
 		  __entry->blockcount,
@@ -306,6 +270,7 @@ DEFINE_EVENT(xfs_bmap_class, name, \
 	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state, \
 		 unsigned long caller_ip), \
 	TP_ARGS(ip, cur, state, caller_ip))
+DEFINE_BMAP_EVENT(xfs_iext_insert);
 DEFINE_BMAP_EVENT(xfs_iext_remove);
 DEFINE_BMAP_EVENT(xfs_bmap_pre_update);
 DEFINE_BMAP_EVENT(xfs_bmap_post_update);
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 17/18] xfs: remove the nr_extents argument to xfs_iext_insert
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (15 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 16/18] xfs: use a b+tree for the in-core extent list Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 22:35   ` Darrick J. Wong
  2017-10-31 14:22 ` [PATCH 18/18] xfs: remove the nr_extents argument to xfs_iext_remove Christoph Hellwig
  2017-11-01  3:08 ` b+tree for the incore extent list Darrick J. Wong
  18 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

We only have two places that insert 2 extents at the same time, so unroll
the loop there.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c       | 31 ++++++++++++++++---------------
 fs/xfs/libxfs/xfs_iext_tree.c  | 31 ++++++++-----------------------
 fs/xfs/libxfs/xfs_inode_fork.c |  2 +-
 fs/xfs/libxfs/xfs_inode_fork.h |  2 +-
 4 files changed, 26 insertions(+), 40 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 46bda00dca79..b832dc402f40 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -900,7 +900,7 @@ xfs_bmap_local_to_extents(
 	rec.br_blockcount = 1;
 	rec.br_state = XFS_EXT_NORM;
 	xfs_iext_first(ifp, &ext);
-	xfs_iext_insert(ip, &ext, 1, &rec, 0);
+	xfs_iext_insert(ip, &ext, &rec, 0);
 
 	XFS_IFORK_NEXT_SET(ip, whichfork, 1);
 	ip->i_d.di_nblocks = 1;
@@ -1272,7 +1272,7 @@ xfs_iread_extents(
 				goto out_brelse;
 			}
 			xfs_bmbt_disk_get_all(frp, &new);
-			xfs_iext_insert(ip, &ext, 1, &new, state);
+			xfs_iext_insert(ip, &ext, &new, state);
 			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
 			xfs_iext_next(ifp, &ext);
 		}
@@ -1828,7 +1828,7 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_blockcount = temp;
 		PREV.br_startblock = nullstartblock(da_new);
 		xfs_iext_next(ifp, &bma->ext);
-		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
+		xfs_iext_insert(bma->ip, &bma->ext, &PREV, state);
 		xfs_iext_prev(ifp, &bma->ext);
 		break;
 
@@ -1904,7 +1904,7 @@ xfs_bmap_add_extent_delay_real(
 
 		PREV.br_startblock = nullstartblock(da_new);
 		PREV.br_blockcount = temp;
-		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
+		xfs_iext_insert(bma->ip, &bma->ext, &PREV, state);
 		xfs_iext_next(ifp, &bma->ext);
 		break;
 
@@ -1950,9 +1950,9 @@ xfs_bmap_add_extent_delay_real(
 					PREV.br_blockcount));
 		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
 
-		/* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
 		xfs_iext_next(ifp, &bma->ext);
-		xfs_iext_insert(bma->ip, &bma->ext, 2, &LEFT, state);
+		xfs_iext_insert(bma->ip, &bma->ext, &RIGHT, state);
+		xfs_iext_insert(bma->ip, &bma->ext, &LEFT, state);
 		(*nextents)++;
 
 		if (bma->cur == NULL)
@@ -2316,7 +2316,7 @@ xfs_bmap_add_extent_unwritten_real(
 		PREV.br_blockcount -= new->br_blockcount;
 
 		xfs_iext_update_extent(ip, state, ext, &PREV);
-		xfs_iext_insert(ip, ext, 1, new, state);
+		xfs_iext_insert(ip, ext, new, state);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 		if (cur == NULL)
@@ -2383,7 +2383,7 @@ xfs_bmap_add_extent_unwritten_real(
 
 		xfs_iext_update_extent(ip, state, ext, &PREV);
 		xfs_iext_next(ifp, ext);
-		xfs_iext_insert(ip, ext, 1, new, state);
+		xfs_iext_insert(ip, ext, new, state);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
@@ -2426,7 +2426,8 @@ xfs_bmap_add_extent_unwritten_real(
 
 		xfs_iext_update_extent(ip, state, ext, &PREV);
 		xfs_iext_next(ifp, ext);
-		xfs_iext_insert(ip, ext, 2, &r[0], state);
+		xfs_iext_insert(ip, ext, &r[1], state);
+		xfs_iext_insert(ip, ext, &r[0], state);
 
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 				XFS_IFORK_NEXTENTS(ip, whichfork) + 2);
@@ -2634,7 +2635,7 @@ xfs_bmap_add_extent_hole_delay(
 		 * Insert a new entry.
 		 */
 		oldlen = newlen = 0;
-		xfs_iext_insert(ip, ext, 1, new, state);
+		xfs_iext_insert(ip, ext, new, state);
 		break;
 	}
 	if (oldlen != newlen) {
@@ -2818,7 +2819,7 @@ xfs_bmap_add_extent_hole_real(
 		 * real allocation.
 		 * Insert a new entry.
 		 */
-		xfs_iext_insert(ip, ext, 1, new, state);
+		xfs_iext_insert(ip, ext, new, state);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 		if (cur == NULL) {
@@ -4741,7 +4742,7 @@ xfs_bmap_del_extent_delay(
 
 		xfs_iext_update_extent(ip, state, ext, got);
 		xfs_iext_next(ifp, ext);
-		xfs_iext_insert(ip, ext, 1, &new, state);
+		xfs_iext_insert(ip, ext, &new, state);
 
 		da_new = got_indlen + new_indlen - stolen;
 		del->br_blockcount -= stolen;
@@ -4822,7 +4823,7 @@ xfs_bmap_del_extent_cow(
 
 		xfs_iext_update_extent(ip, state, ext, got);
 		xfs_iext_next(ifp, ext);
-		xfs_iext_insert(ip, ext, 1, &new, state);
+		xfs_iext_insert(ip, ext, &new, state);
 		break;
 	}
 }
@@ -5035,7 +5036,7 @@ xfs_bmap_del_extent_real(
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 		xfs_iext_next(ifp, ext);
-		xfs_iext_insert(ip, ext, 1, &new, state);
+		xfs_iext_insert(ip, ext, &new, state);
 		break;
 	}
 
@@ -5896,7 +5897,7 @@ xfs_bmap_split_extent_at(
 
 	/* Add new extent */
 	xfs_iext_next(ifp, &ext);
-	xfs_iext_insert(ip, &ext, 1, &new, 0);
+	xfs_iext_insert(ip, &ext, &new, 0);
 	XFS_IFORK_NEXT_SET(ip, whichfork,
 			   XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
 
diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
index acb66c0677e7..c109526e52c5 100644
--- a/fs/xfs/libxfs/xfs_iext_tree.c
+++ b/fs/xfs/libxfs/xfs_iext_tree.c
@@ -627,16 +627,20 @@ xfs_iext_realloc_root(
 	cur->leaf = new;
 }
 
-static void
-__xfs_iext_insert(
-	struct xfs_ifork	*ifp,
+void
+xfs_iext_insert(
+	struct xfs_inode	*ip,
 	struct xfs_iext_cursor	*cur,
-	struct xfs_bmbt_irec	*irec)
+	struct xfs_bmbt_irec	*irec,
+	int			state)
 {
+	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
 	xfs_fileoff_t		offset = irec->br_startoff;
 	struct xfs_iext_leaf	*new = NULL;
 	int			nr_entries, i;
 
+	trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
+
 	if (ifp->if_height == 0)
 		xfs_iext_alloc_root(ifp, cur);
 	else if (ifp->if_height == 1)
@@ -664,25 +668,6 @@ __xfs_iext_insert(
 		xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
 }
 
-void
-xfs_iext_insert(
-	struct xfs_inode	*ip,
-	struct xfs_iext_cursor	*cur,
-	xfs_extnum_t		nr_extents,
-	struct xfs_bmbt_irec	*new,
-	int			state)
-{
-	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
-	int			i;
-
-	ASSERT(nr_extents > 0);
-
-	for (i = nr_extents - 1; i >= 0; i--) {
-		__xfs_iext_insert(ifp, cur, new + i);
-		trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
-	}
-}
-
 static struct xfs_iext_node *
 xfs_iext_rebalance_node(
 	struct xfs_iext_node	*parent,
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 2711ff6ab2b3..dca5dcec2cd4 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -362,7 +362,7 @@ xfs_iformat_extents(
 			}
 
 			xfs_bmbt_disk_get_all(dp, &new);
-			xfs_iext_insert(ip, &ext, 1, &new, state);
+			xfs_iext_insert(ip, &ext, &new, state);
 			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
 			xfs_iext_next(ifp, &ext);
 		}
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index 015872caaab4..cc73fea4e37e 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -114,7 +114,7 @@ void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
 
 xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
 void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
-			xfs_extnum_t, struct xfs_bmbt_irec *, int);
+			struct xfs_bmbt_irec *, int);
 void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
 			int, int);
 void		xfs_iext_destroy(struct xfs_ifork *);
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH 18/18] xfs: remove the nr_extents argument to xfs_iext_remove
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (16 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 17/18] xfs: remove the nr_extents argument to xfs_iext_insert Christoph Hellwig
@ 2017-10-31 14:22 ` Christoph Hellwig
  2017-10-31 22:37   ` Darrick J. Wong
  2017-11-01  3:08 ` b+tree for the incore extent list Darrick J. Wong
  18 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-10-31 14:22 UTC (permalink / raw)
  To: linux-xfs

We only have two places that remove 2 extents at the same time, so unroll
the loop there.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c       | 31 ++++++++++++++-----------------
 fs/xfs/libxfs/xfs_iext_tree.c  | 40 ++++++++++++----------------------------
 fs/xfs/libxfs/xfs_inode_fork.h |  2 +-
 3 files changed, 27 insertions(+), 46 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index b832dc402f40..ce3cedd3f636 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -1196,11 +1196,6 @@ xfs_iread_extents(
 		return -EFSCORRUPTED;
 	}
 
-	ifp->if_bytes = 0;
-	ifp->if_real_bytes = 0;
-	ifp->if_u1.if_root = NULL;
-	ifp->if_height = 0;
-
 	/*
 	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
 	 */
@@ -1649,7 +1644,8 @@ xfs_bmap_add_extent_delay_real(
 		 */
 		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
 
-		xfs_iext_remove(bma->ip, &bma->ext, 2, state);
+		xfs_iext_remove(bma->ip, &bma->ext, state);
+		xfs_iext_remove(bma->ip, &bma->ext, state);
 		xfs_iext_prev(ifp, &bma->ext);
 		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
 		(*nextents)--;
@@ -1684,7 +1680,7 @@ xfs_bmap_add_extent_delay_real(
 		old = LEFT;
 		LEFT.br_blockcount += PREV.br_blockcount;
 
-		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
+		xfs_iext_remove(bma->ip, &bma->ext, state);
 		xfs_iext_prev(ifp, &bma->ext);
 		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
 
@@ -1711,7 +1707,7 @@ xfs_bmap_add_extent_delay_real(
 		PREV.br_blockcount += RIGHT.br_blockcount;
 
 		xfs_iext_next(ifp, &bma->ext);
-		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
+		xfs_iext_remove(bma->ip, &bma->ext, state);
 		xfs_iext_prev(ifp, &bma->ext);
 		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
 
@@ -2148,7 +2144,8 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
 
-		xfs_iext_remove(ip, ext, 2, state);
+		xfs_iext_remove(ip, ext, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		xfs_iext_update_extent(ip, state, ext, &LEFT);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
@@ -2186,7 +2183,7 @@ xfs_bmap_add_extent_unwritten_real(
 		 */
 		LEFT.br_blockcount += PREV.br_blockcount;
 
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		xfs_iext_update_extent(ip, state, ext, &LEFT);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
@@ -2220,7 +2217,7 @@ xfs_bmap_add_extent_unwritten_real(
 		PREV.br_state = new->br_state;
 
 		xfs_iext_next(ifp, ext);
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		xfs_iext_update_extent(ip, state, ext, &PREV);
 
@@ -2587,7 +2584,7 @@ xfs_bmap_add_extent_hole_delay(
 		left.br_startblock = nullstartblock(newlen);
 		left.br_blockcount = temp;
 
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		xfs_iext_update_extent(ip, state, ext, &left);
 		break;
@@ -2732,7 +2729,7 @@ xfs_bmap_add_extent_hole_real(
 		 */
 		left.br_blockcount += new->br_blockcount + right.br_blockcount;
 
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		xfs_iext_update_extent(ip, state, ext, &left);
 
@@ -4690,7 +4687,7 @@ xfs_bmap_del_extent_delay(
 		/*
 		 * Matches the whole extent.  Delete the entry.
 		 */
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		break;
 	case BMAP_LEFT_FILLING:
@@ -4791,7 +4788,7 @@ xfs_bmap_del_extent_cow(
 		/*
 		 * Matches the whole extent.  Delete the entry.
 		 */
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		break;
 	case BMAP_LEFT_FILLING:
@@ -4931,7 +4928,7 @@ xfs_bmap_del_extent_real(
 		/*
 		 * Matches the whole extent.  Delete the entry.
 		 */
-		xfs_iext_remove(ip, ext, 1, state);
+		xfs_iext_remove(ip, ext, state);
 		xfs_iext_prev(ifp, ext);
 		XFS_IFORK_NEXT_SET(ip, whichfork,
 			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
@@ -5557,7 +5554,7 @@ xfs_bmse_merge(
 		return error;
 
 done:
-	xfs_iext_remove(ip, ext, 1, 0);
+	xfs_iext_remove(ip, ext, 0);
 	xfs_iext_prev(XFS_IFORK_PTR(ip, whichfork), ext);
 	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), ext,
 			&new);
diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
index c109526e52c5..c18d344d46cd 100644
--- a/fs/xfs/libxfs/xfs_iext_tree.c
+++ b/fs/xfs/libxfs/xfs_iext_tree.c
@@ -162,12 +162,10 @@ static inline struct xfs_iext_rec *cur_rec(struct xfs_iext_cursor *cur)
 static noinline bool xfs_iext_valid(struct xfs_ifork *ifp,
 		struct xfs_iext_cursor *cur)
 {
-	if (cur->pos < 0)
-		return false;
-	if (cur->pos >= xfs_iext_max_recs(ifp))
-		return false;
 	if (!cur->leaf)
 		return false;
+	if (cur->pos < 0 || cur->pos >= xfs_iext_max_recs(ifp))
+		return false;
 	if (xfs_iext_rec_is_empty(cur_rec(cur)))
 		return false;
 	return true;
@@ -256,8 +254,8 @@ xfs_iext_next(
 	ASSERT(cur->pos < xfs_iext_max_recs(ifp));
 
 	cur->pos++;
-	if (!xfs_iext_valid(ifp, cur) && ifp->if_height > 1 &&
-	    cur->leaf && cur->leaf->next) {
+	if (ifp->if_height > 1 && !xfs_iext_valid(ifp, cur) &&
+	    cur->leaf->next) {
 		cur->leaf = cur->leaf->next;
 		cur->pos = 0;
 	}
@@ -826,15 +824,19 @@ xfs_iext_free_last_leaf(
 	kmem_free(ifp->if_u1.if_root);
 }
 
-static void
-__xfs_iext_remove(
-	struct xfs_ifork	*ifp,
-	struct xfs_iext_cursor	*cur)
+void
+xfs_iext_remove(
+	struct xfs_inode	*ip,
+	struct xfs_iext_cursor	*cur,
+	int			state)
 {
+	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
 	struct xfs_iext_leaf	*leaf = cur->leaf;
 	xfs_fileoff_t		offset = xfs_iext_leaf_key(leaf, 0);
 	int			i, nr_entries;
 
+	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
+
 	ASSERT(ifp->if_height > 0);
 	ASSERT(ifp->if_u1.if_root != NULL);
 	ASSERT(xfs_iext_valid(ifp, cur));
@@ -866,24 +868,6 @@ __xfs_iext_remove(
 		xfs_iext_free_last_leaf(ifp);
 }
 
-void
-xfs_iext_remove(
-	struct xfs_inode	*ip,
-	struct xfs_iext_cursor	*cur,
-	int			nr_extents,
-	int			state)
-{
-	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
-	int			i;
-
-	ASSERT(nr_extents > 0);
-
-	for (i = 0; i < nr_extents; i++) {
-		trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
-		__xfs_iext_remove(ifp, cur);
-	}
-}
-
 /*
  * Lookup the extent covering bno.
  *
diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
index cc73fea4e37e..58da2ec262e3 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.h
+++ b/fs/xfs/libxfs/xfs_inode_fork.h
@@ -116,7 +116,7 @@ xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
 void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
 			struct xfs_bmbt_irec *, int);
 void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
-			int, int);
+			int);
 void		xfs_iext_destroy(struct xfs_ifork *);
 
 bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
-- 
2.14.2


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-10-31 14:22 ` [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:15     ` Darrick J. Wong
  0 siblings, 1 reply; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> This prepares for getting rid of the current in-memory extent format.
> 

Couldn't we port this function over to use whatever the new in-memory
extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
rather than the associated bit in the on-disk format..?

Brian

> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
>  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
>  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
>  3 files changed, 9 insertions(+), 10 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index f45f05c45e15..b2b6832b9e6b 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1259,14 +1259,14 @@ xfs_iread_extents(
>  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
>  		for (j = 0; j < num_recs; j++, i++, frp++) {
>  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> -			trp->l0 = be64_to_cpu(frp->l0);
> -			trp->l1 = be64_to_cpu(frp->l1);
> -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
>  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				error = -EFSCORRUPTED;
>  				goto out_brelse;
>  			}
> +			trp->l0 = be64_to_cpu(frp->l0);
> +			trp->l1 = be64_to_cpu(frp->l1);
>  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
>  		}
>  		xfs_trans_brelse(tp, bp);
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> index 6f891eeb88f6..2fbfe2a24b15 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
>   * Check that the extent does not contain an invalid unwritten extent flag.
>   */
>  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> -		struct xfs_bmbt_rec_host *ep)
> +		struct xfs_bmbt_rec *ep)
>  {
> -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
>  		return true;
>  	if (whichfork == XFS_DATA_FORK &&
>  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index bb63f38b97cc..abe601b48c9c 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -371,13 +371,13 @@ xfs_iformat_extents(
>  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
>  		for (i = 0; i < nex; i++, dp++) {
>  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> -			ep->l0 = get_unaligned_be64(&dp->l0);
> -			ep->l1 = get_unaligned_be64(&dp->l1);
> -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
>  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				return -EFSCORRUPTED;
>  			}
> +			ep->l0 = get_unaligned_be64(&dp->l0);
> +			ep->l1 = get_unaligned_be64(&dp->l1);
>  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
>  		}
>  	}
> @@ -764,8 +764,6 @@ xfs_iextents_copy(
>  	for (i = 0; i < nrecs; i++) {
>  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
>  
> -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> -
>  		start_block = xfs_bmbt_get_startblock(ep);
>  		if (isnullstartblock(start_block)) {
>  			/*
> @@ -779,6 +777,7 @@ xfs_iextents_copy(
>  		/* Translate to on disk format */
>  		put_unaligned_be64(ep->l0, &dp->l0);
>  		put_unaligned_be64(ep->l1, &dp->l1);
> +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
>  
>  		dp++;
>  		copied++;
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real
  2017-10-31 14:22 ` [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:34   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:14PM +0200, Christoph Hellwig wrote:
> Two cases in xfs_bmap_add_extent_delay_real currently insert a new
> extent before updating the existing one that is being split.  While
> this works fine with a simple extent list, a more complex tree can't
> easily cope with overlapping extent.  Reshuffle the code a bit to update
> the slot of the existing delalloc extent to the new real extent before
> inserting the shortened delalloc extent before or after it.  This
> avoids the overlapping extents while still allowing to update the
> br_startblock field of the delalloc extent with the updated indirect
> block reservation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index b2b6832b9e6b..0eda6892b9d0 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1790,7 +1790,7 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in the first part of a previous delayed allocation.
>  		 * The left neighbor is not contiguous.
>  		 */
> -		xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
>  		(*nextents)++;
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
> @@ -1823,7 +1823,7 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_startoff = new_endoff;
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
> -		xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &PREV);
> +		xfs_iext_insert(bma->ip, bma->idx + 1, 1, &PREV, state);
>  		break;
>  
>  	case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
> @@ -1866,7 +1866,7 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in the last part of a previous delayed allocation.
>  		 * The right neighbor is not contiguous.
>  		 */
> -		xfs_iext_insert(bma->ip, bma->idx + 1, 1, new, state);
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
>  		(*nextents)++;
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
> @@ -1898,7 +1898,7 @@ xfs_bmap_add_extent_delay_real(
>  
>  		PREV.br_startblock = nullstartblock(da_new);
>  		PREV.br_blockcount = temp;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		xfs_iext_insert(bma->ip, bma->idx, 1, &PREV, state);
>  
>  		bma->idx++;
>  		break;
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 03/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_delay_real
  2017-10-31 14:22 ` [PATCH 03/18] xfs: treat idx as a cursor " Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:35   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:15PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c | 27 ++++++++++++++++-----------
>  1 file changed, 16 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 0eda6892b9d0..83f5a503dce1 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
...
> @@ -1762,12 +1766,14 @@ xfs_bmap_add_extent_delay_real(
>  				startblockval(PREV.br_startblock));
>  
>  		LEFT.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx - 1, &LEFT);
>  
>  		PREV.br_blockcount = temp = PREV.br_blockcount - new->br_blockcount;

Unrelated to this patch, but this ^ line looks funny. We've already
assigned temp to this value just a few lines above. That might be worth
cleaning up if this requires a v2. Otherwise this looks fine:

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock = nullstartblock(da_new);
> +
>  		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		bma->idx--;
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1782,7 +1788,6 @@ xfs_bmap_add_extent_delay_real(
>  				goto done;
>  		}
>  
> -		bma->idx--;
>  		break;
>  
>  	case BMAP_LEFT_FILLING:
> @@ -1835,7 +1840,6 @@ xfs_bmap_add_extent_delay_real(
>  		RIGHT.br_startoff = new->br_startoff;
>  		RIGHT.br_startblock = new->br_startblock;
>  		RIGHT.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &RIGHT);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1856,9 +1860,10 @@ xfs_bmap_add_extent_delay_real(
>  
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
>  
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
>  		bma->idx++;
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &RIGHT);
>  		break;
>  
>  	case BMAP_RIGHT_FILLING:
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay
  2017-10-31 14:22 ` [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:35   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:16PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 9 +++++----
>  1 file changed, 5 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 83f5a503dce1..6358b30b70f9 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2583,7 +2583,6 @@ xfs_bmap_add_extent_hole_delay(
>  		 * on the left and on the right.
>  		 * Merge all three into a single extent record.
>  		 */
> -		--*idx;
>  		temp = left.br_blockcount + new->br_blockcount +
>  			right.br_blockcount;
>  
> @@ -2594,9 +2593,10 @@ xfs_bmap_add_extent_hole_delay(
>  					 oldlen);
>  		left.br_startblock = nullstartblock(newlen);
>  		left.br_blockcount = temp;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &left);
>  		break;
>  
>  	case BMAP_LEFT_CONTIG:
> @@ -2605,7 +2605,6 @@ xfs_bmap_add_extent_hole_delay(
>  		 * on the left.
>  		 * Merge the new allocation with the left neighbor.
>  		 */
> -		--*idx;
>  		temp = left.br_blockcount + new->br_blockcount;
>  
>  		oldlen = startblockval(left.br_startblock) +
> @@ -2614,6 +2613,8 @@ xfs_bmap_add_extent_hole_delay(
>  					 oldlen);
>  		left.br_blockcount = temp;
>  		left.br_startblock = nullstartblock(newlen);
> +
> +		--*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &left);
>  		break;
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real
  2017-10-31 14:22 ` [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:35   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:17PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 6358b30b70f9..7bd27c08981f 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2741,11 +2741,11 @@ xfs_bmap_add_extent_hole_real(
>  		 * left and on the right.
>  		 * Merge all three into a single extent record.
>  		 */
> -		--*idx;
>  		left.br_blockcount += new->br_blockcount + right.br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &left);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
> @@ -2777,10 +2777,10 @@ xfs_bmap_add_extent_hole_real(
>  		 * on the left.
>  		 * Merge the new allocation with the left neighbor.
>  		 */
> -		--*idx;
>  		old = left;
> -
>  		left.br_blockcount += new->br_blockcount;
> +
> +		--*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &left);
>  
>  		if (cur == NULL) {
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real
  2017-10-31 14:22 ` [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:36   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:18PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 35 ++++++++++++++++++-----------------
>  1 file changed, 18 insertions(+), 17 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 7bd27c08981f..10878c495869 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2151,12 +2151,11 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * Setting all of a previous oldext extent to newext.
>  		 * The left and right neighbors are both contiguous with new.
>  		 */
> -		--*idx;
> -
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  
> -		xfs_iext_remove(ip, *idx + 1, 2, state);
> +		xfs_iext_remove(ip, *idx, 2, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 2);
>  		if (cur == NULL)
> @@ -2190,12 +2189,11 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * Setting all of a previous oldext extent to newext.
>  		 * The left neighbor is contiguous, the right is not.
>  		 */
> -		--*idx;
> -
>  		LEFT.br_blockcount += PREV.br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
>  		if (cur == NULL)
> @@ -2225,9 +2223,12 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		PREV.br_blockcount += RIGHT.br_blockcount;
>  		PREV.br_state = new->br_state;
> +
> +		++*idx;
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
>  		if (cur == NULL)
> @@ -2279,15 +2280,15 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * The left neighbor is contiguous.
>  		 */
>  		LEFT.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx - 1, &LEFT);
>  
>  		old = PREV;
>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock += new->br_blockcount;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  
>  		if (cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -2318,8 +2319,8 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock += new->br_blockcount;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		xfs_iext_insert(ip, *idx, 1, new, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> @@ -2348,13 +2349,13 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		old = PREV;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -
> -		++*idx;
>  
>  		RIGHT.br_startoff = new->br_startoff;
>  		RIGHT.br_startblock = new->br_startblock;
>  		RIGHT.br_blockcount += new->br_blockcount;
> +
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
> +		++*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &RIGHT);
>  
>  		if (cur == NULL)
> @@ -2384,8 +2385,8 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		old = PREV;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 1, new, state);
>  
> @@ -2420,7 +2421,6 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		old = PREV;
>  		PREV.br_blockcount = new->br_startoff - PREV.br_startoff;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
>  		r[0] = *new;
>  		r[1].br_startoff = new_endoff;
> @@ -2429,6 +2429,7 @@ xfs_bmap_add_extent_unwritten_real(
>  		r[1].br_startblock = new->br_startblock + new->br_blockcount;
>  		r[1].br_state = PREV.br_state;
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 2, &r[0], state);
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_*
  2017-10-31 14:22 ` [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_* Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:37   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:19PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 10878c495869..5ba0c0368629 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -4749,12 +4749,12 @@ xfs_bmap_del_extent_delay(
>  						       del->br_blockcount);
>  
>  		got->br_startblock = nullstartblock((int)got_indlen);
> -		xfs_iext_update_extent(ip, state, *idx, got);
>  
>  		new.br_startoff = del_endoff;
>  		new.br_state = got->br_state;
>  		new.br_startblock = nullstartblock((int)new_indlen);
>  
> +		xfs_iext_update_extent(ip, state, *idx, got);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 1, &new, state);
>  
> @@ -4831,13 +4831,13 @@ xfs_bmap_del_extent_cow(
>  		 * Deleting the middle of the extent.
>  		 */
>  		got->br_blockcount = del->br_startoff - got->br_startoff;
> -		xfs_iext_update_extent(ip, state, *idx, got);
>  
>  		new.br_startoff = del_endoff;
>  		new.br_blockcount = got_endoff - del_endoff;
>  		new.br_state = got->br_state;
>  		new.br_startblock = del->br_startblock + del->br_blockcount;
>  
> +		xfs_iext_update_extent(ip, state, *idx, got);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 1, &new, state);
>  		break;
> @@ -5053,8 +5053,8 @@ xfs_bmap_del_extent_real(
>  			flags |= xfs_ilog_fext(whichfork);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> -		xfs_iext_insert(ip, *idx + 1, 1, &new, state);
>  		++*idx;
> +		xfs_iext_insert(ip, *idx, 1, &new, state);
>  		break;
>  	}
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents
  2017-10-31 14:22 ` [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents Christoph Hellwig
@ 2017-10-31 17:53   ` Brian Foster
  2017-10-31 21:37   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-10-31 17:53 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:20PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 17 ++++++-----------
>  1 file changed, 6 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 5ba0c0368629..14428d72cf33 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -5516,7 +5516,7 @@ xfs_bmse_merge(
>  	struct xfs_inode		*ip,
>  	int				whichfork,
>  	xfs_fileoff_t			shift,		/* shift fsb */
> -	int				current_ext,	/* idx of gotp */
> +	int				*current_ext,	/* idx of gotp */
>  	struct xfs_bmbt_irec		*got,		/* extent to shift */
>  	struct xfs_bmbt_irec		*left,		/* preceding extent */
>  	struct xfs_btree_cur		*cur,
> @@ -5571,9 +5571,10 @@ xfs_bmse_merge(
>  		return error;
>  
>  done:
> +	xfs_iext_remove(ip, *current_ext, 1, 0);
> +	--*current_ext;
>  	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
> -			current_ext - 1, &new);
> -	xfs_iext_remove(ip, current_ext, 1, 0);
> +			*current_ext, &new);
>  
>  	/* update reverse mapping. rmap functions merge the rmaps for us */
>  	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, got);
> @@ -5687,16 +5688,10 @@ xfs_bmap_collapse_extents(
>  
>  		if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) {
>  			error = xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
> -					current_ext, &got, &prev, cur,
> +					&current_ext, &got, &prev, cur,
>  					&logflags, dfops);
>  			if (error)
>  				goto del_cursor;
> -
> -			/* update got after merge */
> -			if (!xfs_iext_get_extent(ifp, current_ext, &got)) {
> -				*done = true;
> -				goto del_cursor;
> -			}
>  			goto done;
>  		}
>  	} else {
> @@ -5711,12 +5706,12 @@ xfs_bmap_collapse_extents(
>  	if (error)
>  		goto del_cursor;
>  
> +done:
>  	if (!xfs_iext_get_extent(ifp, ++current_ext, &got)) {
>  		 *done = true;
>  		 goto del_cursor;
>  	}
>  
> -done:
>  	*next_fsb = got.br_startoff;
>  del_cursor:
>  	if (cur)
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:15     ` Darrick J. Wong
  2017-11-01 13:58       ` Brian Foster
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:15 UTC (permalink / raw)
  To: Brian Foster; +Cc: Christoph Hellwig, linux-xfs

On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > This prepares for getting rid of the current in-memory extent format.
> > 
> 
> Couldn't we port this function over to use whatever the new in-memory
> extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> rather than the associated bit in the on-disk format..?

It's certainly possible, but in general verifiers are supposed to check
on-disk metadata before they end up in-core, and this would seem to get
us closer to that, right?

--D

> Brian
> 
> > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > ---
> >  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
> >  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
> >  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
> >  3 files changed, 9 insertions(+), 10 deletions(-)
> > 
> > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > index f45f05c45e15..b2b6832b9e6b 100644
> > --- a/fs/xfs/libxfs/xfs_bmap.c
> > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > @@ -1259,14 +1259,14 @@ xfs_iread_extents(
> >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> >  		for (j = 0; j < num_recs; j++, i++, frp++) {
> >  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> > -			trp->l0 = be64_to_cpu(frp->l0);
> > -			trp->l1 = be64_to_cpu(frp->l1);
> > -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> > +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
> >  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> >  						 XFS_ERRLEVEL_LOW, mp);
> >  				error = -EFSCORRUPTED;
> >  				goto out_brelse;
> >  			}
> > +			trp->l0 = be64_to_cpu(frp->l0);
> > +			trp->l1 = be64_to_cpu(frp->l1);
> >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> >  		}
> >  		xfs_trans_brelse(tp, bp);
> > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > index 6f891eeb88f6..2fbfe2a24b15 100644
> > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> >   * Check that the extent does not contain an invalid unwritten extent flag.
> >   */
> >  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > -		struct xfs_bmbt_rec_host *ep)
> > +		struct xfs_bmbt_rec *ep)
> >  {
> > -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> >  		return true;
> >  	if (whichfork == XFS_DATA_FORK &&
> >  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > index bb63f38b97cc..abe601b48c9c 100644
> > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > @@ -371,13 +371,13 @@ xfs_iformat_extents(
> >  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> >  		for (i = 0; i < nex; i++, dp++) {
> >  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > -			ep->l0 = get_unaligned_be64(&dp->l0);
> > -			ep->l1 = get_unaligned_be64(&dp->l1);
> > -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> > +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
> >  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> >  						 XFS_ERRLEVEL_LOW, mp);
> >  				return -EFSCORRUPTED;
> >  			}
> > +			ep->l0 = get_unaligned_be64(&dp->l0);
> > +			ep->l1 = get_unaligned_be64(&dp->l1);
> >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> >  		}
> >  	}
> > @@ -764,8 +764,6 @@ xfs_iextents_copy(
> >  	for (i = 0; i < nrecs; i++) {
> >  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> >  
> > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> > -
> >  		start_block = xfs_bmbt_get_startblock(ep);
> >  		if (isnullstartblock(start_block)) {
> >  			/*
> > @@ -779,6 +777,7 @@ xfs_iextents_copy(
> >  		/* Translate to on disk format */
> >  		put_unaligned_be64(ep->l0, &dp->l0);
> >  		put_unaligned_be64(ep->l1, &dp->l1);
> > +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> >  
> >  		dp++;
> >  		copied++;
> > -- 
> > 2.14.2
> > 
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all
  2017-10-31 14:22 ` [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all Christoph Hellwig
@ 2017-10-31 21:34   ` Darrick J. Wong
  2017-11-02 13:54   ` Brian Foster
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:34 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:21PM +0200, Christoph Hellwig wrote:
> To make life a little simpler make xfs_bmbt_set_all unaligned access
> aware so that we can use it directly on the destination buffer.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap_btree.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
> index 086e6fc8e4fc..89260972a0f6 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.c
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.c
> @@ -199,14 +199,14 @@ xfs_bmbt_disk_set_all(
>  	ASSERT(!(s->br_blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)));
>  	ASSERT(!(s->br_startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)));
>  
> -	r->l0 = cpu_to_be64(
> +	put_unaligned_be64(
>  		((xfs_bmbt_rec_base_t)extent_flag << 63) |
>  		 ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
> -		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43));
> -	r->l1 = cpu_to_be64(
> +		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43), &r->l0);
> +	put_unaligned_be64(
>  		((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
>  		 ((xfs_bmbt_rec_base_t)s->br_blockcount &
> -		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21)));
> +		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21)), &r->l1);
>  }
>  
>  /*
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real
  2017-10-31 14:22 ` [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:34   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:34 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:14PM +0200, Christoph Hellwig wrote:
> Two cases in xfs_bmap_add_extent_delay_real currently insert a new
> extent before updating the existing one that is being split.  While
> this works fine with a simple extent list, a more complex tree can't
> easily cope with overlapping extent.  Reshuffle the code a bit to update
> the slot of the existing delalloc extent to the new real extent before
> inserting the shortened delalloc extent before or after it.  This
> avoids the overlapping extents while still allowing to update the
> br_startblock field of the delalloc extent with the updated indirect
> block reservation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index b2b6832b9e6b..0eda6892b9d0 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1790,7 +1790,7 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in the first part of a previous delayed allocation.
>  		 * The left neighbor is not contiguous.
>  		 */
> -		xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
>  		(*nextents)++;
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
> @@ -1823,7 +1823,7 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_startoff = new_endoff;
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
> -		xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &PREV);
> +		xfs_iext_insert(bma->ip, bma->idx + 1, 1, &PREV, state);
>  		break;
>  
>  	case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
> @@ -1866,7 +1866,7 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in the last part of a previous delayed allocation.
>  		 * The right neighbor is not contiguous.
>  		 */
> -		xfs_iext_insert(bma->ip, bma->idx + 1, 1, new, state);
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
>  		(*nextents)++;
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
> @@ -1898,7 +1898,7 @@ xfs_bmap_add_extent_delay_real(
>  
>  		PREV.br_startblock = nullstartblock(da_new);
>  		PREV.br_blockcount = temp;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		xfs_iext_insert(bma->ip, bma->idx, 1, &PREV, state);
>  
>  		bma->idx++;
>  		break;
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 03/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_delay_real
  2017-10-31 14:22 ` [PATCH 03/18] xfs: treat idx as a cursor " Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:35   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:35 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:15PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 27 ++++++++++++++++-----------
>  1 file changed, 16 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 0eda6892b9d0..83f5a503dce1 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1647,12 +1647,13 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in all of a previously delayed allocation extent.
>  		 * The left and right neighbors are both contiguous with new.
>  		 */
> -		bma->idx--;
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
>  
> -		xfs_iext_remove(bma->ip, bma->idx + 1, 2, state);
> +		xfs_iext_remove(bma->ip, bma->idx, 2, state);
> +		bma->idx--;
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
>  		(*nextents)--;
> +
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
>  		else {
> @@ -1680,13 +1681,13 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in all of a previously delayed allocation extent.
>  		 * The left neighbor is contiguous, the right is not.
>  		 */
> -		bma->idx--;
> -
>  		old = LEFT;
>  		LEFT.br_blockcount += PREV.br_blockcount;
> +
> +		xfs_iext_remove(bma->ip, bma->idx, 1, state);
> +		bma->idx--;
>  		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
>  
> -		xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
>  		else {
> @@ -1708,9 +1709,12 @@ xfs_bmap_add_extent_delay_real(
>  		 */
>  		PREV.br_startblock = new->br_startblock;
>  		PREV.br_blockcount += RIGHT.br_blockcount;
> +
> +		bma->idx++;
> +		xfs_iext_remove(bma->ip, bma->idx, 1, state);
> +		bma->idx--;
>  		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
>  
> -		xfs_iext_remove(bma->ip, bma->idx + 1, 1, state);
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
>  		else {
> @@ -1762,12 +1766,14 @@ xfs_bmap_add_extent_delay_real(
>  				startblockval(PREV.br_startblock));
>  
>  		LEFT.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx - 1, &LEFT);
>  
>  		PREV.br_blockcount = temp = PREV.br_blockcount - new->br_blockcount;
>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock = nullstartblock(da_new);
> +
>  		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		bma->idx--;
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1782,7 +1788,6 @@ xfs_bmap_add_extent_delay_real(
>  				goto done;
>  		}
>  
> -		bma->idx--;
>  		break;
>  
>  	case BMAP_LEFT_FILLING:
> @@ -1835,7 +1840,6 @@ xfs_bmap_add_extent_delay_real(
>  		RIGHT.br_startoff = new->br_startoff;
>  		RIGHT.br_startblock = new->br_startblock;
>  		RIGHT.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx + 1, &RIGHT);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1856,9 +1860,10 @@ xfs_bmap_add_extent_delay_real(
>  
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
>  
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
>  		bma->idx++;
> +		xfs_iext_update_extent(bma->ip, state, bma->idx, &RIGHT);
>  		break;
>  
>  	case BMAP_RIGHT_FILLING:
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay
  2017-10-31 14:22 ` [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:35   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:35 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:16PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 9 +++++----
>  1 file changed, 5 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 83f5a503dce1..6358b30b70f9 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2583,7 +2583,6 @@ xfs_bmap_add_extent_hole_delay(
>  		 * on the left and on the right.
>  		 * Merge all three into a single extent record.
>  		 */
> -		--*idx;
>  		temp = left.br_blockcount + new->br_blockcount +
>  			right.br_blockcount;
>  
> @@ -2594,9 +2593,10 @@ xfs_bmap_add_extent_hole_delay(
>  					 oldlen);
>  		left.br_startblock = nullstartblock(newlen);
>  		left.br_blockcount = temp;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &left);
>  		break;
>  
>  	case BMAP_LEFT_CONTIG:
> @@ -2605,7 +2605,6 @@ xfs_bmap_add_extent_hole_delay(
>  		 * on the left.
>  		 * Merge the new allocation with the left neighbor.
>  		 */
> -		--*idx;
>  		temp = left.br_blockcount + new->br_blockcount;
>  
>  		oldlen = startblockval(left.br_startblock) +
> @@ -2614,6 +2613,8 @@ xfs_bmap_add_extent_hole_delay(
>  					 oldlen);
>  		left.br_blockcount = temp;
>  		left.br_startblock = nullstartblock(newlen);
> +
> +		--*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &left);
>  		break;
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real
  2017-10-31 14:22 ` [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:35   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:35 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:17PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 10 +++++-----
>  1 file changed, 5 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 6358b30b70f9..7bd27c08981f 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2741,11 +2741,11 @@ xfs_bmap_add_extent_hole_real(
>  		 * left and on the right.
>  		 * Merge all three into a single extent record.
>  		 */
> -		--*idx;
>  		left.br_blockcount += new->br_blockcount + right.br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &left);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
> @@ -2777,10 +2777,10 @@ xfs_bmap_add_extent_hole_real(
>  		 * on the left.
>  		 * Merge the new allocation with the left neighbor.
>  		 */
> -		--*idx;
>  		old = left;
> -
>  		left.br_blockcount += new->br_blockcount;
> +
> +		--*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &left);
>  
>  		if (cur == NULL) {
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real
  2017-10-31 14:22 ` [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:36   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:36 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:18PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 35 ++++++++++++++++++-----------------
>  1 file changed, 18 insertions(+), 17 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 7bd27c08981f..10878c495869 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -2151,12 +2151,11 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * Setting all of a previous oldext extent to newext.
>  		 * The left and right neighbors are both contiguous with new.
>  		 */
> -		--*idx;
> -
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  
> -		xfs_iext_remove(ip, *idx + 1, 2, state);
> +		xfs_iext_remove(ip, *idx, 2, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 2);
>  		if (cur == NULL)
> @@ -2190,12 +2189,11 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * Setting all of a previous oldext extent to newext.
>  		 * The left neighbor is contiguous, the right is not.
>  		 */
> -		--*idx;
> -
>  		LEFT.br_blockcount += PREV.br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
>  		if (cur == NULL)
> @@ -2225,9 +2223,12 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		PREV.br_blockcount += RIGHT.br_blockcount;
>  		PREV.br_state = new->br_state;
> +
> +		++*idx;
> +		xfs_iext_remove(ip, *idx, 1, state);
> +		--*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> -		xfs_iext_remove(ip, *idx + 1, 1, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
>  		if (cur == NULL)
> @@ -2279,15 +2280,15 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * The left neighbor is contiguous.
>  		 */
>  		LEFT.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx - 1, &LEFT);
>  
>  		old = PREV;
>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock += new->br_blockcount;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		--*idx;
> +		xfs_iext_update_extent(ip, state, *idx, &LEFT);
>  
>  		if (cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -2318,8 +2319,8 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock += new->br_blockcount;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		xfs_iext_insert(ip, *idx, 1, new, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> @@ -2348,13 +2349,13 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		old = PREV;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -
> -		++*idx;
>  
>  		RIGHT.br_startoff = new->br_startoff;
>  		RIGHT.br_startblock = new->br_startblock;
>  		RIGHT.br_blockcount += new->br_blockcount;
> +
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
> +		++*idx;
>  		xfs_iext_update_extent(ip, state, *idx, &RIGHT);
>  
>  		if (cur == NULL)
> @@ -2384,8 +2385,8 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		old = PREV;
>  		PREV.br_blockcount -= new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 1, new, state);
>  
> @@ -2420,7 +2421,6 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		old = PREV;
>  		PREV.br_blockcount = new->br_startoff - PREV.br_startoff;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  
>  		r[0] = *new;
>  		r[1].br_startoff = new_endoff;
> @@ -2429,6 +2429,7 @@ xfs_bmap_add_extent_unwritten_real(
>  		r[1].br_startblock = new->br_startblock + new->br_blockcount;
>  		r[1].br_state = PREV.br_state;
>  
> +		xfs_iext_update_extent(ip, state, *idx, &PREV);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 2, &r[0], state);
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_*
  2017-10-31 14:22 ` [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_* Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:37   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:37 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:19PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 10878c495869..5ba0c0368629 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -4749,12 +4749,12 @@ xfs_bmap_del_extent_delay(
>  						       del->br_blockcount);
>  
>  		got->br_startblock = nullstartblock((int)got_indlen);
> -		xfs_iext_update_extent(ip, state, *idx, got);
>  
>  		new.br_startoff = del_endoff;
>  		new.br_state = got->br_state;
>  		new.br_startblock = nullstartblock((int)new_indlen);
>  
> +		xfs_iext_update_extent(ip, state, *idx, got);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 1, &new, state);
>  
> @@ -4831,13 +4831,13 @@ xfs_bmap_del_extent_cow(
>  		 * Deleting the middle of the extent.
>  		 */
>  		got->br_blockcount = del->br_startoff - got->br_startoff;
> -		xfs_iext_update_extent(ip, state, *idx, got);
>  
>  		new.br_startoff = del_endoff;
>  		new.br_blockcount = got_endoff - del_endoff;
>  		new.br_state = got->br_state;
>  		new.br_startblock = del->br_startblock + del->br_blockcount;
>  
> +		xfs_iext_update_extent(ip, state, *idx, got);
>  		++*idx;
>  		xfs_iext_insert(ip, *idx, 1, &new, state);
>  		break;
> @@ -5053,8 +5053,8 @@ xfs_bmap_del_extent_real(
>  			flags |= xfs_ilog_fext(whichfork);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> -		xfs_iext_insert(ip, *idx + 1, 1, &new, state);
>  		++*idx;
> +		xfs_iext_insert(ip, *idx, 1, &new, state);
>  		break;
>  	}
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents
  2017-10-31 14:22 ` [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents Christoph Hellwig
  2017-10-31 17:53   ` Brian Foster
@ 2017-10-31 21:37   ` Darrick J. Wong
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:37 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:20PM +0200, Christoph Hellwig wrote:
> Stop poking before and after the index and just increment or decrement
> it while doing our operations on it to prepare for a new extent list
> implementation.
> 

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c | 17 ++++++-----------
>  1 file changed, 6 insertions(+), 11 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 5ba0c0368629..14428d72cf33 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -5516,7 +5516,7 @@ xfs_bmse_merge(
>  	struct xfs_inode		*ip,
>  	int				whichfork,
>  	xfs_fileoff_t			shift,		/* shift fsb */
> -	int				current_ext,	/* idx of gotp */
> +	int				*current_ext,	/* idx of gotp */
>  	struct xfs_bmbt_irec		*got,		/* extent to shift */
>  	struct xfs_bmbt_irec		*left,		/* preceding extent */
>  	struct xfs_btree_cur		*cur,
> @@ -5571,9 +5571,10 @@ xfs_bmse_merge(
>  		return error;
>  
>  done:
> +	xfs_iext_remove(ip, *current_ext, 1, 0);
> +	--*current_ext;
>  	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
> -			current_ext - 1, &new);
> -	xfs_iext_remove(ip, current_ext, 1, 0);
> +			*current_ext, &new);
>  
>  	/* update reverse mapping. rmap functions merge the rmaps for us */
>  	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, got);
> @@ -5687,16 +5688,10 @@ xfs_bmap_collapse_extents(
>  
>  		if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) {
>  			error = xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
> -					current_ext, &got, &prev, cur,
> +					&current_ext, &got, &prev, cur,
>  					&logflags, dfops);
>  			if (error)
>  				goto del_cursor;
> -
> -			/* update got after merge */
> -			if (!xfs_iext_get_extent(ifp, current_ext, &got)) {
> -				*done = true;
> -				goto del_cursor;
> -			}
>  			goto done;
>  		}
>  	} else {
> @@ -5711,12 +5706,12 @@ xfs_bmap_collapse_extents(
>  	if (error)
>  		goto del_cursor;
>  
> +done:
>  	if (!xfs_iext_get_extent(ifp, ++current_ext, &got)) {
>  		 *done = true;
>  		 goto del_cursor;
>  	}
>  
> -done:
>  	*next_fsb = got.br_startoff;
>  del_cursor:
>  	if (cur)
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy
  2017-10-31 14:22 ` [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy Christoph Hellwig
@ 2017-10-31 21:41   ` Darrick J. Wong
  2017-11-02 13:54   ` Brian Foster
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:41 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:22PM +0200, Christoph Hellwig wrote:
> This actually makes the function very slightly less efficient for now as we
> detour through the expanded irect format between the in-core extent format
> and the on-disk one instead of just endian swapping them.  But with the
> incore extent btree the in-core one will use a different format and the
> representation will be entirely hidden.  It also happens to make the
> function a whole more readable.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks good enough to test,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_inode_fork.c | 53 +++++++++++-------------------------------
>  1 file changed, 13 insertions(+), 40 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index abe601b48c9c..7dd77b497fc2 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -725,9 +725,6 @@ xfs_iext_count(struct xfs_ifork *ifp)
>  /*
>   * Convert in-core extents to on-disk form
>   *
> - * For either the data or attr fork in extent format, we need to endian convert
> - * the in-core extent as we place them into the on-disk inode.
> - *
>   * In the case of the data fork, the in-core and on-disk fork sizes can be
>   * different due to delayed allocation extents. We only copy on-disk extents
>   * here, so callers must always use the physical fork size to determine the
> @@ -736,55 +733,31 @@ xfs_iext_count(struct xfs_ifork *ifp)
>   */
>  int
>  xfs_iextents_copy(
> -	xfs_inode_t		*ip,
> -	xfs_bmbt_rec_t		*dp,
> +	struct xfs_inode	*ip,
> +	struct xfs_bmbt_rec	*dp,
>  	int			whichfork)
>  {
>  	int			state = xfs_bmap_fork_to_state(whichfork);
> -	int			copied;
> -	int			i;
> -	xfs_ifork_t		*ifp;
> -	int			nrecs;
> -	xfs_fsblock_t		start_block;
> +	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
> +	struct xfs_bmbt_irec	rec;
> +	int			copied = 0, i = 0;
>  
> -	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
> +	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
>  	ASSERT(ifp->if_bytes > 0);
>  
> -	nrecs = xfs_iext_count(ifp);
> -	ASSERT(nrecs > 0);
> -
> -	/*
> -	 * There are some delayed allocation extents in the
> -	 * inode, so copy the extents one at a time and skip
> -	 * the delayed ones.  There must be at least one
> -	 * non-delayed extent.
> -	 */
> -	copied = 0;
> -	for (i = 0; i < nrecs; i++) {
> -		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> -
> -		start_block = xfs_bmbt_get_startblock(ep);
> -		if (isnullstartblock(start_block)) {
> -			/*
> -			 * It's a delayed allocation extent, so skip it.
> -			 */
> +	while (xfs_iext_get_extent(ifp, i++, &rec)) {
> +		if (isnullstartblock(rec.br_startblock))
>  			continue;
> -		}
> -
> +		xfs_bmbt_disk_set_all(dp, &rec);
>  		trace_xfs_write_extent(ip, i, state, _RET_IP_);
> -
> -		/* Translate to on disk format */
> -		put_unaligned_be64(ep->l0, &dp->l0);
> -		put_unaligned_be64(ep->l1, &dp->l1);
>  		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> -
> +		copied += sizeof(struct xfs_bmbt_rec);
>  		dp++;
> -		copied++;
>  	}
> -	ASSERT(copied != 0);
>  
> -	return (copied * (uint)sizeof(xfs_bmbt_rec_t));
> +	ASSERT(copied > 0);
> +	ASSERT(copied <= ifp->if_bytes);
> +	return copied;
>  }
>  
>  /*
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree
  2017-10-31 14:22 ` [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree Christoph Hellwig
@ 2017-10-31 21:41   ` Darrick J. Wong
  2017-11-02 13:54   ` Brian Foster
  1 sibling, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 21:41 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:23PM +0200, Christoph Hellwig wrote:
> This actually makes the function very slightly less efficient for now as we
> detour through the expanded irect format between the in-core extent format
> and the on-disk one instead of just endian swapping them.  But with the
> incore extent btree the in-core one will use a different format and the
> representation will be entirely hidden.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks good enough to test,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 20 ++++++++------------
>  1 file changed, 8 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 14428d72cf33..56482bf6280d 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -666,14 +666,13 @@ xfs_bmap_extents_to_btree(
>  	xfs_bmbt_rec_t		*arp;		/* child record pointer */
>  	struct xfs_btree_block	*block;		/* btree root block */
>  	xfs_btree_cur_t		*cur;		/* bmap btree cursor */
> -	xfs_bmbt_rec_host_t	*ep;		/* extent record pointer */
>  	int			error;		/* error return value */
> -	xfs_extnum_t		i, cnt;		/* extent record index */
>  	xfs_ifork_t		*ifp;		/* inode fork pointer */
>  	xfs_bmbt_key_t		*kp;		/* root block key pointer */
>  	xfs_mount_t		*mp;		/* mount structure */
> -	xfs_extnum_t		nextents;	/* number of file extents */
>  	xfs_bmbt_ptr_t		*pp;		/* root block address pointer */
> +	struct xfs_bmbt_irec	rec;
> +	xfs_extnum_t		i = 0, cnt = 0;
>  
>  	mp = ip->i_mount;
>  	ASSERT(whichfork != XFS_COW_FORK);
> @@ -752,15 +751,12 @@ xfs_bmap_extents_to_btree(
>  				XFS_BTNUM_BMAP, 0, 0, ip->i_ino,
>  				XFS_BTREE_LONG_PTRS);
>  
> -	arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
> -	nextents =  xfs_iext_count(ifp);
> -	for (cnt = i = 0; i < nextents; i++) {
> -		ep = xfs_iext_get_ext(ifp, i);
> -		if (!isnullstartblock(xfs_bmbt_get_startblock(ep))) {
> -			arp->l0 = cpu_to_be64(ep->l0);
> -			arp->l1 = cpu_to_be64(ep->l1);
> -			arp++; cnt++;
> -		}
> +	while (xfs_iext_get_extent(ifp, i++, &rec)) {
> +		if (isnullstartblock(rec.br_startblock))
> +			continue;
> +		arp = XFS_BMBT_REC_ADDR(mp, ablock, 1 + cnt);
> +		xfs_bmbt_disk_set_all(arp, &rec);
> +		cnt++;
>  	}
>  	ASSERT(cnt == XFS_IFORK_NEXTENTS(ip, whichfork));
>  	xfs_btree_set_numrecs(ablock, cnt);
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-10-31 14:22 ` [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction Christoph Hellwig
@ 2017-10-31 22:02   ` Darrick J. Wong
  2017-11-02 18:49     ` Christoph Hellwig
  2017-11-02 17:14   ` Brian Foster
  1 sibling, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 22:02 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:24PM +0200, Christoph Hellwig wrote:
> Add a new xfs_iext_cursor structure to hide the direct extent map
> index manipulations. In addition to the existing lookup/get/insert/
> remove and update routines new primitives to get the first and last
> extent cursor, as well as moving up and down by one extent are
> provided.  Also new are convenience to increment/decrement the
> cursor and retreive the new extent, as well as to peek into the
> previous/next extent without updating the cursor and last but not
> least a macro to iterate over all extents in a fork.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c       | 432 ++++++++++++++++++++---------------------
>  fs/xfs/libxfs/xfs_bmap.h       |  12 +-
>  fs/xfs/libxfs/xfs_inode_fork.c |  75 +++----
>  fs/xfs/libxfs/xfs_inode_fork.h |  87 ++++++++-
>  fs/xfs/libxfs/xfs_types.h      |   3 +
>  fs/xfs/scrub/bmap.c            |   6 +-
>  fs/xfs/scrub/dir.c             |  14 +-
>  fs/xfs/xfs_bmap_util.c         |  12 +-
>  fs/xfs/xfs_dir2_readdir.c      |   8 +-
>  fs/xfs/xfs_dquot.c             |   4 +-
>  fs/xfs/xfs_iomap.c             |  13 +-
>  fs/xfs/xfs_reflink.c           |  56 +++---
>  fs/xfs/xfs_trace.h             |  12 +-
>  13 files changed, 401 insertions(+), 333 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 56482bf6280d..453dc1ae76ab 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -671,8 +671,9 @@ xfs_bmap_extents_to_btree(
>  	xfs_bmbt_key_t		*kp;		/* root block key pointer */
>  	xfs_mount_t		*mp;		/* mount structure */
>  	xfs_bmbt_ptr_t		*pp;		/* root block address pointer */
> +	struct xfs_iext_cursor	ext;
>  	struct xfs_bmbt_irec	rec;
> -	xfs_extnum_t		i = 0, cnt = 0;
> +	xfs_extnum_t		cnt = 0;
>  
>  	mp = ip->i_mount;
>  	ASSERT(whichfork != XFS_COW_FORK);
> @@ -751,7 +752,7 @@ xfs_bmap_extents_to_btree(
>  				XFS_BTNUM_BMAP, 0, 0, ip->i_ino,
>  				XFS_BTREE_LONG_PTRS);
>  
> -	while (xfs_iext_get_extent(ifp, i++, &rec)) {
> +	for_each_iext(ifp, &ext, &rec) {

I'm torn here, because everything else has "xfs" in the name somewhere.
Consistently done we end up with this uglyness:

xfs_iext_for_each_extent(ifp, &ext, &rec) {

But ... it's syntactic sugar applied to a for loop.  I like sugar.

for_each_xfs_iext() { ... } ?

Not sure, I rarely add such things to header files.  The English works
better in this second version, so I think I like this (for_each_iext).

Anyone have strong opinions about this?

>  		if (isnullstartblock(rec.br_startblock))
>  			continue;
>  		arp = XFS_BMBT_REC_ADDR(mp, ablock, 1 + cnt);
> @@ -827,6 +828,7 @@ xfs_bmap_local_to_extents(
>  	xfs_alloc_arg_t	args;		/* allocation arguments */
>  	xfs_buf_t	*bp;		/* buffer for extent block */
>  	struct xfs_bmbt_irec rec;
> +	struct xfs_iext_cursor ext;

I was expecting an extent map cursor to have 'cur' in the name --
'ext' misleads me into thinking that 'ext' is an actual extent.

struct xfs_iext_cursor	*icur;

Also, xfs_iext_insert names its cursor parameter *cur, so why ext here?

For now I'm going to assume that xfs_iext_{first,last,next,prev} will
actually do something with the ifp parameter in the near future.

--D

>  
>  	/*
>  	 * We don't want to deal with the case of keeping inode data inline yet.
> @@ -893,7 +895,8 @@ xfs_bmap_local_to_extents(
>  	rec.br_startblock = args.fsbno;
>  	rec.br_blockcount = 1;
>  	rec.br_state = XFS_EXT_NORM;
> -	xfs_iext_insert(ip, 0, 1, &rec, 0);
> +	xfs_iext_first(ifp, &ext);
> +	xfs_iext_insert(ip, &ext, 1, &rec, 0);
>  
>  	XFS_IFORK_NEXT_SET(ip, whichfork, 1);
>  	ip->i_d.di_nblocks = 1;
> @@ -1173,6 +1176,7 @@ xfs_iread_extents(
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
>  	xfs_extnum_t		nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
>  	struct xfs_btree_block	*block = ifp->if_broot;
> +	struct xfs_iext_cursor	ext;
>  	xfs_fsblock_t		bno;
>  	struct xfs_buf		*bp;
>  	xfs_extnum_t		i, j;
> @@ -1222,6 +1226,7 @@ xfs_iread_extents(
>  	 * Here with bp and block set to the leftmost leaf node in the tree.
>  	 */
>  	i = 0;
> +	xfs_iext_first(ifp, &ext);
>  
>  	/*
>  	 * Loop over all leaf nodes.  Copy information to the extent records.
> @@ -1263,7 +1268,8 @@ xfs_iread_extents(
>  			}
>  			trp->l0 = be64_to_cpu(frp->l0);
>  			trp->l1 = be64_to_cpu(frp->l1);
> -			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> +			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
> +			xfs_iext_next(ifp, &ext);
>  		}
>  		xfs_trans_brelse(tp, bp);
>  		bno = nextbno;
> @@ -1311,7 +1317,7 @@ xfs_bmap_first_unused(
>  {
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
>  	struct xfs_bmbt_irec	got;
> -	xfs_extnum_t		idx = 0;
> +	struct xfs_iext_cursor	ext;
>  	xfs_fileoff_t		lastaddr = 0;
>  	xfs_fileoff_t		lowest, max;
>  	int			error;
> @@ -1332,7 +1338,7 @@ xfs_bmap_first_unused(
>  	}
>  
>  	lowest = max = *first_unused;
> -	while (xfs_iext_get_extent(ifp, idx++, &got)) {
> +	for_each_iext(ifp, &ext, &got) {
>  		/*
>  		 * See if the hole before this extent will work.
>  		 */
> @@ -1362,7 +1368,7 @@ xfs_bmap_last_before(
>  {
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
>  	struct xfs_bmbt_irec	got;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  	int			error;
>  
>  	switch (XFS_IFORK_FORMAT(ip, whichfork)) {
> @@ -1382,7 +1388,7 @@ xfs_bmap_last_before(
>  			return error;
>  	}
>  
> -	if (!xfs_iext_lookup_extent_before(ip, ifp, last_block, &idx, &got))
> +	if (!xfs_iext_lookup_extent_before(ip, ifp, last_block, &ext, &got))
>  		*last_block = 0;
>  	return 0;
>  }
> @@ -1396,8 +1402,8 @@ xfs_bmap_last_extent(
>  	int			*is_empty)
>  {
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
> +	struct xfs_iext_cursor	ext;
>  	int			error;
> -	int			nextents;
>  
>  	if (!(ifp->if_flags & XFS_IFEXTENTS)) {
>  		error = xfs_iread_extents(tp, ip, whichfork);
> @@ -1405,14 +1411,11 @@ xfs_bmap_last_extent(
>  			return error;
>  	}
>  
> -	nextents = xfs_iext_count(ifp);
> -	if (nextents == 0) {
> +	xfs_iext_last(ifp, &ext);
> +	if (!xfs_iext_get_extent(ifp, &ext, rec))
>  		*is_empty = 1;
> -		return 0;
> -	}
> -
> -	xfs_iext_get_extent(ifp, nextents - 1, rec);
> -	*is_empty = 0;
> +	else
> +		*is_empty = 0;
>  	return 0;
>  }
>  
> @@ -1500,6 +1503,7 @@ xfs_bmap_one_block(
>  	xfs_ifork_t	*ifp;		/* inode fork pointer */
>  	int		rval;		/* return value */
>  	xfs_bmbt_irec_t	s;		/* internal version of extent */
> +	struct xfs_iext_cursor ext;
>  
>  #ifndef DEBUG
>  	if (whichfork == XFS_DATA_FORK)
> @@ -1511,7 +1515,8 @@ xfs_bmap_one_block(
>  		return 0;
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
>  	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
> -	xfs_iext_get_extent(ifp, 0, &s);
> +	xfs_iext_first(ifp, &ext);
> +	xfs_iext_get_extent(ifp, &ext, &s);
>  	rval = s.br_startoff == 0 && s.br_blockcount == 1;
>  	if (rval && whichfork == XFS_DATA_FORK)
>  		ASSERT(XFS_ISIZE(ip) == ip->i_mount->m_sb.sb_blocksize);
> @@ -1553,8 +1558,6 @@ xfs_bmap_add_extent_delay_real(
>  	nextents = (whichfork == XFS_COW_FORK ? &bma->ip->i_cnextents :
>  						&bma->ip->i_d.di_nextents);
>  
> -	ASSERT(bma->idx >= 0);
> -	ASSERT(bma->idx <= xfs_iext_count(ifp));
>  	ASSERT(!isnullstartblock(new->br_startblock));
>  	ASSERT(!bma->cur ||
>  	       (bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
> @@ -1568,7 +1571,7 @@ xfs_bmap_add_extent_delay_real(
>  	/*
>  	 * Set up a bunch of variables to make the tests simpler.
>  	 */
> -	xfs_iext_get_extent(ifp, bma->idx, &PREV);
> +	xfs_iext_get_extent(ifp, &bma->ext, &PREV);
>  	new_endoff = new->br_startoff + new->br_blockcount;
>  	ASSERT(isnullstartblock(PREV.br_startblock));
>  	ASSERT(PREV.br_startoff <= new->br_startoff);
> @@ -1590,10 +1593,8 @@ xfs_bmap_add_extent_delay_real(
>  	 * Check and set flags if this segment has a left neighbor.
>  	 * Don't set contiguous if the combined extent would be too large.
>  	 */
> -	if (bma->idx > 0) {
> +	if (xfs_iext_peek_prev_extent(ifp, &bma->ext, &LEFT)) {
>  		state |= BMAP_LEFT_VALID;
> -		xfs_iext_get_extent(ifp, bma->idx - 1, &LEFT);
> -
>  		if (isnullstartblock(LEFT.br_startblock))
>  			state |= BMAP_LEFT_DELAY;
>  	}
> @@ -1610,10 +1611,8 @@ xfs_bmap_add_extent_delay_real(
>  	 * Don't set contiguous if the combined extent would be too large.
>  	 * Also check for all-three-contiguous being too large.
>  	 */
> -	if (bma->idx < xfs_iext_count(ifp) - 1) {
> +	if (xfs_iext_peek_next_extent(ifp, &bma->ext, &RIGHT)) {
>  		state |= BMAP_RIGHT_VALID;
> -		xfs_iext_get_extent(ifp, bma->idx + 1, &RIGHT);
> -
>  		if (isnullstartblock(RIGHT.br_startblock))
>  			state |= BMAP_RIGHT_DELAY;
>  	}
> @@ -1645,9 +1644,9 @@ xfs_bmap_add_extent_delay_real(
>  		 */
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
>  
> -		xfs_iext_remove(bma->ip, bma->idx, 2, state);
> -		bma->idx--;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
> +		xfs_iext_remove(bma->ip, &bma->ext, 2, state);
> +		xfs_iext_prev(ifp, &bma->ext);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
>  		(*nextents)--;
>  
>  		if (bma->cur == NULL)
> @@ -1680,9 +1679,9 @@ xfs_bmap_add_extent_delay_real(
>  		old = LEFT;
>  		LEFT.br_blockcount += PREV.br_blockcount;
>  
> -		xfs_iext_remove(bma->ip, bma->idx, 1, state);
> -		bma->idx--;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
> +		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
> +		xfs_iext_prev(ifp, &bma->ext);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1706,10 +1705,10 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_startblock = new->br_startblock;
>  		PREV.br_blockcount += RIGHT.br_blockcount;
>  
> -		bma->idx++;
> -		xfs_iext_remove(bma->ip, bma->idx, 1, state);
> -		bma->idx--;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		xfs_iext_next(ifp, &bma->ext);
> +		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
> +		xfs_iext_prev(ifp, &bma->ext);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1733,7 +1732,7 @@ xfs_bmap_add_extent_delay_real(
>  		 */
>  		PREV.br_startblock = new->br_startblock;
>  		PREV.br_state = new->br_state;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
>  
>  		(*nextents)++;
>  		if (bma->cur == NULL)
> @@ -1767,9 +1766,9 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_startoff += new->br_blockcount;
>  		PREV.br_startblock = nullstartblock(da_new);
>  
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> -		bma->idx--;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &LEFT);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
> +		xfs_iext_prev(ifp, &bma->ext);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
>  
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -1783,7 +1782,6 @@ xfs_bmap_add_extent_delay_real(
>  			if (error)
>  				goto done;
>  		}
> -
>  		break;
>  
>  	case BMAP_LEFT_FILLING:
> @@ -1791,7 +1789,7 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in the first part of a previous delayed allocation.
>  		 * The left neighbor is not contiguous.
>  		 */
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, new);
>  		(*nextents)++;
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
> @@ -1824,7 +1822,9 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_startoff = new_endoff;
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
> -		xfs_iext_insert(bma->ip, bma->idx + 1, 1, &PREV, state);
> +		xfs_iext_next(ifp, &bma->ext);
> +		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
> +		xfs_iext_prev(ifp, &bma->ext);
>  		break;
>  
>  	case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG:
> @@ -1857,9 +1857,9 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
>  
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> -		bma->idx++;
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &RIGHT);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
> +		xfs_iext_next(ifp, &bma->ext);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &RIGHT);
>  		break;
>  
>  	case BMAP_RIGHT_FILLING:
> @@ -1867,7 +1867,7 @@ xfs_bmap_add_extent_delay_real(
>  		 * Filling in the last part of a previous delayed allocation.
>  		 * The right neighbor is not contiguous.
>  		 */
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, new);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, new);
>  		(*nextents)++;
>  		if (bma->cur == NULL)
>  			rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
> @@ -1899,9 +1899,8 @@ xfs_bmap_add_extent_delay_real(
>  
>  		PREV.br_startblock = nullstartblock(da_new);
>  		PREV.br_blockcount = temp;
> -		xfs_iext_insert(bma->ip, bma->idx, 1, &PREV, state);
> -
> -		bma->idx++;
> +		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
> +		xfs_iext_next(ifp, &bma->ext);
>  		break;
>  
>  	case 0:
> @@ -1944,10 +1943,11 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_startblock =
>  			nullstartblock(xfs_bmap_worst_indlen(bma->ip,
>  					PREV.br_blockcount));
> -		xfs_iext_update_extent(bma->ip, state, bma->idx, &PREV);
> +		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
>  
>  		/* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
> -		xfs_iext_insert(bma->ip, bma->idx + 1, 2, &LEFT, state);
> +		xfs_iext_next(ifp, &bma->ext);
> +		xfs_iext_insert(bma->ip, &bma->ext, 2, &LEFT, state);
>  		(*nextents)++;
>  
>  		if (bma->cur == NULL)
> @@ -1975,7 +1975,6 @@ xfs_bmap_add_extent_delay_real(
>  
>  		da_new = startblockval(PREV.br_startblock) +
>  			 startblockval(RIGHT.br_startblock);
> -		bma->idx++;
>  		break;
>  
>  	case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG:
> @@ -2039,7 +2038,7 @@ xfs_bmap_add_extent_unwritten_real(
>  	struct xfs_trans	*tp,
>  	xfs_inode_t		*ip,	/* incore inode pointer */
>  	int			whichfork,
> -	xfs_extnum_t		*idx,	/* extent number to update/insert */
> +	struct xfs_iext_cursor	*ext,
>  	xfs_btree_cur_t		**curp,	/* if *curp is null, not a btree */
>  	xfs_bmbt_irec_t		*new,	/* new data to add to file extents */
>  	xfs_fsblock_t		*first,	/* pointer to firstblock variable */
> @@ -2063,8 +2062,6 @@ xfs_bmap_add_extent_unwritten_real(
>  	cur = *curp;
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
>  
> -	ASSERT(*idx >= 0);
> -	ASSERT(*idx <= xfs_iext_count(ifp));
>  	ASSERT(!isnullstartblock(new->br_startblock));
>  
>  	XFS_STATS_INC(mp, xs_add_exlist);
> @@ -2077,7 +2074,7 @@ xfs_bmap_add_extent_unwritten_real(
>  	 * Set up a bunch of variables to make the tests simpler.
>  	 */
>  	error = 0;
> -	xfs_iext_get_extent(ifp, *idx, &PREV);
> +	xfs_iext_get_extent(ifp, ext, &PREV);
>  	ASSERT(new->br_state != PREV.br_state);
>  	new_endoff = new->br_startoff + new->br_blockcount;
>  	ASSERT(PREV.br_startoff <= new->br_startoff);
> @@ -2096,10 +2093,8 @@ xfs_bmap_add_extent_unwritten_real(
>  	 * Check and set flags if this segment has a left neighbor.
>  	 * Don't set contiguous if the combined extent would be too large.
>  	 */
> -	if (*idx > 0) {
> +	if (xfs_iext_peek_prev_extent(ifp, ext, &LEFT)) {
>  		state |= BMAP_LEFT_VALID;
> -		xfs_iext_get_extent(ifp, *idx - 1, &LEFT);
> -
>  		if (isnullstartblock(LEFT.br_startblock))
>  			state |= BMAP_LEFT_DELAY;
>  	}
> @@ -2116,9 +2111,8 @@ xfs_bmap_add_extent_unwritten_real(
>  	 * Don't set contiguous if the combined extent would be too large.
>  	 * Also check for all-three-contiguous being too large.
>  	 */
> -	if (*idx < xfs_iext_count(ifp) - 1) {
> +	if (xfs_iext_peek_next_extent(ifp, ext, &RIGHT)) {
>  		state |= BMAP_RIGHT_VALID;
> -		xfs_iext_get_extent(ifp, *idx + 1, &RIGHT);
>  		if (isnullstartblock(RIGHT.br_startblock))
>  			state |= BMAP_RIGHT_DELAY;
>  	}
> @@ -2149,9 +2143,9 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
>  
> -		xfs_iext_remove(ip, *idx, 2, state);
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
> +		xfs_iext_remove(ip, ext, 2, state);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 2);
>  		if (cur == NULL)
> @@ -2187,9 +2181,9 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		LEFT.br_blockcount += PREV.br_blockcount;
>  
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
>  		if (cur == NULL)
> @@ -2220,10 +2214,10 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_blockcount += RIGHT.br_blockcount;
>  		PREV.br_state = new->br_state;
>  
> -		++*idx;
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
> @@ -2254,7 +2248,7 @@ xfs_bmap_add_extent_unwritten_real(
>  		 * the new one.
>  		 */
>  		PREV.br_state = new->br_state;
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
>  
>  		if (cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -2282,9 +2276,9 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_startblock += new->br_blockcount;
>  		PREV.br_blockcount -= new->br_blockcount;
>  
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &LEFT);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &LEFT);
>  
>  		if (cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -2316,8 +2310,8 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_startblock += new->br_blockcount;
>  		PREV.br_blockcount -= new->br_blockcount;
>  
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -		xfs_iext_insert(ip, *idx, 1, new, state);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
> +		xfs_iext_insert(ip, ext, 1, new, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  		if (cur == NULL)
> @@ -2350,9 +2344,9 @@ xfs_bmap_add_extent_unwritten_real(
>  		RIGHT.br_startblock = new->br_startblock;
>  		RIGHT.br_blockcount += new->br_blockcount;
>  
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -		++*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &RIGHT);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &RIGHT);
>  
>  		if (cur == NULL)
>  			rval = XFS_ILOG_DEXT;
> @@ -2382,9 +2376,9 @@ xfs_bmap_add_extent_unwritten_real(
>  		old = PREV;
>  		PREV.br_blockcount -= new->br_blockcount;
>  
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -		++*idx;
> -		xfs_iext_insert(ip, *idx, 1, new, state);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_insert(ip, ext, 1, new, state);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> @@ -2425,9 +2419,9 @@ xfs_bmap_add_extent_unwritten_real(
>  		r[1].br_startblock = new->br_startblock + new->br_blockcount;
>  		r[1].br_state = PREV.br_state;
>  
> -		xfs_iext_update_extent(ip, state, *idx, &PREV);
> -		++*idx;
> -		xfs_iext_insert(ip, *idx, 2, &r[0], state);
> +		xfs_iext_update_extent(ip, state, ext, &PREV);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_insert(ip, ext, 2, &r[0], state);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 2);
> @@ -2516,7 +2510,7 @@ STATIC void
>  xfs_bmap_add_extent_hole_delay(
>  	xfs_inode_t		*ip,	/* incore inode pointer */
>  	int			whichfork,
> -	xfs_extnum_t		*idx,	/* extent number to update/insert */
> +	struct xfs_iext_cursor	*ext,
>  	xfs_bmbt_irec_t		*new)	/* new data to add to file extents */
>  {
>  	xfs_ifork_t		*ifp;	/* inode fork pointer */
> @@ -2533,10 +2527,8 @@ xfs_bmap_add_extent_hole_delay(
>  	/*
>  	 * Check and set flags if this segment has a left neighbor
>  	 */
> -	if (*idx > 0) {
> +	if (xfs_iext_peek_prev_extent(ifp, ext, &left)) {
>  		state |= BMAP_LEFT_VALID;
> -		xfs_iext_get_extent(ifp, *idx - 1, &left);
> -
>  		if (isnullstartblock(left.br_startblock))
>  			state |= BMAP_LEFT_DELAY;
>  	}
> @@ -2545,10 +2537,8 @@ xfs_bmap_add_extent_hole_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 (*idx < xfs_iext_count(ifp)) {
> +	if (xfs_iext_get_extent(ifp, ext, &right)) {
>  		state |= BMAP_RIGHT_VALID;
> -		xfs_iext_get_extent(ifp, *idx, &right);
> -
>  		if (isnullstartblock(right.br_startblock))
>  			state |= BMAP_RIGHT_DELAY;
>  	}
> @@ -2591,9 +2581,9 @@ xfs_bmap_add_extent_hole_delay(
>  		left.br_startblock = nullstartblock(newlen);
>  		left.br_blockcount = temp;
>  
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &left);
>  		break;
>  
>  	case BMAP_LEFT_CONTIG:
> @@ -2611,8 +2601,8 @@ xfs_bmap_add_extent_hole_delay(
>  		left.br_blockcount = temp;
>  		left.br_startblock = nullstartblock(newlen);
>  
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &left);
>  		break;
>  
>  	case BMAP_RIGHT_CONTIG:
> @@ -2629,7 +2619,7 @@ xfs_bmap_add_extent_hole_delay(
>  		right.br_startoff = new->br_startoff;
>  		right.br_startblock = nullstartblock(newlen);
>  		right.br_blockcount = temp;
> -		xfs_iext_update_extent(ip, state, *idx, &right);
> +		xfs_iext_update_extent(ip, state, ext, &right);
>  		break;
>  
>  	case 0:
> @@ -2639,7 +2629,7 @@ xfs_bmap_add_extent_hole_delay(
>  		 * Insert a new entry.
>  		 */
>  		oldlen = newlen = 0;
> -		xfs_iext_insert(ip, *idx, 1, new, state);
> +		xfs_iext_insert(ip, ext, 1, new, state);
>  		break;
>  	}
>  	if (oldlen != newlen) {
> @@ -2660,7 +2650,7 @@ xfs_bmap_add_extent_hole_real(
>  	struct xfs_trans	*tp,
>  	struct xfs_inode	*ip,
>  	int			whichfork,
> -	xfs_extnum_t		*idx,
> +	struct xfs_iext_cursor	*ext,
>  	struct xfs_btree_cur	**curp,
>  	struct xfs_bmbt_irec	*new,
>  	xfs_fsblock_t		*first,
> @@ -2678,8 +2668,6 @@ xfs_bmap_add_extent_hole_real(
>  	int			state = xfs_bmap_fork_to_state(whichfork);
>  	struct xfs_bmbt_irec	old;
>  
> -	ASSERT(*idx >= 0);
> -	ASSERT(*idx <= xfs_iext_count(ifp));
>  	ASSERT(!isnullstartblock(new->br_startblock));
>  	ASSERT(!cur || !(cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
>  
> @@ -2688,9 +2676,8 @@ xfs_bmap_add_extent_hole_real(
>  	/*
>  	 * Check and set flags if this segment has a left neighbor.
>  	 */
> -	if (*idx > 0) {
> +	if (xfs_iext_peek_prev_extent(ifp, ext, &left)) {
>  		state |= BMAP_LEFT_VALID;
> -		xfs_iext_get_extent(ifp, *idx - 1, &left);
>  		if (isnullstartblock(left.br_startblock))
>  			state |= BMAP_LEFT_DELAY;
>  	}
> @@ -2699,9 +2686,8 @@ xfs_bmap_add_extent_hole_real(
>  	 * Check and set flags if this segment has a current value.
>  	 * Not true if we're inserting into the "hole" at eof.
>  	 */
> -	if (*idx < xfs_iext_count(ifp)) {
> +	if (xfs_iext_get_extent(ifp, ext, &right)) {
>  		state |= BMAP_RIGHT_VALID;
> -		xfs_iext_get_extent(ifp, *idx, &right);
>  		if (isnullstartblock(right.br_startblock))
>  			state |= BMAP_RIGHT_DELAY;
>  	}
> @@ -2740,9 +2726,9 @@ xfs_bmap_add_extent_hole_real(
>  		 */
>  		left.br_blockcount += new->br_blockcount + right.br_blockcount;
>  
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &left);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
> @@ -2777,8 +2763,8 @@ xfs_bmap_add_extent_hole_real(
>  		old = left;
>  		left.br_blockcount += new->br_blockcount;
>  
> -		--*idx;
> -		xfs_iext_update_extent(ip, state, *idx, &left);
> +		xfs_iext_prev(ifp, ext);
> +		xfs_iext_update_extent(ip, state, ext, &left);
>  
>  		if (cur == NULL) {
>  			rval = xfs_ilog_fext(whichfork);
> @@ -2805,7 +2791,7 @@ xfs_bmap_add_extent_hole_real(
>  		right.br_startoff = new->br_startoff;
>  		right.br_startblock = new->br_startblock;
>  		right.br_blockcount += new->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &right);
> +		xfs_iext_update_extent(ip, state, ext, &right);
>  
>  		if (cur == NULL) {
>  			rval = xfs_ilog_fext(whichfork);
> @@ -2827,7 +2813,7 @@ xfs_bmap_add_extent_hole_real(
>  		 * real allocation.
>  		 * Insert a new entry.
>  		 */
> -		xfs_iext_insert(ip, *idx, 1, new, state);
> +		xfs_iext_insert(ip, ext, 1, new, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  		if (cur == NULL) {
> @@ -3777,7 +3763,7 @@ xfs_bmapi_read(
>  	struct xfs_bmbt_irec	got;
>  	xfs_fileoff_t		obno;
>  	xfs_fileoff_t		end;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  	int			error;
>  	bool			eof = false;
>  	int			n = 0;
> @@ -3819,7 +3805,7 @@ xfs_bmapi_read(
>  			return error;
>  	}
>  
> -	if (!xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got))
> +	if (!xfs_iext_lookup_extent(ip, ifp, bno, &ext, &got))
>  		eof = true;
>  	end = bno + len;
>  	obno = bno;
> @@ -3851,7 +3837,7 @@ xfs_bmapi_read(
>  			break;
>  
>  		/* Else go on to the next record. */
> -		if (!xfs_iext_get_extent(ifp, ++idx, &got))
> +		if (!xfs_iext_next_extent(ifp, &ext, &got))
>  			eof = true;
>  	}
>  	*nmap = n;
> @@ -3879,7 +3865,7 @@ xfs_bmapi_reserve_delalloc(
>  	xfs_filblks_t		len,
>  	xfs_filblks_t		prealloc,
>  	struct xfs_bmbt_irec	*got,
> -	xfs_extnum_t		*lastx,
> +	struct xfs_iext_cursor	*ext,
>  	int			eof)
>  {
>  	struct xfs_mount	*mp = ip->i_mount;
> @@ -3909,7 +3895,7 @@ xfs_bmapi_reserve_delalloc(
>  	if (extsz) {
>  		struct xfs_bmbt_irec	prev;
>  
> -		if (!xfs_iext_get_extent(ifp, *lastx - 1, &prev))
> +		if (!xfs_iext_peek_prev_extent(ifp, ext, &prev))
>  			prev.br_startoff = NULLFILEOFF;
>  
>  		error = xfs_bmap_extsize_align(mp, got, &prev, extsz, rt, eof,
> @@ -3958,7 +3944,7 @@ xfs_bmapi_reserve_delalloc(
>  	got->br_blockcount = alen;
>  	got->br_state = XFS_EXT_NORM;
>  
> -	xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
> +	xfs_bmap_add_extent_hole_delay(ip, whichfork, ext, got);
>  
>  	/*
>  	 * Tag the inode if blocks were preallocated. Note that COW fork
> @@ -4003,8 +3989,7 @@ xfs_bmapi_allocate(
>  	if (bma->wasdel) {
>  		bma->length = (xfs_extlen_t)bma->got.br_blockcount;
>  		bma->offset = bma->got.br_startoff;
> -		if (bma->idx)
> -			xfs_iext_get_extent(ifp, bma->idx - 1, &bma->prev);
> +		xfs_iext_peek_prev_extent(ifp, &bma->ext, &bma->prev);
>  	} else {
>  		bma->length = XFS_FILBLKS_MIN(bma->length, MAXEXTLEN);
>  		if (!bma->eof)
> @@ -4089,7 +4074,7 @@ xfs_bmapi_allocate(
>  		error = xfs_bmap_add_extent_delay_real(bma, whichfork);
>  	else
>  		error = xfs_bmap_add_extent_hole_real(bma->tp, bma->ip,
> -				whichfork, &bma->idx, &bma->cur, &bma->got,
> +				whichfork, &bma->ext, &bma->cur, &bma->got,
>  				bma->firstblock, bma->dfops, &bma->logflags);
>  
>  	bma->logflags |= tmp_logflags;
> @@ -4101,7 +4086,7 @@ xfs_bmapi_allocate(
>  	 * or xfs_bmap_add_extent_hole_real might have merged it into one of
>  	 * the neighbouring ones.
>  	 */
> -	xfs_iext_get_extent(ifp, bma->idx, &bma->got);
> +	xfs_iext_get_extent(ifp, &bma->ext, &bma->got);
>  
>  	ASSERT(bma->got.br_startoff <= bma->offset);
>  	ASSERT(bma->got.br_startoff + bma->got.br_blockcount >=
> @@ -4159,7 +4144,7 @@ xfs_bmapi_convert_unwritten(
>  	}
>  
>  	error = xfs_bmap_add_extent_unwritten_real(bma->tp, bma->ip, whichfork,
> -			&bma->idx, &bma->cur, mval, bma->firstblock, bma->dfops,
> +			&bma->ext, &bma->cur, mval, bma->firstblock, bma->dfops,
>  			&tmp_logflags);
>  	/*
>  	 * Log the inode core unconditionally in the unwritten extent conversion
> @@ -4182,7 +4167,7 @@ xfs_bmapi_convert_unwritten(
>  	 * xfs_bmap_add_extent_unwritten_real might have merged it into one
>  	 * of the neighbouring ones.
>  	 */
> -	xfs_iext_get_extent(ifp, bma->idx, &bma->got);
> +	xfs_iext_get_extent(ifp, &bma->ext, &bma->got);
>  
>  	/*
>  	 * We may have combined previously unwritten space with written space,
> @@ -4301,9 +4286,9 @@ xfs_bmapi_write(
>  	end = bno + len;
>  	obno = bno;
>  
> -	if (!xfs_iext_lookup_extent(ip, ifp, bno, &bma.idx, &bma.got))
> +	if (!xfs_iext_lookup_extent(ip, ifp, bno, &bma.ext, &bma.got))
>  		eof = true;
> -	if (!xfs_iext_get_extent(ifp, bma.idx - 1, &bma.prev))
> +	if (!xfs_iext_peek_prev_extent(ifp, &bma.ext, &bma.prev))
>  		bma.prev.br_startoff = NULLFILEOFF;
>  	bma.tp = tp;
>  	bma.ip = ip;
> @@ -4408,7 +4393,7 @@ xfs_bmapi_write(
>  
>  		/* Else go on to the next record. */
>  		bma.prev = bma.got;
> -		if (!xfs_iext_get_extent(ifp, ++bma.idx, &bma.got))
> +		if (!xfs_iext_next_extent(ifp, &bma.ext, &bma.got))
>  			eof = true;
>  	}
>  	*nmap = n;
> @@ -4481,7 +4466,7 @@ xfs_bmapi_remap(
>  	struct xfs_btree_cur	*cur = NULL;
>  	xfs_fsblock_t		firstblock = NULLFSBLOCK;
>  	struct xfs_bmbt_irec	got;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  	int			logflags = 0, error;
>  
>  	ASSERT(len > 0);
> @@ -4505,7 +4490,7 @@ xfs_bmapi_remap(
>  			return error;
>  	}
>  
> -	if (xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got)) {
> +	if (xfs_iext_lookup_extent(ip, ifp, bno, &ext, &got)) {
>  		/* make sure we only reflink into a hole. */
>  		ASSERT(got.br_startoff > bno);
>  		ASSERT(got.br_startoff - bno >= len);
> @@ -4526,7 +4511,7 @@ xfs_bmapi_remap(
>  	got.br_blockcount = len;
>  	got.br_state = XFS_EXT_NORM;
>  
> -	error = xfs_bmap_add_extent_hole_real(tp, ip, XFS_DATA_FORK, &idx, &cur,
> +	error = xfs_bmap_add_extent_hole_real(tp, ip, XFS_DATA_FORK, &ext, &cur,
>  			&got, &firstblock, dfops, &logflags);
>  	if (error)
>  		goto error0;
> @@ -4643,7 +4628,7 @@ int
>  xfs_bmap_del_extent_delay(
>  	struct xfs_inode	*ip,
>  	int			whichfork,
> -	xfs_extnum_t		*idx,
> +	struct xfs_iext_cursor	*ext,
>  	struct xfs_bmbt_irec	*got,
>  	struct xfs_bmbt_irec	*del)
>  {
> @@ -4665,8 +4650,6 @@ xfs_bmap_del_extent_delay(
>  	da_old = startblockval(got->br_startblock);
>  	da_new = 0;
>  
> -	ASSERT(*idx >= 0);
> -	ASSERT(*idx <= xfs_iext_count(ifp));
>  	ASSERT(del->br_blockcount > 0);
>  	ASSERT(got->br_startoff <= del->br_startoff);
>  	ASSERT(got_endoff >= del_endoff);
> @@ -4700,8 +4683,8 @@ xfs_bmap_del_extent_delay(
>  		/*
>  		 * Matches the whole extent.  Delete the entry.
>  		 */
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
>  		break;
>  	case BMAP_LEFT_FILLING:
>  		/*
> @@ -4712,7 +4695,7 @@ xfs_bmap_del_extent_delay(
>  		da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
>  				got->br_blockcount), da_old);
>  		got->br_startblock = nullstartblock((int)da_new);
> -		xfs_iext_update_extent(ip, state, *idx, got);
> +		xfs_iext_update_extent(ip, state, ext, got);
>  		break;
>  	case BMAP_RIGHT_FILLING:
>  		/*
> @@ -4722,7 +4705,7 @@ xfs_bmap_del_extent_delay(
>  		da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip,
>  				got->br_blockcount), da_old);
>  		got->br_startblock = nullstartblock((int)da_new);
> -		xfs_iext_update_extent(ip, state, *idx, got);
> +		xfs_iext_update_extent(ip, state, ext, got);
>  		break;
>  	case 0:
>  		/*
> @@ -4750,9 +4733,9 @@ xfs_bmap_del_extent_delay(
>  		new.br_state = got->br_state;
>  		new.br_startblock = nullstartblock((int)new_indlen);
>  
> -		xfs_iext_update_extent(ip, state, *idx, got);
> -		++*idx;
> -		xfs_iext_insert(ip, *idx, 1, &new, state);
> +		xfs_iext_update_extent(ip, state, ext, got);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_insert(ip, ext, 1, &new, state);
>  
>  		da_new = got_indlen + new_indlen - stolen;
>  		del->br_blockcount -= stolen;
> @@ -4771,7 +4754,7 @@ xfs_bmap_del_extent_delay(
>  void
>  xfs_bmap_del_extent_cow(
>  	struct xfs_inode	*ip,
> -	xfs_extnum_t		*idx,
> +	struct xfs_iext_cursor	*ext,
>  	struct xfs_bmbt_irec	*got,
>  	struct xfs_bmbt_irec	*del)
>  {
> @@ -4786,8 +4769,6 @@ xfs_bmap_del_extent_cow(
>  	del_endoff = del->br_startoff + del->br_blockcount;
>  	got_endoff = got->br_startoff + got->br_blockcount;
>  
> -	ASSERT(*idx >= 0);
> -	ASSERT(*idx <= xfs_iext_count(ifp));
>  	ASSERT(del->br_blockcount > 0);
>  	ASSERT(got->br_startoff <= del->br_startoff);
>  	ASSERT(got_endoff >= del_endoff);
> @@ -4803,8 +4784,8 @@ xfs_bmap_del_extent_cow(
>  		/*
>  		 * Matches the whole extent.  Delete the entry.
>  		 */
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
>  		break;
>  	case BMAP_LEFT_FILLING:
>  		/*
> @@ -4813,14 +4794,14 @@ xfs_bmap_del_extent_cow(
>  		got->br_startoff = del_endoff;
>  		got->br_blockcount -= del->br_blockcount;
>  		got->br_startblock = del->br_startblock + del->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, got);
> +		xfs_iext_update_extent(ip, state, ext, got);
>  		break;
>  	case BMAP_RIGHT_FILLING:
>  		/*
>  		 * Deleting the last part of the extent.
>  		 */
>  		got->br_blockcount -= del->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, got);
> +		xfs_iext_update_extent(ip, state, ext, got);
>  		break;
>  	case 0:
>  		/*
> @@ -4833,9 +4814,9 @@ xfs_bmap_del_extent_cow(
>  		new.br_state = got->br_state;
>  		new.br_startblock = del->br_startblock + del->br_blockcount;
>  
> -		xfs_iext_update_extent(ip, state, *idx, got);
> -		++*idx;
> -		xfs_iext_insert(ip, *idx, 1, &new, state);
> +		xfs_iext_update_extent(ip, state, ext, got);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_insert(ip, ext, 1, &new, state);
>  		break;
>  	}
>  }
> @@ -4848,7 +4829,7 @@ STATIC int				/* error */
>  xfs_bmap_del_extent_real(
>  	xfs_inode_t		*ip,	/* incore inode pointer */
>  	xfs_trans_t		*tp,	/* current transaction pointer */
> -	xfs_extnum_t		*idx,	/* extent number to update/delete */
> +	struct xfs_iext_cursor	*ext,
>  	struct xfs_defer_ops	*dfops,	/* list of extents to be freed */
>  	xfs_btree_cur_t		*cur,	/* if null, not a btree */
>  	xfs_bmbt_irec_t		*del,	/* data to remove from extents */
> @@ -4877,9 +4858,8 @@ xfs_bmap_del_extent_real(
>  	XFS_STATS_INC(mp, xs_del_exlist);
>  
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	ASSERT((*idx >= 0) && (*idx < xfs_iext_count(ifp)));
>  	ASSERT(del->br_blockcount > 0);
> -	xfs_iext_get_extent(ifp, *idx, &got);
> +	xfs_iext_get_extent(ifp, ext, &got);
>  	ASSERT(got.br_startoff <= del->br_startoff);
>  	del_endoff = del->br_startoff + del->br_blockcount;
>  	got_endoff = got.br_startoff + got.br_blockcount;
> @@ -4944,9 +4924,8 @@ xfs_bmap_del_extent_real(
>  		/*
>  		 * Matches the whole extent.  Delete the entry.
>  		 */
> -		xfs_iext_remove(ip, *idx, 1, state);
> -		--*idx;
> -
> +		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_prev(ifp, ext);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
>  		flags |= XFS_ILOG_CORE;
> @@ -4965,7 +4944,7 @@ xfs_bmap_del_extent_real(
>  		got.br_startoff = del_endoff;
>  		got.br_startblock = del_endblock;
>  		got.br_blockcount -= del->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &got);
> +		xfs_iext_update_extent(ip, state, ext, &got);
>  		if (!cur) {
>  			flags |= xfs_ilog_fext(whichfork);
>  			break;
> @@ -4979,7 +4958,7 @@ xfs_bmap_del_extent_real(
>  		 * Deleting the last part of the extent.
>  		 */
>  		got.br_blockcount -= del->br_blockcount;
> -		xfs_iext_update_extent(ip, state, *idx, &got);
> +		xfs_iext_update_extent(ip, state, ext, &got);
>  		if (!cur) {
>  			flags |= xfs_ilog_fext(whichfork);
>  			break;
> @@ -4995,7 +4974,7 @@ xfs_bmap_del_extent_real(
>  		old = got;
>  
>  		got.br_blockcount = del->br_startoff - got.br_startoff;
> -		xfs_iext_update_extent(ip, state, *idx, &got);
> +		xfs_iext_update_extent(ip, state, ext, &got);
>  
>  		new.br_startoff = del_endoff;
>  		new.br_blockcount = got_endoff - del_endoff;
> @@ -5039,7 +5018,7 @@ xfs_bmap_del_extent_real(
>  				 * Reset the extent record back
>  				 * to the original value.
>  				 */
> -				xfs_iext_update_extent(ip, state, *idx, &old);
> +				xfs_iext_update_extent(ip, state, ext, &old);
>  				flags = 0;
>  				error = -ENOSPC;
>  				goto done;
> @@ -5049,8 +5028,8 @@ xfs_bmap_del_extent_real(
>  			flags |= xfs_ilog_fext(whichfork);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> -		++*idx;
> -		xfs_iext_insert(ip, *idx, 1, &new, state);
> +		xfs_iext_next(ifp, ext);
> +		xfs_iext_insert(ip, ext, 1, &new, state);
>  		break;
>  	}
>  
> @@ -5113,7 +5092,6 @@ __xfs_bunmapi(
>  	xfs_bmbt_irec_t		got;		/* current extent record */
>  	xfs_ifork_t		*ifp;		/* inode fork pointer */
>  	int			isrt;		/* freeing in rt area */
> -	xfs_extnum_t		lastx;		/* last extent index used */
>  	int			logflags;	/* transaction logging flags */
>  	xfs_extlen_t		mod;		/* rt extent offset */
>  	xfs_mount_t		*mp;		/* mount structure */
> @@ -5125,6 +5103,8 @@ __xfs_bunmapi(
>  	xfs_fileoff_t		max_len;
>  	xfs_agnumber_t		prev_agno = NULLAGNUMBER, agno;
>  	xfs_fileoff_t		end;
> +	struct xfs_iext_cursor	ext;
> +	bool			done = false;
>  
>  	trace_xfs_bunmap(ip, start, len, flags, _RET_IP_);
>  
> @@ -5167,7 +5147,7 @@ __xfs_bunmapi(
>  	isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip);
>  	end = start + len;
>  
> -	if (!xfs_iext_lookup_extent_before(ip, ifp, &end, &lastx, &got)) {
> +	if (!xfs_iext_lookup_extent_before(ip, ifp, &end, &ext, &got)) {
>  		*rlen = 0;
>  		return 0;
>  	}
> @@ -5194,16 +5174,16 @@ __xfs_bunmapi(
>  	}
>  
>  	extno = 0;
> -	while (end != (xfs_fileoff_t)-1 && end >= start && lastx >= 0 &&
> +	while (end != (xfs_fileoff_t)-1 && end >= start &&
>  	       (nexts == 0 || extno < nexts) && max_len > 0) {
>  		/*
>  		 * Is the found extent after a hole in which end lives?
>  		 * Just back up to the previous extent, if so.
>  		 */
> -		if (got.br_startoff > end) {
> -			if (--lastx < 0)
> -				break;
> -			xfs_iext_get_extent(ifp, lastx, &got);
> +		if (got.br_startoff > end &&
> +		    !xfs_iext_prev_extent(ifp, &ext, &got)) {
> +			done = true;
> +			break;
>  		}
>  		/*
>  		 * Is the last block of this extent before the range
> @@ -5266,10 +5246,10 @@ __xfs_bunmapi(
>  				ASSERT(end >= mod);
>  				end -= mod > del.br_blockcount ?
>  					del.br_blockcount : mod;
> -				if (end < got.br_startoff) {
> -					if (--lastx >= 0)
> -						xfs_iext_get_extent(ifp, lastx,
> -								&got);
> +				if (end < got.br_startoff &&
> +				    !xfs_iext_prev_extent(ifp, &ext, &got)) {
> +					done = true;
> +					break;
>  				}
>  				continue;
>  			}
> @@ -5290,7 +5270,7 @@ __xfs_bunmapi(
>  			}
>  			del.br_state = XFS_EXT_UNWRITTEN;
>  			error = xfs_bmap_add_extent_unwritten_real(tp, ip,
> -					whichfork, &lastx, &cur, &del,
> +					whichfork, &ext, &cur, &del,
>  					firstblock, dfops, &logflags);
>  			if (error)
>  				goto error0;
> @@ -5317,8 +5297,11 @@ __xfs_bunmapi(
>  				 */
>  				ASSERT(end >= del.br_blockcount);
>  				end -= del.br_blockcount;
> -				if (got.br_startoff > end && --lastx >= 0)
> -					xfs_iext_get_extent(ifp, lastx, &got);
> +				if (got.br_startoff > end &&
> +				    !xfs_iext_prev_extent(ifp, &ext, &got)) {
> +					done = true;
> +					break;
> +				}
>  				continue;
>  			} else if (del.br_state == XFS_EXT_UNWRITTEN) {
>  				struct xfs_bmbt_irec	prev;
> @@ -5329,8 +5312,8 @@ __xfs_bunmapi(
>  				 * Unwrite the killed part of that one and
>  				 * try again.
>  				 */
> -				ASSERT(lastx > 0);
> -				xfs_iext_get_extent(ifp, lastx - 1, &prev);
> +				if (!xfs_iext_prev_extent(ifp, &ext, &prev))
> +					ASSERT(0);
>  				ASSERT(prev.br_state == XFS_EXT_NORM);
>  				ASSERT(!isnullstartblock(prev.br_startblock));
>  				ASSERT(del.br_startblock ==
> @@ -5342,9 +5325,8 @@ __xfs_bunmapi(
>  					prev.br_startoff = start;
>  				}
>  				prev.br_state = XFS_EXT_UNWRITTEN;
> -				lastx--;
>  				error = xfs_bmap_add_extent_unwritten_real(tp,
> -						ip, whichfork, &lastx, &cur,
> +						ip, whichfork, &ext, &cur,
>  						&prev, firstblock, dfops,
>  						&logflags);
>  				if (error)
> @@ -5354,7 +5336,7 @@ __xfs_bunmapi(
>  				ASSERT(del.br_state == XFS_EXT_NORM);
>  				del.br_state = XFS_EXT_UNWRITTEN;
>  				error = xfs_bmap_add_extent_unwritten_real(tp,
> -						ip, whichfork, &lastx, &cur,
> +						ip, whichfork, &ext, &cur,
>  						&del, firstblock, dfops,
>  						&logflags);
>  				if (error)
> @@ -5364,10 +5346,10 @@ __xfs_bunmapi(
>  		}
>  
>  		if (wasdel) {
> -			error = xfs_bmap_del_extent_delay(ip, whichfork, &lastx,
> +			error = xfs_bmap_del_extent_delay(ip, whichfork, &ext,
>  					&got, &del);
>  		} else {
> -			error = xfs_bmap_del_extent_real(ip, tp, &lastx, dfops,
> +			error = xfs_bmap_del_extent_real(ip, tp, &ext, dfops,
>  					cur, &del, &tmp_logflags, whichfork,
>  					flags);
>  			logflags |= tmp_logflags;
> @@ -5383,15 +5365,16 @@ __xfs_bunmapi(
>  		 * If not done go on to the next (previous) record.
>  		 */
>  		if (end != (xfs_fileoff_t)-1 && end >= start) {
> -			if (lastx >= 0) {
> -				xfs_iext_get_extent(ifp, lastx, &got);
> -				if (got.br_startoff > end && --lastx >= 0)
> -					xfs_iext_get_extent(ifp, lastx, &got);
> +			if (!xfs_iext_get_extent(ifp, &ext, &got) ||
> +			    (got.br_startoff > end &&
> +			     !xfs_iext_prev_extent(ifp, &ext, &got))) {
> +				done = true;
> +				break;
>  			}
>  			extno++;
>  		}
>  	}
> -	if (end == (xfs_fileoff_t)-1 || end < start || lastx < 0)
> +	if (done || end == (xfs_fileoff_t)-1 || end < start)
>  		*rlen = 0;
>  	else
>  		*rlen = end - start + 1;
> @@ -5512,7 +5495,7 @@ xfs_bmse_merge(
>  	struct xfs_inode		*ip,
>  	int				whichfork,
>  	xfs_fileoff_t			shift,		/* shift fsb */
> -	int				*current_ext,	/* idx of gotp */
> +	struct xfs_iext_cursor		*ext,
>  	struct xfs_bmbt_irec		*got,		/* extent to shift */
>  	struct xfs_bmbt_irec		*left,		/* preceding extent */
>  	struct xfs_btree_cur		*cur,
> @@ -5567,10 +5550,10 @@ xfs_bmse_merge(
>  		return error;
>  
>  done:
> -	xfs_iext_remove(ip, *current_ext, 1, 0);
> -	--*current_ext;
> -	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
> -			*current_ext, &new);
> +	xfs_iext_remove(ip, ext, 1, 0);
> +	xfs_iext_prev(XFS_IFORK_PTR(ip, whichfork), ext);
> +	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), ext,
> +			&new);
>  
>  	/* update reverse mapping. rmap functions merge the rmaps for us */
>  	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, got);
> @@ -5585,7 +5568,7 @@ static int
>  xfs_bmap_shift_update_extent(
>  	struct xfs_inode	*ip,
>  	int			whichfork,
> -	xfs_extnum_t		idx,
> +	struct xfs_iext_cursor	*ext,
>  	struct xfs_bmbt_irec	*got,
>  	struct xfs_btree_cur	*cur,
>  	int			*logflags,
> @@ -5613,7 +5596,7 @@ xfs_bmap_shift_update_extent(
>  		*logflags |= XFS_ILOG_DEXT;
>  	}
>  
> -	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), idx, got);
> +	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), ext, got);
>  
>  	/* update reverse mapping */
>  	error = xfs_rmap_unmap_extent(mp, dfops, ip, whichfork, &prev);
> @@ -5638,7 +5621,7 @@ xfs_bmap_collapse_extents(
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
>  	struct xfs_btree_cur	*cur = NULL;
>  	struct xfs_bmbt_irec	got, prev;
> -	xfs_extnum_t		current_ext;
> +	struct xfs_iext_cursor	ext;
>  	xfs_fileoff_t		new_startoff;
>  	int			error = 0;
>  	int			logflags = 0;
> @@ -5669,14 +5652,14 @@ xfs_bmap_collapse_extents(
>  		cur->bc_private.b.flags = 0;
>  	}
>  
> -	if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &current_ext, &got)) {
> +	if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &ext, &got)) {
>  		*done = true;
>  		goto del_cursor;
>  	}
>  	XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
>  
>  	new_startoff = got.br_startoff - offset_shift_fsb;
> -	if (xfs_iext_get_extent(ifp, current_ext - 1, &prev)) {
> +	if (xfs_iext_peek_prev_extent(ifp, &ext, &prev)) {
>  		if (new_startoff < prev.br_startoff + prev.br_blockcount) {
>  			error = -EINVAL;
>  			goto del_cursor;
> @@ -5684,8 +5667,8 @@ xfs_bmap_collapse_extents(
>  
>  		if (xfs_bmse_can_merge(&prev, &got, offset_shift_fsb)) {
>  			error = xfs_bmse_merge(ip, whichfork, offset_shift_fsb,
> -					&current_ext, &got, &prev, cur,
> -					&logflags, dfops);
> +					&ext, &got, &prev, cur, &logflags,
> +					dfops);
>  			if (error)
>  				goto del_cursor;
>  			goto done;
> @@ -5697,13 +5680,13 @@ xfs_bmap_collapse_extents(
>  		}
>  	}
>  
> -	error = xfs_bmap_shift_update_extent(ip, whichfork, current_ext, &got,
> -			cur, &logflags, dfops, new_startoff);
> +	error = xfs_bmap_shift_update_extent(ip, whichfork, &ext, &got, cur,
> +			&logflags, dfops, new_startoff);
>  	if (error)
>  		goto del_cursor;
>  
>  done:
> -	if (!xfs_iext_get_extent(ifp, ++current_ext, &got)) {
> +	if (!xfs_iext_next_extent(ifp, &ext, &got)) {
>  		 *done = true;
>  		 goto del_cursor;
>  	}
> @@ -5734,7 +5717,7 @@ xfs_bmap_insert_extents(
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
>  	struct xfs_btree_cur	*cur = NULL;
>  	struct xfs_bmbt_irec	got, next;
> -	xfs_extnum_t		current_ext;
> +	struct xfs_iext_cursor	ext;
>  	xfs_fileoff_t		new_startoff;
>  	int			error = 0;
>  	int			logflags = 0;
> @@ -5766,15 +5749,14 @@ xfs_bmap_insert_extents(
>  	}
>  
>  	if (*next_fsb == NULLFSBLOCK) {
> -		current_ext = xfs_iext_count(ifp) - 1;
> -		if (!xfs_iext_get_extent(ifp, current_ext, &got) ||
> +		xfs_iext_last(ifp, &ext);
> +		if (!xfs_iext_get_extent(ifp, &ext, &got) ||
>  		    stop_fsb > got.br_startoff) {
>  			*done = true;
>  			goto del_cursor;
>  		}
>  	} else {
> -		if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &current_ext,
> -				&got)) {
> +		if (!xfs_iext_lookup_extent(ip, ifp, *next_fsb, &ext, &got)) {
>  			*done = true;
>  			goto del_cursor;
>  		}
> @@ -5787,7 +5769,7 @@ xfs_bmap_insert_extents(
>  	}
>  
>  	new_startoff = got.br_startoff + offset_shift_fsb;
> -	if (xfs_iext_get_extent(ifp, current_ext + 1, &next)) {
> +	if (xfs_iext_peek_next_extent(ifp, &ext, &next)) {
>  		if (new_startoff + got.br_blockcount > next.br_startoff) {
>  			error = -EINVAL;
>  			goto del_cursor;
> @@ -5803,12 +5785,12 @@ xfs_bmap_insert_extents(
>  			WARN_ON_ONCE(1);
>  	}
>  
> -	error = xfs_bmap_shift_update_extent(ip, whichfork, current_ext, &got,
> -			cur, &logflags, dfops, new_startoff);
> +	error = xfs_bmap_shift_update_extent(ip, whichfork, &ext, &got, cur,
> +			&logflags, dfops, new_startoff);
>  	if (error)
>  		goto del_cursor;
>  
> -	if (!xfs_iext_get_extent(ifp, --current_ext, &got) ||
> +	if (!xfs_iext_prev_extent(ifp, &ext, &got) ||
>  	    stop_fsb >= got.br_startoff + got.br_blockcount) {
>  		*done = true;
>  		goto del_cursor;
> @@ -5825,10 +5807,10 @@ xfs_bmap_insert_extents(
>  }
>  
>  /*
> - * 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.
> + * Splits an extent into two extents at split_fsb block such that it is the
> + * first block of the current_ext. @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(
> @@ -5845,7 +5827,7 @@ xfs_bmap_split_extent_at(
>  	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;
> +	struct xfs_iext_cursor		ext;
>  	int				error = 0;
>  	int				logflags = 0;
>  	int				i = 0;
> @@ -5873,7 +5855,7 @@ xfs_bmap_split_extent_at(
>  	/*
>  	 * If there are not extents, or split_fsb lies in a hole we are done.
>  	 */
> -	if (!xfs_iext_lookup_extent(ip, ifp, split_fsb, &current_ext, &got) ||
> +	if (!xfs_iext_lookup_extent(ip, ifp, split_fsb, &ext, &got) ||
>  	    got.br_startoff >= split_fsb)
>  		return 0;
>  
> @@ -5895,8 +5877,8 @@ xfs_bmap_split_extent_at(
>  	}
>  
>  	got.br_blockcount = gotblkcnt;
> -	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork),
> -			current_ext, &got);
> +	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), &ext,
> +			&got);
>  
>  	logflags = XFS_ILOG_CORE;
>  	if (cur) {
> @@ -5907,8 +5889,8 @@ xfs_bmap_split_extent_at(
>  		logflags |= XFS_ILOG_DEXT;
>  
>  	/* Add new extent */
> -	current_ext++;
> -	xfs_iext_insert(ip, current_ext, 1, &new, 0);
> +	xfs_iext_next(ifp, &ext);
> +	xfs_iext_insert(ip, &ext, 1, &new, 0);
>  	XFS_IFORK_NEXT_SET(ip, whichfork,
>  			   XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  
> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index a8777682ba57..195f335f4615 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -43,7 +43,7 @@ struct xfs_bmalloca {
>  	xfs_fsblock_t		blkno;	/* starting block of new extent */
>  
>  	struct xfs_btree_cur	*cur;	/* btree cursor */
> -	xfs_extnum_t		idx;	/* current extent index */
> +	struct xfs_iext_cursor	ext;
>  	int			nallocs;/* number of extents alloc'd */
>  	int			logflags;/* flags for transaction logging */
>  
> @@ -216,10 +216,11 @@ int	xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
>  		xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
>  		struct xfs_defer_ops *dfops, int *done);
>  int	xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
> -		xfs_extnum_t *idx, struct xfs_bmbt_irec *got,
> +		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *got,
> +		struct xfs_bmbt_irec *del);
> +void	xfs_bmap_del_extent_cow(struct xfs_inode *ip,
> +		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *got,
>  		struct xfs_bmbt_irec *del);
> -void	xfs_bmap_del_extent_cow(struct xfs_inode *ip, xfs_extnum_t *idx,
> -		struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *del);
>  uint	xfs_default_attroffset(struct xfs_inode *ip);
>  int	xfs_bmap_collapse_extents(struct xfs_trans *tp, struct xfs_inode *ip,
>  		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,
> @@ -232,7 +233,8 @@ int	xfs_bmap_insert_extents(struct xfs_trans *tp, struct xfs_inode *ip,
>  int	xfs_bmap_split_extent(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, xfs_extnum_t *lastx, int eof);
> +		struct xfs_bmbt_irec *got, struct xfs_iext_cursor *cur,
> +		int eof);
>  
>  enum xfs_bmap_intent_type {
>  	XFS_BMAP_MAP = 1,
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 7dd77b497fc2..c9e10d4818b7 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -343,6 +343,7 @@ xfs_iformat_extents(
>  	int			state = xfs_bmap_fork_to_state(whichfork);
>  	int			nex = XFS_DFORK_NEXTENTS(dip, whichfork);
>  	int			size = nex * sizeof(xfs_bmbt_rec_t);
> +	struct xfs_iext_cursor	ext;
>  	struct xfs_bmbt_rec	*dp;
>  	int			i;
>  
> @@ -369,16 +370,21 @@ xfs_iformat_extents(
>  	ifp->if_bytes = size;
>  	if (size) {
>  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> +
> +		xfs_iext_first(ifp, &ext);
>  		for (i = 0; i < nex; i++, dp++) {
>  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> +
>  			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
>  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				return -EFSCORRUPTED;
>  			}
> +
>  			ep->l0 = get_unaligned_be64(&dp->l0);
>  			ep->l1 = get_unaligned_be64(&dp->l1);
> -			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> +			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
> +			xfs_iext_next(ifp, &ext);
>  		}
>  	}
>  	ifp->if_flags |= XFS_IFEXTENTS;
> @@ -739,17 +745,18 @@ xfs_iextents_copy(
>  {
>  	int			state = xfs_bmap_fork_to_state(whichfork);
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
> +	struct xfs_iext_cursor	ext;
>  	struct xfs_bmbt_irec	rec;
> -	int			copied = 0, i = 0;
> +	int			copied = 0;
>  
>  	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
>  	ASSERT(ifp->if_bytes > 0);
>  
> -	while (xfs_iext_get_extent(ifp, i++, &rec)) {
> +	for_each_iext(ifp, &ext, &rec) {
>  		if (isnullstartblock(rec.br_startblock))
>  			continue;
>  		xfs_bmbt_disk_set_all(dp, &rec);
> -		trace_xfs_write_extent(ip, i, state, _RET_IP_);
> +		trace_xfs_write_extent(ip, &ext, state, _RET_IP_);
>  		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
>  		copied += sizeof(struct xfs_bmbt_rec);
>  		dp++;
> @@ -894,7 +901,7 @@ xfs_iext_state_to_fork(
>  void
>  xfs_iext_insert(
>  	xfs_inode_t	*ip,		/* incore inode pointer */
> -	xfs_extnum_t	idx,		/* starting index of new items */
> +	struct xfs_iext_cursor *cur,
>  	xfs_extnum_t	count,		/* number of inserted items */
>  	xfs_bmbt_irec_t	*new,		/* items to insert */
>  	int		state)		/* type of extent conversion */
> @@ -902,12 +909,12 @@ xfs_iext_insert(
>  	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
>  	xfs_extnum_t	i;		/* extent record index */
>  
> -	trace_xfs_iext_insert(ip, idx, new, state, _RET_IP_);
> +	trace_xfs_iext_insert(ip, cur->idx, new, state, _RET_IP_);
>  
>  	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
> -	xfs_iext_add(ifp, idx, count);
> -	for (i = idx; i < idx + count; i++, new++)
> -		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, i), new);
> +	xfs_iext_add(ifp, cur->idx, count);
> +	for (i = 0; i < count; i++, new++)
> +		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx + i), new);
>  }
>  
>  /*
> @@ -1145,7 +1152,7 @@ xfs_iext_add_indirect_multi(
>  void
>  xfs_iext_remove(
>  	xfs_inode_t	*ip,		/* incore inode pointer */
> -	xfs_extnum_t	idx,		/* index to begin removing exts */
> +	struct xfs_iext_cursor *cur,
>  	int		ext_diff,	/* number of extents to remove */
>  	int		state)		/* type of extent conversion */
>  {
> @@ -1153,7 +1160,7 @@ xfs_iext_remove(
>  	xfs_extnum_t	nextents;	/* number of extents in file */
>  	int		new_size;	/* size of extents after removal */
>  
> -	trace_xfs_iext_remove(ip, idx, state, _RET_IP_);
> +	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
>  
>  	ASSERT(ext_diff > 0);
>  	nextents = xfs_iext_count(ifp);
> @@ -1162,11 +1169,11 @@ xfs_iext_remove(
>  	if (new_size == 0) {
>  		xfs_iext_destroy(ifp);
>  	} else if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_iext_remove_indirect(ifp, idx, ext_diff);
> +		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
>  	} else if (ifp->if_real_bytes) {
> -		xfs_iext_remove_direct(ifp, idx, ext_diff);
> +		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
>  	} else {
> -		xfs_iext_remove_inline(ifp, idx, ext_diff);
> +		xfs_iext_remove_inline(ifp, cur->idx, ext_diff);
>  	}
>  	ifp->if_bytes = new_size;
>  }
> @@ -1913,26 +1920,26 @@ xfs_ifork_init_cow(
>   * Lookup the extent covering bno.
>   *
>   * If there is an extent covering bno return the extent index, and store the
> - * expanded extent structure in *gotp, and the extent index in *idx.
> + * expanded extent structure in *gotp, and the extent cursor in *cur.
>   * If there is no extent covering bno, but there is an extent after it (e.g.
> - * it lies in a hole) return that extent in *gotp and its index in *idx
> + * it lies in a hole) return that extent in *gotp and its cursor in *cur
>   * instead.
> - * If bno is beyond the last extent return false, and return the index after
> - * the last valid index in *idxp.
> + * If bno is beyond the last extent return false, and return an invalid
> + * cursor value.
>   */
>  bool
>  xfs_iext_lookup_extent(
>  	struct xfs_inode	*ip,
>  	struct xfs_ifork	*ifp,
>  	xfs_fileoff_t		bno,
> -	xfs_extnum_t		*idxp,
> +	struct xfs_iext_cursor	*cur,
>  	struct xfs_bmbt_irec	*gotp)
>  {
>  	struct xfs_bmbt_rec_host *ep;
>  
>  	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
>  
> -	ep = xfs_iext_bno_to_ext(ifp, bno, idxp);
> +	ep = xfs_iext_bno_to_ext(ifp, bno, &cur->idx);
>  	if (!ep)
>  		return false;
>  	xfs_bmbt_get_all(ep, gotp);
> @@ -1948,31 +1955,31 @@ xfs_iext_lookup_extent_before(
>  	struct xfs_inode	*ip,
>  	struct xfs_ifork	*ifp,
>  	xfs_fileoff_t		*end,
> -	xfs_extnum_t		*idxp,
> +	struct xfs_iext_cursor	*cur,
>  	struct xfs_bmbt_irec	*gotp)
>  {
> -	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, idxp, gotp) &&
> +	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
>  	    gotp->br_startoff <= *end - 1)
>  		return true;
> -	if (!xfs_iext_get_extent(ifp, --*idxp, gotp))
> +	if (!xfs_iext_prev_extent(ifp, cur, gotp))
>  		return false;
>  	*end = gotp->br_startoff + gotp->br_blockcount;
>  	return true;
>  }
>  
>  /*
> - * Return true if there is an extent at index idx, and return the expanded
> - * extent structure at idx in that case.  Else return false.
> + * Return true if there is an extent at cursor cur and return the expanded
> + * extent structure at cur in gotp in that case.  Else return false.
>   */
>  bool
>  xfs_iext_get_extent(
>  	struct xfs_ifork	*ifp,
> -	xfs_extnum_t		idx,
> +	struct xfs_iext_cursor	*cur,
>  	struct xfs_bmbt_irec	*gotp)
>  {
> -	if (idx < 0 || idx >= xfs_iext_count(ifp))
> +	if (cur->idx < 0 || cur->idx >= xfs_iext_count(ifp))
>  		return false;
> -	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), gotp);
> +	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
>  	return true;
>  }
>  
> @@ -1980,15 +1987,15 @@ void
>  xfs_iext_update_extent(
>  	struct xfs_inode	*ip,
>  	int			state,
> -	xfs_extnum_t		idx,
> +	struct xfs_iext_cursor	*cur,
>  	struct xfs_bmbt_irec	*gotp)
>  {
>  	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
>  
> -	ASSERT(idx >= 0);
> -	ASSERT(idx < xfs_iext_count(ifp));
> +	ASSERT(cur->idx >= 0);
> +	ASSERT(cur->idx < xfs_iext_count(ifp));
>  
> -	trace_xfs_bmap_pre_update(ip, idx, state, _RET_IP_);
> -	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, idx), gotp);
> -	trace_xfs_bmap_post_update(ip, idx, state, _RET_IP_);
> +	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
> +	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
> +	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
>  }
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index 113fd42ec36d..dc347dd9dc78 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
> @@ -151,12 +151,13 @@ void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
>  struct xfs_bmbt_rec_host *
>  		xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
>  xfs_extnum_t	xfs_iext_count(struct xfs_ifork *);
> -void		xfs_iext_insert(struct xfs_inode *, xfs_extnum_t, xfs_extnum_t,
> -				struct xfs_bmbt_irec *, int);
> +void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
> +			xfs_extnum_t, struct xfs_bmbt_irec *, int);
>  void		xfs_iext_add(struct xfs_ifork *, xfs_extnum_t, int);
>  void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
>  					    xfs_extnum_t, int);
> -void		xfs_iext_remove(struct xfs_inode *, xfs_extnum_t, int, int);
> +void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
> +			int, int);
>  void		xfs_iext_remove_inline(struct xfs_ifork *, xfs_extnum_t, int);
>  void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
>  void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
> @@ -182,15 +183,85 @@ void		xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
>  
>  bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
>  			struct xfs_ifork *ifp, xfs_fileoff_t bno,
> -			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
> +			struct xfs_iext_cursor *cur,
> +			 struct xfs_bmbt_irec *gotp);
>  bool		xfs_iext_lookup_extent_before(struct xfs_inode *ip,
>  			struct xfs_ifork *ifp, xfs_fileoff_t *end,
> -			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
> -
> -bool		xfs_iext_get_extent(struct xfs_ifork *ifp, xfs_extnum_t idx,
> +			struct xfs_iext_cursor *cur,
> +			struct xfs_bmbt_irec *gotp);
> +bool		xfs_iext_get_extent(struct xfs_ifork *ifp,
> +			struct xfs_iext_cursor *cur,
>  			struct xfs_bmbt_irec *gotp);
>  void		xfs_iext_update_extent(struct xfs_inode *ip, int state,
> -			xfs_extnum_t idx, struct xfs_bmbt_irec *gotp);
> +			struct xfs_iext_cursor *cur,
> +			struct xfs_bmbt_irec *gotp);
> +
> +static inline void xfs_iext_first(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur)
> +{
> +	cur->idx = 0;
> +}
> +
> +static inline void xfs_iext_last(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur)
> +{
> +	cur->idx = xfs_iext_count(ifp) - 1;
> +}
> +
> +static inline void xfs_iext_next(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur)
> +{
> +	cur->idx++;
> +}
> +
> +static inline void xfs_iext_prev(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur)
> +{
> +	cur->idx--;
> +}
> +
> +static inline bool xfs_iext_next_extent(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
> +{
> +	xfs_iext_next(ifp, cur);
> +	return xfs_iext_get_extent(ifp, cur, gotp);
> +}
> +
> +static inline bool xfs_iext_prev_extent(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
> +{
> +	xfs_iext_prev(ifp, cur);
> +	return xfs_iext_get_extent(ifp, cur, gotp);
> +}
> +
> +/*
> + * Return the extent after cur in gotp without updating the cursor.
> + */
> +static inline bool xfs_iext_peek_next_extent(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
> +{
> +	struct xfs_iext_cursor ncur = *cur;
> +
> +	xfs_iext_next(ifp, &ncur);
> +	return xfs_iext_get_extent(ifp, &ncur, gotp);
> +}
> +
> +/*
> + * Return the extent before cur in gotp without updating the cursor.
> + */
> +static inline bool xfs_iext_peek_prev_extent(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
> +{
> +	struct xfs_iext_cursor ncur = *cur;
> +
> +	xfs_iext_prev(ifp, &ncur);
> +	return xfs_iext_get_extent(ifp, &ncur, gotp);
> +}
> +
> +#define for_each_iext(ifp, ext, got)			\
> +	for (xfs_iext_first((ifp), (ext));		\
> +	     xfs_iext_get_extent((ifp), (ext), (got));	\
> +	     xfs_iext_next((ifp), (ext)))
>  
>  extern struct kmem_zone	*xfs_ifork_zone;
>  
> diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
> index f04dbfb2f50d..5da6382bdaf1 100644
> --- a/fs/xfs/libxfs/xfs_types.h
> +++ b/fs/xfs/libxfs/xfs_types.h
> @@ -142,5 +142,8 @@ typedef uint32_t	xfs_dqid_t;
>  #define	XFS_NBWORD	(1 << XFS_NBWORDLOG)
>  #define	XFS_WORDMASK	((1 << XFS_WORDLOG) - 1)
>  
> +struct xfs_iext_cursor {
> +	xfs_extnum_t		idx;
> +};
>  
>  #endif	/* __XFS_TYPES_H__ */
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index 3c17b182616f..778b709dbd0c 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -237,7 +237,7 @@ xfs_scrub_bmap(
>  	struct xfs_inode		*ip = sc->ip;
>  	struct xfs_ifork		*ifp;
>  	xfs_fileoff_t			endoff;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  	bool				found;
>  	int				error = 0;
>  
> @@ -317,9 +317,9 @@ xfs_scrub_bmap(
>  	/* Scrub extent records. */
>  	info.lastoff = 0;
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	for (found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &irec);
> +	for (found = xfs_iext_lookup_extent(ip, ifp, 0, &ext, &irec);
>  	     found != 0;
> -	     found = xfs_iext_get_extent(ifp, ++idx, &irec)) {
> +	     found = xfs_iext_next_extent(ifp, &ext, &irec)) {
>  		if (xfs_scrub_should_terminate(sc, &error))
>  			break;
>  		if (isnullstartblock(irec.br_startblock))
> diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
> index 169fb10daaaa..46765102638c 100644
> --- a/fs/xfs/scrub/dir.c
> +++ b/fs/xfs/scrub/dir.c
> @@ -614,7 +614,7 @@ xfs_scrub_directory_blocks(
>  	xfs_fileoff_t			leaf_lblk;
>  	xfs_fileoff_t			free_lblk;
>  	xfs_fileoff_t			lblk;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  	xfs_dablk_t			dabno;
>  	bool				found;
>  	int				is_block = 0;
> @@ -639,7 +639,7 @@ xfs_scrub_directory_blocks(
>  		goto out;
>  
>  	/* Iterate all the data extents in the directory... */
> -	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
> +	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
>  	while (found) {
>  		/* Block directories only have a single block at offset 0. */
>  		if (is_block &&
> @@ -676,17 +676,17 @@ xfs_scrub_directory_blocks(
>  		}
>  		dabno = got.br_startoff + got.br_blockcount;
>  		lblk = roundup(dabno, args.geo->fsbcount);
> -		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
> +		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
>  	}
>  
>  	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
>  		goto out;
>  
>  	/* Look for a leaf1 block, which has free info. */
> -	if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &idx, &got) &&
> +	if (xfs_iext_lookup_extent(sc->ip, ifp, leaf_lblk, &ext, &got) &&
>  	    got.br_startoff == leaf_lblk &&
>  	    got.br_blockcount == args.geo->fsbcount &&
> -	    !xfs_iext_get_extent(ifp, ++idx, &got)) {
> +	    !xfs_iext_next_extent(ifp, &ext, &got)) {
>  		if (is_block) {
>  			xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
>  			goto out;
> @@ -702,7 +702,7 @@ xfs_scrub_directory_blocks(
>  
>  	/* Scan for free blocks */
>  	lblk = free_lblk;
> -	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
> +	found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
>  	while (found) {
>  		/*
>  		 * Dirs can't have blocks mapped above 2^32.
> @@ -740,7 +740,7 @@ xfs_scrub_directory_blocks(
>  		}
>  		dabno = got.br_startoff + got.br_blockcount;
>  		lblk = roundup(dabno, args.geo->fsbcount);
> -		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &idx, &got);
> +		found = xfs_iext_lookup_extent(sc->ip, ifp, lblk, &ext, &got);
>  	}
>  out:
>  	return error;
> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index 170b74c7f2d5..b6b954d5cf54 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -229,15 +229,17 @@ xfs_bmap_count_leaves(
>  	struct xfs_ifork	*ifp,
>  	xfs_filblks_t		*count)
>  {
> +	struct xfs_iext_cursor	ext;
>  	struct xfs_bmbt_irec	got;
> -	xfs_extnum_t		numrecs = 0, i = 0;
> +	xfs_extnum_t		numrecs = 0;
>  
> -	while (xfs_iext_get_extent(ifp, i++, &got)) {
> +	for_each_iext(ifp, &ext, &got) {
>  		if (!isnullstartblock(got.br_startblock)) {
>  			*count += got.br_blockcount;
>  			numrecs++;
>  		}
>  	}
> +
>  	return numrecs;
>  }
>  
> @@ -525,7 +527,7 @@ xfs_getbmap(
>  	struct xfs_ifork	*ifp;
>  	struct xfs_bmbt_irec	got, rec;
>  	xfs_filblks_t		len;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  
>  	if (bmv->bmv_iflags & ~BMV_IF_VALID)
>  		return -EINVAL;
> @@ -629,7 +631,7 @@ xfs_getbmap(
>  			goto out_unlock_ilock;
>  	}
>  
> -	if (!xfs_iext_lookup_extent(ip, ifp, bno, &idx, &got)) {
> +	if (!xfs_iext_lookup_extent(ip, ifp, bno, &ext, &got)) {
>  		/*
>  		 * Report a whole-file hole if the delalloc flag is set to
>  		 * stay compatible with the old implementation.
> @@ -668,7 +670,7 @@ xfs_getbmap(
>  				goto out_unlock_ilock;
>  		} while (xfs_getbmap_next_rec(&rec, bno));
>  
> -		if (!xfs_iext_get_extent(ifp, ++idx, &got)) {
> +		if (!xfs_iext_next_extent(ifp, &ext, &got)) {
>  			xfs_fileoff_t	end = XFS_B_TO_FSB(mp, XFS_ISIZE(ip));
>  
>  			out[bmv->bmv_entries - 1].bmv_oflags |= BMV_OF_LAST;
> diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c
> index 238e3650a9d2..ad54fd775bda 100644
> --- a/fs/xfs/xfs_dir2_readdir.c
> +++ b/fs/xfs/xfs_dir2_readdir.c
> @@ -266,7 +266,7 @@ xfs_dir2_leaf_readbuf(
>  	xfs_dablk_t		next_ra;
>  	xfs_dablk_t		map_off;
>  	xfs_dablk_t		last_da;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  	int			ra_want;
>  	int			error = 0;
>  
> @@ -283,7 +283,7 @@ xfs_dir2_leaf_readbuf(
>  	 */
>  	last_da = xfs_dir2_byte_to_da(geo, XFS_DIR2_LEAF_OFFSET);
>  	map_off = xfs_dir2_db_to_da(geo, xfs_dir2_byte_to_db(geo, *cur_off));
> -	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &idx, &map))
> +	if (!xfs_iext_lookup_extent(dp, ifp, map_off, &ext, &map))
>  		goto out;
>  	if (map.br_startoff >= last_da)
>  		goto out;
> @@ -311,7 +311,7 @@ xfs_dir2_leaf_readbuf(
>  	if (next_ra >= last_da)
>  		goto out_no_ra;
>  	if (map.br_blockcount < geo->fsbcount &&
> -	    !xfs_iext_get_extent(ifp, ++idx, &map))
> +	    !xfs_iext_next_extent(ifp, &ext, &map))
>  		goto out_no_ra;
>  	if (map.br_startoff >= last_da)
>  		goto out_no_ra;
> @@ -334,7 +334,7 @@ xfs_dir2_leaf_readbuf(
>  			ra_want -= geo->fsbcount;
>  			next_ra += geo->fsbcount;
>  		}
> -		if (!xfs_iext_get_extent(ifp, ++idx, &map)) {
> +		if (!xfs_iext_next_extent(ifp, &ext, &map)) {
>  			*ra_blk = last_da;
>  			break;
>  		}
> diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
> index cd82429d8df7..8338b894d54f 100644
> --- a/fs/xfs/xfs_dquot.c
> +++ b/fs/xfs/xfs_dquot.c
> @@ -703,7 +703,7 @@ xfs_dq_get_next_id(
>  	xfs_dqid_t		next_id = *id + 1; /* simple advance */
>  	uint			lock_flags;
>  	struct xfs_bmbt_irec	got;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	cur;
>  	xfs_fsblock_t		start;
>  	int			error = 0;
>  
> @@ -727,7 +727,7 @@ xfs_dq_get_next_id(
>  			return error;
>  	}
>  
> -	if (xfs_iext_lookup_extent(quotip, &quotip->i_df, start, &idx, &got)) {
> +	if (xfs_iext_lookup_extent(quotip, &quotip->i_df, start, &cur, &got)) {
>  		/* contiguous chunk, bump startoff for the id calculation */
>  		if (got.br_startoff < start)
>  			got.br_startoff = start;
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index f179bdf1644d..046ade883611 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -389,7 +389,7 @@ xfs_iomap_prealloc_size(
>  	struct xfs_inode	*ip,
>  	loff_t			offset,
>  	loff_t			count,
> -	xfs_extnum_t		idx)
> +	struct xfs_iext_cursor	*ext)
>  {
>  	struct xfs_mount	*mp = ip->i_mount;
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
> @@ -414,7 +414,7 @@ xfs_iomap_prealloc_size(
>  	 */
>  	if ((mp->m_flags & XFS_MOUNT_DFLT_IOSIZE) ||
>  	    XFS_ISIZE(ip) < XFS_FSB_TO_B(mp, mp->m_dalign) ||
> -	    !xfs_iext_get_extent(ifp, idx - 1, &prev) ||
> +	    !xfs_iext_peek_prev_extent(ifp, ext, &prev) ||
>  	    prev.br_startoff + prev.br_blockcount < offset_fsb)
>  		return mp->m_writeio_blocks;
>  
> @@ -532,7 +532,7 @@ xfs_file_iomap_begin_delay(
>  	xfs_fileoff_t		end_fsb;
>  	int			error = 0, eof = 0;
>  	struct xfs_bmbt_irec	got;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  	xfs_fsblock_t		prealloc_blocks = 0;
>  
>  	ASSERT(!XFS_IS_REALTIME_INODE(ip));
> @@ -557,7 +557,7 @@ xfs_file_iomap_begin_delay(
>  			goto out_unlock;
>  	}
>  
> -	eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
> +	eof = !xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got);
>  	if (!eof && got.br_startoff <= offset_fsb) {
>  		if (xfs_is_reflink_inode(ip)) {
>  			bool		shared;
> @@ -591,7 +591,8 @@ xfs_file_iomap_begin_delay(
>  	end_fsb = min(XFS_B_TO_FSB(mp, offset + count), maxbytes_fsb);
>  
>  	if (eof) {
> -		prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count, idx);
> +		prealloc_blocks = xfs_iomap_prealloc_size(ip, offset, count,
> +				&ext);
>  		if (prealloc_blocks) {
>  			xfs_extlen_t	align;
>  			xfs_off_t	end_offset;
> @@ -613,7 +614,7 @@ xfs_file_iomap_begin_delay(
>  
>  retry:
>  	error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
> -			end_fsb - offset_fsb, prealloc_blocks, &got, &idx, eof);
> +			end_fsb - offset_fsb, prealloc_blocks, &got, &ext, eof);
>  	switch (error) {
>  	case 0:
>  		break;
> diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
> index 1205747e1409..cadf0ff68003 100644
> --- a/fs/xfs/xfs_reflink.c
> +++ b/fs/xfs/xfs_reflink.c
> @@ -273,7 +273,7 @@ xfs_reflink_reserve_cow(
>  	struct xfs_bmbt_irec	got;
>  	int			error = 0;
>  	bool			eof = false, trimmed;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  
>  	/*
>  	 * Search the COW fork extent list first.  This serves two purposes:
> @@ -284,7 +284,7 @@ xfs_reflink_reserve_cow(
>  	 * tree.
>  	 */
>  
> -	if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &idx, &got))
> +	if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &ext, &got))
>  		eof = true;
>  	if (!eof && got.br_startoff <= imap->br_startoff) {
>  		trace_xfs_reflink_cow_found(ip, imap);
> @@ -312,7 +312,7 @@ xfs_reflink_reserve_cow(
>  		return error;
>  
>  	error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, imap->br_startoff,
> -			imap->br_blockcount, 0, &got, &idx, eof);
> +			imap->br_blockcount, 0, &got, &ext, eof);
>  	if (error == -ENOSPC || error == -EDQUOT)
>  		trace_xfs_reflink_cow_enospc(ip, imap);
>  	if (error)
> @@ -359,16 +359,16 @@ xfs_reflink_convert_cow(
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
>  	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
>  	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + count);
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  	bool			found;
>  	int			error = 0;
>  
>  	xfs_ilock(ip, XFS_ILOCK_EXCL);
>  
>  	/* Convert all the extents to real from unwritten. */
> -	for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got);
> +	for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got);
>  	     found && got.br_startoff < end_fsb;
> -	     found = xfs_iext_get_extent(ifp, ++idx, &got)) {
> +	     found = xfs_iext_next_extent(ifp, &ext, &got)) {
>  		error = xfs_reflink_convert_cow_extent(ip, &got, offset_fsb,
>  				end_fsb - offset_fsb, &dfops);
>  		if (error)
> @@ -399,7 +399,7 @@ xfs_reflink_allocate_cow(
>  	bool			trimmed;
>  	xfs_filblks_t		resaligned;
>  	xfs_extlen_t		resblks = 0;
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_cursor	ext;
>  
>  retry:
>  	ASSERT(xfs_is_reflink_inode(ip));
> @@ -409,7 +409,7 @@ xfs_reflink_allocate_cow(
>  	 * Even if the extent is not shared we might have a preallocation for
>  	 * it in the COW fork.  If so use it.
>  	 */
> -	if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &idx, &got) &&
> +	if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &ext, &got) &&
>  	    got.br_startoff <= offset_fsb) {
>  		*shared = true;
>  
> @@ -496,13 +496,13 @@ xfs_reflink_find_cow_mapping(
>  	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
>  	xfs_fileoff_t			offset_fsb;
>  	struct xfs_bmbt_irec		got;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  
>  	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
>  	ASSERT(xfs_is_reflink_inode(ip));
>  
>  	offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
> -	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
> +	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
>  		return false;
>  	if (got.br_startoff > offset_fsb)
>  		return false;
> @@ -524,18 +524,18 @@ xfs_reflink_trim_irec_to_next_cow(
>  {
>  	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
>  	struct xfs_bmbt_irec		got;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  
>  	if (!xfs_is_reflink_inode(ip))
>  		return;
>  
>  	/* Find the extent in the CoW fork. */
> -	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
> +	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
>  		return;
>  
>  	/* This is the extent before; try sliding up one. */
>  	if (got.br_startoff < offset_fsb) {
> -		if (!xfs_iext_get_extent(ifp, idx + 1, &got))
> +		if (!xfs_iext_next_extent(ifp, &ext, &got))
>  			return;
>  	}
>  
> @@ -562,14 +562,14 @@ xfs_reflink_cancel_cow_blocks(
>  {
>  	struct xfs_ifork		*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
>  	struct xfs_bmbt_irec		got, del;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  	xfs_fsblock_t			firstfsb;
>  	struct xfs_defer_ops		dfops;
>  	int				error = 0;
>  
>  	if (!xfs_is_reflink_inode(ip))
>  		return 0;
> -	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
> +	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
>  		return 0;
>  
>  	while (got.br_startoff < end_fsb) {
> @@ -579,7 +579,7 @@ xfs_reflink_cancel_cow_blocks(
>  
>  		if (isnullstartblock(del.br_startblock)) {
>  			error = xfs_bmap_del_extent_delay(ip, XFS_COW_FORK,
> -					&idx, &got, &del);
> +					&ext, &got, &del);
>  			if (error)
>  				break;
>  		} else if (del.br_state == XFS_EXT_UNWRITTEN || cancel_real) {
> @@ -610,10 +610,10 @@ xfs_reflink_cancel_cow_blocks(
>  			}
>  
>  			/* Remove the mapping from the CoW fork. */
> -			xfs_bmap_del_extent_cow(ip, &idx, &got, &del);
> +			xfs_bmap_del_extent_cow(ip, &ext, &got, &del);
>  		}
>  
> -		if (!xfs_iext_get_extent(ifp, ++idx, &got))
> +		if (!xfs_iext_next_extent(ifp, &ext, &got))
>  			break;
>  	}
>  
> @@ -698,7 +698,7 @@ xfs_reflink_end_cow(
>  	int				error;
>  	unsigned int			resblks;
>  	xfs_filblks_t			rlen;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  
>  	trace_xfs_reflink_end_cow(ip, offset, count);
>  
> @@ -738,7 +738,7 @@ xfs_reflink_end_cow(
>  	 * left by the time I/O completes for the loser of the race.  In that
>  	 * case we are done.
>  	 */
> -	if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &idx, &got))
> +	if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &ext, &got))
>  		goto out_cancel;
>  
>  	/* Walk backwards until we're out of the I/O range... */
> @@ -746,9 +746,9 @@ xfs_reflink_end_cow(
>  		del = got;
>  		xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb);
>  
> -		/* Extent delete may have bumped idx forward */
> +		/* Extent delete may have bumped ext forward */
>  		if (!del.br_blockcount) {
> -			idx--;
> +			xfs_iext_prev(ifp, &ext);
>  			goto next_extent;
>  		}
>  
> @@ -760,7 +760,7 @@ xfs_reflink_end_cow(
>  		 * allocated but have not yet been involved in a write.
>  		 */
>  		if (got.br_state == XFS_EXT_UNWRITTEN) {
> -			idx--;
> +			xfs_iext_prev(ifp, &ext);
>  			goto next_extent;
>  		}
>  
> @@ -791,14 +791,14 @@ xfs_reflink_end_cow(
>  			goto out_defer;
>  
>  		/* Remove the mapping from the CoW fork. */
> -		xfs_bmap_del_extent_cow(ip, &idx, &got, &del);
> +		xfs_bmap_del_extent_cow(ip, &ext, &got, &del);
>  
>  		xfs_defer_ijoin(&dfops, ip);
>  		error = xfs_defer_finish(&tp, &dfops);
>  		if (error)
>  			goto out_defer;
>  next_extent:
> -		if (!xfs_iext_get_extent(ifp, idx, &got))
> +		if (!xfs_iext_get_extent(ifp, &ext, &got))
>  			break;
>  	}
>  
> @@ -1428,7 +1428,7 @@ xfs_reflink_inode_has_shared_extents(
>  	xfs_extlen_t			aglen;
>  	xfs_agblock_t			rbno;
>  	xfs_extlen_t			rlen;
> -	xfs_extnum_t			idx;
> +	struct xfs_iext_cursor		ext;
>  	bool				found;
>  	int				error;
>  
> @@ -1440,7 +1440,7 @@ xfs_reflink_inode_has_shared_extents(
>  	}
>  
>  	*has_shared = false;
> -	found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &got);
> +	found = xfs_iext_lookup_extent(ip, ifp, 0, &ext, &got);
>  	while (found) {
>  		if (isnullstartblock(got.br_startblock) ||
>  		    got.br_state != XFS_EXT_NORM)
> @@ -1459,7 +1459,7 @@ xfs_reflink_inode_has_shared_extents(
>  			return 0;
>  		}
>  next:
> -		found = xfs_iext_get_extent(ifp, ++idx, &got);
> +		found = xfs_iext_next_extent(ifp, &ext, &got);
>  	}
>  
>  	return 0;
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index 665ef6cca90c..667bfce802cd 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -258,9 +258,9 @@ TRACE_EVENT(xfs_iext_insert,
>  );
>  
>  DECLARE_EVENT_CLASS(xfs_bmap_class,
> -	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx, int state,
> +	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state,
>  		 unsigned long caller_ip),
> -	TP_ARGS(ip, idx, state, caller_ip),
> +	TP_ARGS(ip, cur, state, caller_ip),
>  	TP_STRUCT__entry(
>  		__field(dev_t, dev)
>  		__field(xfs_ino_t, ino)
> @@ -277,10 +277,10 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  		struct xfs_bmbt_irec	r;
>  
>  		ifp = xfs_iext_state_to_fork(ip, state);
> -		xfs_iext_get_extent(ifp, idx, &r);
> +		xfs_iext_get_extent(ifp, cur, &r);
>  		__entry->dev = VFS_I(ip)->i_sb->s_dev;
>  		__entry->ino = ip->i_ino;
> -		__entry->idx = idx;
> +		__entry->idx = cur->idx;
>  		__entry->startoff = r.br_startoff;
>  		__entry->startblock = r.br_startblock;
>  		__entry->blockcount = r.br_blockcount;
> @@ -303,9 +303,9 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  
>  #define DEFINE_BMAP_EVENT(name) \
>  DEFINE_EVENT(xfs_bmap_class, name, \
> -	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx, int state, \
> +	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state, \
>  		 unsigned long caller_ip), \
> -	TP_ARGS(ip, idx, state, caller_ip))
> +	TP_ARGS(ip, cur, state, caller_ip))
>  DEFINE_BMAP_EVENT(xfs_iext_remove);
>  DEFINE_BMAP_EVENT(xfs_bmap_pre_update);
>  DEFINE_BMAP_EVENT(xfs_bmap_post_update);
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 13/18] xfs: iterate backwards in xfs_reflink_cancel_cow_blocks
  2017-10-31 14:22 ` [PATCH 13/18] xfs: iterate backwards in xfs_reflink_cancel_cow_blocks Christoph Hellwig
@ 2017-10-31 22:10   ` Darrick J. Wong
  0 siblings, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 22:10 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:25PM +0200, Christoph Hellwig wrote:
> This follow the iteration oder for extent deletion in both the regular
                            ^^^^
Jeez man I didn't think the code smelled /that/ bad. :P

"order", I'm assuming. :)

> reflink I/O completion path and normal block truncation, and makes
> implementing the new incore extent list easier.

"Make consistent the extent iteration order for deleting extents in both
the regular reflink IO completion path and regular block truncation, so
that implementing the new incore extent map is easier." ?

(Eh, the language is still a bit tortured...)

--D

> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/xfs_reflink.c | 16 ++++++++++++----
>  1 file changed, 12 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
> index cadf0ff68003..43bec0def51d 100644
> --- a/fs/xfs/xfs_reflink.c
> +++ b/fs/xfs/xfs_reflink.c
> @@ -569,12 +569,20 @@ xfs_reflink_cancel_cow_blocks(
>  
>  	if (!xfs_is_reflink_inode(ip))
>  		return 0;
> -	if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got))
> +	if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &ext, &got))
>  		return 0;
>  
> -	while (got.br_startoff < end_fsb) {
> +	/* Walk backwards until we're out of the I/O range... */
> +	while (got.br_startoff + got.br_blockcount > offset_fsb) {
>  		del = got;
>  		xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb);
> +
> +		/* Extent delete may have bumped ext forward */
> +		if (!del.br_blockcount) {
> +			xfs_iext_prev(ifp, &ext);
> +			goto next_extent;
> +		}
> +
>  		trace_xfs_reflink_cancel_cow(ip, &del);
>  
>  		if (isnullstartblock(del.br_startblock)) {
> @@ -612,8 +620,8 @@ xfs_reflink_cancel_cow_blocks(
>  			/* Remove the mapping from the CoW fork. */
>  			xfs_bmap_del_extent_cow(ip, &ext, &got, &del);
>  		}
> -
> -		if (!xfs_iext_next_extent(ifp, &ext, &got))
> +next_extent:
> +		if (!xfs_iext_get_extent(ifp, &ext, &got))
>  			break;
>  	}
>  
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow
  2017-10-31 14:22 ` [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow Christoph Hellwig
@ 2017-10-31 22:20   ` Darrick J. Wong
  2017-11-02 18:56     ` Christoph Hellwig
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 22:20 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:26PM +0200, Christoph Hellwig wrote:
> Instead of looking up extents to convert and calling xfs_bmapi_write on
> each of them just let xfs_bmapi_write handle the full range.  To make
> this robust add a new XFS_BMAPI_CONVERT_ONLY that only converts ranges
> and never allocates blocks.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c |  3 ++-
>  fs/xfs/libxfs/xfs_bmap.h |  3 +++
>  fs/xfs/xfs_reflink.c     | 29 +++++++++++------------------
>  3 files changed, 16 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 453dc1ae76ab..e020bd3f8717 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -4330,7 +4330,8 @@ xfs_bmapi_write(
>  		 * First, deal with the hole before the allocated space
>  		 * that we found, if any.
>  		 */
> -		if (need_alloc || wasdelay) {
> +		if ((need_alloc || wasdelay) &&
> +		    !(flags & XFS_BMAPI_CONVERT_ONLY)) {
>  			bma.eof = eof;
>  			bma.conv = !!(flags & XFS_BMAPI_CONVERT);
>  			bma.wasdel = wasdelay;
> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index 195f335f4615..fba60ed7aea5 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -113,6 +113,9 @@ struct xfs_extent_free_item
>  /* Only convert delalloc space, don't allocate entirely new extents */
>  #define XFS_BMAPI_DELALLOC	0x400
>  
> +/* Only convert unwritten extents, don't allocate new hotels */

New ... blocks?

> +#define XFS_BMAPI_CONVERT_ONLY	0x800

I wonder if this and XFS_BMAPI_DELALLOC could be solved with a single
flag that means "don't fill in any holes; only touch pre-existing
extents" ?

(Though I guess Dave is still working on getting /rid/ of BMAPI_DELALLOC
so maybe that doesn't matter...)

>  #define XFS_BMAPI_FLAGS \
>  	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
>  	{ XFS_BMAPI_METADATA,	"METADATA" }, \

If you do end up adding a new BMAPI flag, this needs updating so the
tracepoints keep working.

--D

> diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
> index 43bec0def51d..99c5d4dbc5d3 100644
> --- a/fs/xfs/xfs_reflink.c
> +++ b/fs/xfs/xfs_reflink.c
> @@ -353,29 +353,22 @@ xfs_reflink_convert_cow(
>  	xfs_off_t		offset,
>  	xfs_off_t		count)
>  {
> -	struct xfs_bmbt_irec	got;
> -	struct xfs_defer_ops	dfops;
>  	struct xfs_mount	*mp = ip->i_mount;
> -	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
>  	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
>  	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + count);
> -	struct xfs_iext_cursor	ext;
> -	bool			found;
> -	int			error = 0;
> -
> -	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +	xfs_filblks_t		count_fsb = end_fsb - offset_fsb;
> +	struct xfs_bmbt_irec	imap;
> +	struct xfs_defer_ops	dfops;
> +	xfs_fsblock_t		first_block = NULLFSBLOCK;
> +	int			nimaps = 1, error = 0;
>  
> -	/* Convert all the extents to real from unwritten. */
> -	for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &ext, &got);
> -	     found && got.br_startoff < end_fsb;
> -	     found = xfs_iext_next_extent(ifp, &ext, &got)) {
> -		error = xfs_reflink_convert_cow_extent(ip, &got, offset_fsb,
> -				end_fsb - offset_fsb, &dfops);
> -		if (error)
> -			break;
> -	}
> +	ASSERT(count != 0);
>  
> -	/* Finish up. */
> +	xfs_ilock(ip, XFS_ILOCK_EXCL);
> +	error = xfs_bmapi_write(NULL, ip, offset_fsb, count_fsb,
> +			XFS_BMAPI_COWFORK | XFS_BMAPI_CONVERT |
> +			XFS_BMAPI_CONVERT_ONLY, &first_block, 0, &imap, &nimaps,
> +			&dfops);
>  	xfs_iunlock(ip, XFS_ILOCK_EXCL);
>  	return error;
>  }
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork
  2017-10-31 14:22 ` [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork Christoph Hellwig
@ 2017-10-31 22:35   ` Darrick J. Wong
  2017-11-02 18:57     ` Christoph Hellwig
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 22:35 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:27PM +0200, Christoph Hellwig wrote:
> Supporting a small bit of data inside the inode fork blows up the fork size
> a lot, removing the 32 bytes of inline data halves the effective size of
> the inode fork (and it still has a lot of unused padding left), and the
> performance of a single kmalloc doesn't show up compared to the size to read
> an inode or create one.

Do you see any secondary effects, such as increased slab fragmentation
because of the extra kmem_allocs?  In general I think this should be ok,
I just worry slightly that whatever reason we had for having
if_inline_data is still around and will blow up in weird ways if we get
rid of it.

(I will go play with scrub on a big filesystem to see if it runs
noticeably slower or goes whacky.)

Code-wise,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> 
> It also simplifies the fork management code a lot.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_inode_fork.c | 185 +++--------------------------------------
>  fs/xfs/libxfs/xfs_inode_fork.h |  11 ---
>  fs/xfs/xfs_bmap_util.c         |  15 ----
>  3 files changed, 13 insertions(+), 198 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index c9e10d4818b7..5ac341d2b093 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -269,19 +269,14 @@ xfs_init_local_fork(
>  	if (zero_terminate)
>  		mem_size++;
>  
> -	if (size == 0)
> -		ifp->if_u1.if_data = NULL;
> -	else if (mem_size <= sizeof(ifp->if_u2.if_inline_data))
> -		ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
> -	else {
> +	if (size) {
>  		real_size = roundup(mem_size, 4);
>  		ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
> -	}
> -
> -	if (size) {
>  		memcpy(ifp->if_u1.if_data, data, size);
>  		if (zero_terminate)
>  			ifp->if_u1.if_data[size] = '\0';
> +	} else {
> +		ifp->if_u1.if_data = NULL;
>  	}
>  
>  	ifp->if_bytes = size;
> @@ -292,13 +287,6 @@ xfs_init_local_fork(
>  
>  /*
>   * The file is in-lined in the on-disk inode.
> - * If it fits into if_inline_data, then copy
> - * it there, otherwise allocate a buffer for it
> - * and copy the data there.  Either way, set
> - * if_data to point at the data.
> - * If we allocate a buffer for the data, make
> - * sure that its size is a multiple of 4 and
> - * record the real size in i_real_bytes.
>   */
>  STATIC int
>  xfs_iformat_local(
> @@ -328,9 +316,7 @@ xfs_iformat_local(
>  
>  /*
>   * The file consists of a set of extents all of which fit into the on-disk
> - * inode.  If there are few enough extents to fit into the if_inline_ext, then
> - * copy them there.  Otherwise allocate a buffer for them and copy them into it.
> - * Either way, set if_extents to point at the extents.
> + * inode.
>   */
>  STATIC int
>  xfs_iformat_extents(
> @@ -362,8 +348,6 @@ xfs_iformat_extents(
>  	ifp->if_real_bytes = 0;
>  	if (nex == 0)
>  		ifp->if_u1.if_extents = NULL;
> -	else if (nex <= XFS_INLINE_EXTS)
> -		ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
>  	else
>  		xfs_iext_add(ifp, 0, nex);
>  
> @@ -618,26 +602,9 @@ xfs_idata_realloc(
>  	ASSERT(new_size >= 0);
>  
>  	if (new_size == 0) {
> -		if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
> -			kmem_free(ifp->if_u1.if_data);
> -		}
> +		kmem_free(ifp->if_u1.if_data);
>  		ifp->if_u1.if_data = NULL;
>  		real_size = 0;
> -	} else if (new_size <= sizeof(ifp->if_u2.if_inline_data)) {
> -		/*
> -		 * If the valid extents/data can fit in if_inline_ext/data,
> -		 * copy them from the malloc'd vector and free it.
> -		 */
> -		if (ifp->if_u1.if_data == NULL) {
> -			ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
> -		} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
> -			ASSERT(ifp->if_real_bytes != 0);
> -			memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
> -			      new_size);
> -			kmem_free(ifp->if_u1.if_data);
> -			ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
> -		}
> -		real_size = 0;
>  	} else {
>  		/*
>  		 * Stuck with malloc/realloc.
> @@ -651,7 +618,7 @@ xfs_idata_realloc(
>  			ASSERT(ifp->if_real_bytes == 0);
>  			ifp->if_u1.if_data = kmem_alloc(real_size,
>  							KM_SLEEP | KM_NOFS);
> -		} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
> +		} else {
>  			/*
>  			 * Only do the realloc if the underlying size
>  			 * is really changing.
> @@ -662,12 +629,6 @@ xfs_idata_realloc(
>  							real_size,
>  							KM_SLEEP | KM_NOFS);
>  			}
> -		} else {
> -			ASSERT(ifp->if_real_bytes == 0);
> -			ifp->if_u1.if_data = kmem_alloc(real_size,
> -							KM_SLEEP | KM_NOFS);
> -			memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
> -				ifp->if_bytes);
>  		}
>  	}
>  	ifp->if_real_bytes = real_size;
> @@ -695,8 +656,7 @@ xfs_idestroy_fork(
>  	 * so check and free it up if we do.
>  	 */
>  	if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
> -		if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
> -		    (ifp->if_u1.if_data != NULL)) {
> +		if (ifp->if_u1.if_data != NULL) {
>  			ASSERT(ifp->if_real_bytes != 0);
>  			kmem_free(ifp->if_u1.if_data);
>  			ifp->if_u1.if_data = NULL;
> @@ -704,13 +664,11 @@ xfs_idestroy_fork(
>  		}
>  	} else if ((ifp->if_flags & XFS_IFEXTENTS) &&
>  		   ((ifp->if_flags & XFS_IFEXTIREC) ||
> -		    ((ifp->if_u1.if_extents != NULL) &&
> -		     (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)))) {
> +		    (ifp->if_u1.if_extents != NULL))) {
>  		ASSERT(ifp->if_real_bytes != 0);
>  		xfs_iext_destroy(ifp);
>  	}
> -	ASSERT(ifp->if_u1.if_extents == NULL ||
> -	       ifp->if_u1.if_extents == ifp->if_u2.if_inline_ext);
> +	ASSERT(ifp->if_u1.if_extents == NULL);
>  	ASSERT(ifp->if_real_bytes == 0);
>  	if (whichfork == XFS_ATTR_FORK) {
>  		kmem_zone_free(xfs_ifork_zone, ip->i_afp);
> @@ -943,28 +901,14 @@ xfs_iext_add(
>  	ASSERT((idx >= 0) && (idx <= nextents));
>  	byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
>  	new_size = ifp->if_bytes + byte_diff;
> +
>  	/*
> -	 * If the new number of extents (nextents + ext_diff)
> -	 * fits inside the inode, then continue to use the inline
> -	 * extent buffer.
> -	 */
> -	if (nextents + ext_diff <= XFS_INLINE_EXTS) {
> -		if (idx < nextents) {
> -			memmove(&ifp->if_u2.if_inline_ext[idx + ext_diff],
> -				&ifp->if_u2.if_inline_ext[idx],
> -				(nextents - idx) * sizeof(xfs_bmbt_rec_t));
> -			memset(&ifp->if_u2.if_inline_ext[idx], 0, byte_diff);
> -		}
> -		ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
> -		ifp->if_real_bytes = 0;
> -	}
> -	/*
> -	 * Otherwise use a linear (direct) extent list.
> +	 * Use a linear (direct) extent list.
>  	 * If the extents are currently inside the inode,
>  	 * xfs_iext_realloc_direct will switch us from
>  	 * inline to direct extent allocation mode.
>  	 */
> -	else if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
> +	if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
>  		xfs_iext_realloc_direct(ifp, new_size);
>  		if (idx < nextents) {
>  			memmove(&ifp->if_u1.if_extents[idx + ext_diff],
> @@ -1172,43 +1116,10 @@ xfs_iext_remove(
>  		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
>  	} else if (ifp->if_real_bytes) {
>  		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
> -	} else {
> -		xfs_iext_remove_inline(ifp, cur->idx, ext_diff);
>  	}
>  	ifp->if_bytes = new_size;
>  }
>  
> -/*
> - * This removes ext_diff extents from the inline buffer, beginning
> - * at extent index idx.
> - */
> -void
> -xfs_iext_remove_inline(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin removing exts */
> -	int		ext_diff)	/* number of extents to remove */
> -{
> -	int		nextents;	/* number of extents in file */
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
> -	ASSERT(idx < XFS_INLINE_EXTS);
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT(((nextents - ext_diff) > 0) &&
> -		(nextents - ext_diff) < XFS_INLINE_EXTS);
> -
> -	if (idx + ext_diff < nextents) {
> -		memmove(&ifp->if_u2.if_inline_ext[idx],
> -			&ifp->if_u2.if_inline_ext[idx + ext_diff],
> -			(nextents - (idx + ext_diff)) *
> -			 sizeof(xfs_bmbt_rec_t));
> -		memset(&ifp->if_u2.if_inline_ext[nextents - ext_diff],
> -			0, ext_diff * sizeof(xfs_bmbt_rec_t));
> -	} else {
> -		memset(&ifp->if_u2.if_inline_ext[idx], 0,
> -			ext_diff * sizeof(xfs_bmbt_rec_t));
> -	}
> -}
> -
>  /*
>   * This removes ext_diff extents from a linear (direct) extent list,
>   * beginning at extent index idx. If the extents are being removed
> @@ -1351,16 +1262,7 @@ xfs_iext_realloc_direct(
>  	/* Free extent records */
>  	if (new_size == 0) {
>  		xfs_iext_destroy(ifp);
> -	}
> -	/* Resize direct extent list and zero any new bytes */
> -	else if (ifp->if_real_bytes) {
> -		/* Check if extents will fit inside the inode */
> -		if (new_size <= XFS_INLINE_EXTS * sizeof(xfs_bmbt_rec_t)) {
> -			xfs_iext_direct_to_inline(ifp, new_size /
> -				(uint)sizeof(xfs_bmbt_rec_t));
> -			ifp->if_bytes = new_size;
> -			return;
> -		}
> +	} else {
>  		if (!is_power_of_2(new_size)){
>  			rnew_size = roundup_pow_of_two(new_size);
>  		}
> @@ -1375,63 +1277,10 @@ xfs_iext_realloc_direct(
>  				rnew_size - ifp->if_real_bytes);
>  		}
>  	}
> -	/* Switch from the inline extent buffer to a direct extent list */
> -	else {
> -		if (!is_power_of_2(new_size)) {
> -			rnew_size = roundup_pow_of_two(new_size);
> -		}
> -		xfs_iext_inline_to_direct(ifp, rnew_size);
> -	}
>  	ifp->if_real_bytes = rnew_size;
>  	ifp->if_bytes = new_size;
>  }
>  
> -/*
> - * Switch from linear (direct) extent records to inline buffer.
> - */
> -void
> -xfs_iext_direct_to_inline(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	nextents)	/* number of extents in file */
> -{
> -	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
> -	ASSERT(nextents <= XFS_INLINE_EXTS);
> -	/*
> -	 * The inline buffer was zeroed when we switched
> -	 * from inline to direct extent allocation mode,
> -	 * so we don't need to clear it here.
> -	 */
> -	memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
> -		nextents * sizeof(xfs_bmbt_rec_t));
> -	kmem_free(ifp->if_u1.if_extents);
> -	ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
> -	ifp->if_real_bytes = 0;
> -}
> -
> -/*
> - * Switch from inline buffer to linear (direct) extent records.
> - * new_size should already be rounded up to the next power of 2
> - * by the caller (when appropriate), so use new_size as it is.
> - * However, since new_size may be rounded up, we can't update
> - * if_bytes here. It is the caller's responsibility to update
> - * if_bytes upon return.
> - */
> -void
> -xfs_iext_inline_to_direct(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		new_size)	/* number of extents in file */
> -{
> -	ifp->if_u1.if_extents = kmem_alloc(new_size, KM_NOFS);
> -	memset(ifp->if_u1.if_extents, 0, new_size);
> -	if (ifp->if_bytes) {
> -		memcpy(ifp->if_u1.if_extents, ifp->if_u2.if_inline_ext,
> -			ifp->if_bytes);
> -		memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
> -			sizeof(xfs_bmbt_rec_t));
> -	}
> -	ifp->if_real_bytes = new_size;
> -}
> -
>  /*
>   * Resize an extent indirection array to new_size bytes.
>   */
> @@ -1511,9 +1360,6 @@ xfs_iext_destroy(
>  		xfs_iext_irec_remove_all(ifp);
>  	} else if (ifp->if_real_bytes) {
>  		kmem_free(ifp->if_u1.if_extents);
> -	} else if (ifp->if_bytes) {
> -		memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
> -			sizeof(xfs_bmbt_rec_t));
>  	}
>  	ifp->if_u1.if_extents = NULL;
>  	ifp->if_real_bytes = 0;
> @@ -1708,8 +1554,6 @@ xfs_iext_irec_init(
>  
>  	if (nextents == 0) {
>  		ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
> -	} else if (!ifp->if_real_bytes) {
> -		xfs_iext_inline_to_direct(ifp, XFS_IEXT_BUFSZ);
>  	} else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
>  		xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
>  	}
> @@ -1829,9 +1673,6 @@ xfs_iext_irec_compact(
>  
>  	if (nextents == 0) {
>  		xfs_iext_destroy(ifp);
> -	} else if (nextents <= XFS_INLINE_EXTS) {
> -		xfs_iext_indirect_to_direct(ifp);
> -		xfs_iext_direct_to_inline(ifp, nextents);
>  	} else if (nextents <= XFS_LINEAR_EXTS) {
>  		xfs_iext_indirect_to_direct(ifp);
>  	} else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index dc347dd9dc78..508f13784334 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
> @@ -51,8 +51,6 @@ typedef struct xfs_ext_irec {
>   */
>  #define	XFS_IEXT_BUFSZ		4096
>  #define	XFS_LINEAR_EXTS		(XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t))
> -#define	XFS_INLINE_EXTS		2
> -#define	XFS_INLINE_DATA		32
>  typedef struct xfs_ifork {
>  	int			if_bytes;	/* bytes in if_u1 */
>  	int			if_real_bytes;	/* bytes allocated in if_u1 */
> @@ -64,12 +62,6 @@ typedef struct xfs_ifork {
>  		xfs_ext_irec_t	*if_ext_irec;	/* irec map file exts */
>  		char		*if_data;	/* inline file data */
>  	} if_u1;
> -	union {
> -		xfs_bmbt_rec_host_t if_inline_ext[XFS_INLINE_EXTS];
> -						/* very small file extents */
> -		char		if_inline_data[XFS_INLINE_DATA];
> -						/* very small file data */
> -	} if_u2;
>  } xfs_ifork_t;
>  
>  /*
> @@ -158,12 +150,9 @@ void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
>  					    xfs_extnum_t, int);
>  void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
>  			int, int);
> -void		xfs_iext_remove_inline(struct xfs_ifork *, xfs_extnum_t, int);
>  void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
>  void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
>  void		xfs_iext_realloc_direct(struct xfs_ifork *, int);
> -void		xfs_iext_direct_to_inline(struct xfs_ifork *, xfs_extnum_t);
> -void		xfs_iext_inline_to_direct(struct xfs_ifork *, int);
>  void		xfs_iext_destroy(struct xfs_ifork *);
>  struct xfs_bmbt_rec_host *
>  		xfs_iext_bno_to_ext(struct xfs_ifork *, xfs_fileoff_t, int *);
> diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
> index b6b954d5cf54..c2f8e40a3e1f 100644
> --- a/fs/xfs/xfs_bmap_util.c
> +++ b/fs/xfs/xfs_bmap_util.c
> @@ -1709,7 +1709,6 @@ xfs_swap_extent_forks(
>  	xfs_filblks_t		aforkblks = 0;
>  	xfs_filblks_t		taforkblks = 0;
>  	xfs_extnum_t		junk;
> -	xfs_extnum_t		nextents;
>  	uint64_t		tmp;
>  	int			error;
>  
> @@ -1784,13 +1783,6 @@ xfs_swap_extent_forks(
>  
>  	switch (ip->i_d.di_format) {
>  	case XFS_DINODE_FMT_EXTENTS:
> -		/*
> -		 * If the extents fit in the inode, fix the pointer.  Otherwise
> -		 * it's already NULL or pointing to the extent.
> -		 */
> -		nextents = xfs_iext_count(&ip->i_df);
> -		if (nextents <= XFS_INLINE_EXTS)
> -			ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
>  		(*src_log_flags) |= XFS_ILOG_DEXT;
>  		break;
>  	case XFS_DINODE_FMT_BTREE:
> @@ -1802,13 +1794,6 @@ xfs_swap_extent_forks(
>  
>  	switch (tip->i_d.di_format) {
>  	case XFS_DINODE_FMT_EXTENTS:
> -		/*
> -		 * If the extents fit in the inode, fix the pointer.  Otherwise
> -		 * it's already NULL or pointing to the extent.
> -		 */
> -		nextents = xfs_iext_count(&tip->i_df);
> -		if (nextents <= XFS_INLINE_EXTS)
> -			tifp->if_u1.if_extents = tifp->if_u2.if_inline_ext;
>  		(*target_log_flags) |= XFS_ILOG_DEXT;
>  		break;
>  	case XFS_DINODE_FMT_BTREE:
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 17/18] xfs: remove the nr_extents argument to xfs_iext_insert
  2017-10-31 14:22 ` [PATCH 17/18] xfs: remove the nr_extents argument to xfs_iext_insert Christoph Hellwig
@ 2017-10-31 22:35   ` Darrick J. Wong
  0 siblings, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 22:35 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:29PM +0200, Christoph Hellwig wrote:
> We only have two places that insert 2 extents at the same time, so unroll
> the loop there.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> ---
>  fs/xfs/libxfs/xfs_bmap.c       | 31 ++++++++++++++++---------------
>  fs/xfs/libxfs/xfs_iext_tree.c  | 31 ++++++++-----------------------
>  fs/xfs/libxfs/xfs_inode_fork.c |  2 +-
>  fs/xfs/libxfs/xfs_inode_fork.h |  2 +-
>  4 files changed, 26 insertions(+), 40 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 46bda00dca79..b832dc402f40 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -900,7 +900,7 @@ xfs_bmap_local_to_extents(
>  	rec.br_blockcount = 1;
>  	rec.br_state = XFS_EXT_NORM;
>  	xfs_iext_first(ifp, &ext);
> -	xfs_iext_insert(ip, &ext, 1, &rec, 0);
> +	xfs_iext_insert(ip, &ext, &rec, 0);
>  
>  	XFS_IFORK_NEXT_SET(ip, whichfork, 1);
>  	ip->i_d.di_nblocks = 1;
> @@ -1272,7 +1272,7 @@ xfs_iread_extents(
>  				goto out_brelse;
>  			}
>  			xfs_bmbt_disk_get_all(frp, &new);
> -			xfs_iext_insert(ip, &ext, 1, &new, state);
> +			xfs_iext_insert(ip, &ext, &new, state);
>  			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
>  			xfs_iext_next(ifp, &ext);
>  		}
> @@ -1828,7 +1828,7 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_blockcount = temp;
>  		PREV.br_startblock = nullstartblock(da_new);
>  		xfs_iext_next(ifp, &bma->ext);
> -		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
> +		xfs_iext_insert(bma->ip, &bma->ext, &PREV, state);
>  		xfs_iext_prev(ifp, &bma->ext);
>  		break;
>  
> @@ -1904,7 +1904,7 @@ xfs_bmap_add_extent_delay_real(
>  
>  		PREV.br_startblock = nullstartblock(da_new);
>  		PREV.br_blockcount = temp;
> -		xfs_iext_insert(bma->ip, &bma->ext, 1, &PREV, state);
> +		xfs_iext_insert(bma->ip, &bma->ext, &PREV, state);
>  		xfs_iext_next(ifp, &bma->ext);
>  		break;
>  
> @@ -1950,9 +1950,9 @@ xfs_bmap_add_extent_delay_real(
>  					PREV.br_blockcount));
>  		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
>  
> -		/* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
>  		xfs_iext_next(ifp, &bma->ext);
> -		xfs_iext_insert(bma->ip, &bma->ext, 2, &LEFT, state);
> +		xfs_iext_insert(bma->ip, &bma->ext, &RIGHT, state);
> +		xfs_iext_insert(bma->ip, &bma->ext, &LEFT, state);
>  		(*nextents)++;
>  
>  		if (bma->cur == NULL)
> @@ -2316,7 +2316,7 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_blockcount -= new->br_blockcount;
>  
>  		xfs_iext_update_extent(ip, state, ext, &PREV);
> -		xfs_iext_insert(ip, ext, 1, new, state);
> +		xfs_iext_insert(ip, ext, new, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  		if (cur == NULL)
> @@ -2383,7 +2383,7 @@ xfs_bmap_add_extent_unwritten_real(
>  
>  		xfs_iext_update_extent(ip, state, ext, &PREV);
>  		xfs_iext_next(ifp, ext);
> -		xfs_iext_insert(ip, ext, 1, new, state);
> +		xfs_iext_insert(ip, ext, new, state);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
> @@ -2426,7 +2426,8 @@ xfs_bmap_add_extent_unwritten_real(
>  
>  		xfs_iext_update_extent(ip, state, ext, &PREV);
>  		xfs_iext_next(ifp, ext);
> -		xfs_iext_insert(ip, ext, 2, &r[0], state);
> +		xfs_iext_insert(ip, ext, &r[1], state);
> +		xfs_iext_insert(ip, ext, &r[0], state);
>  
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  				XFS_IFORK_NEXTENTS(ip, whichfork) + 2);
> @@ -2634,7 +2635,7 @@ xfs_bmap_add_extent_hole_delay(
>  		 * Insert a new entry.
>  		 */
>  		oldlen = newlen = 0;
> -		xfs_iext_insert(ip, ext, 1, new, state);
> +		xfs_iext_insert(ip, ext, new, state);
>  		break;
>  	}
>  	if (oldlen != newlen) {
> @@ -2818,7 +2819,7 @@ xfs_bmap_add_extent_hole_real(
>  		 * real allocation.
>  		 * Insert a new entry.
>  		 */
> -		xfs_iext_insert(ip, ext, 1, new, state);
> +		xfs_iext_insert(ip, ext, new, state);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  		if (cur == NULL) {
> @@ -4741,7 +4742,7 @@ xfs_bmap_del_extent_delay(
>  
>  		xfs_iext_update_extent(ip, state, ext, got);
>  		xfs_iext_next(ifp, ext);
> -		xfs_iext_insert(ip, ext, 1, &new, state);
> +		xfs_iext_insert(ip, ext, &new, state);
>  
>  		da_new = got_indlen + new_indlen - stolen;
>  		del->br_blockcount -= stolen;
> @@ -4822,7 +4823,7 @@ xfs_bmap_del_extent_cow(
>  
>  		xfs_iext_update_extent(ip, state, ext, got);
>  		xfs_iext_next(ifp, ext);
> -		xfs_iext_insert(ip, ext, 1, &new, state);
> +		xfs_iext_insert(ip, ext, &new, state);
>  		break;
>  	}
>  }
> @@ -5035,7 +5036,7 @@ xfs_bmap_del_extent_real(
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  		xfs_iext_next(ifp, ext);
> -		xfs_iext_insert(ip, ext, 1, &new, state);
> +		xfs_iext_insert(ip, ext, &new, state);
>  		break;
>  	}
>  
> @@ -5896,7 +5897,7 @@ xfs_bmap_split_extent_at(
>  
>  	/* Add new extent */
>  	xfs_iext_next(ifp, &ext);
> -	xfs_iext_insert(ip, &ext, 1, &new, 0);
> +	xfs_iext_insert(ip, &ext, &new, 0);
>  	XFS_IFORK_NEXT_SET(ip, whichfork,
>  			   XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
>  
> diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
> index acb66c0677e7..c109526e52c5 100644
> --- a/fs/xfs/libxfs/xfs_iext_tree.c
> +++ b/fs/xfs/libxfs/xfs_iext_tree.c
> @@ -627,16 +627,20 @@ xfs_iext_realloc_root(
>  	cur->leaf = new;
>  }
>  
> -static void
> -__xfs_iext_insert(
> -	struct xfs_ifork	*ifp,
> +void
> +xfs_iext_insert(
> +	struct xfs_inode	*ip,
>  	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*irec)
> +	struct xfs_bmbt_irec	*irec,
> +	int			state)
>  {
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
>  	xfs_fileoff_t		offset = irec->br_startoff;
>  	struct xfs_iext_leaf	*new = NULL;
>  	int			nr_entries, i;
>  
> +	trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
> +
>  	if (ifp->if_height == 0)
>  		xfs_iext_alloc_root(ifp, cur);
>  	else if (ifp->if_height == 1)
> @@ -664,25 +668,6 @@ __xfs_iext_insert(
>  		xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
>  }
>  
> -void
> -xfs_iext_insert(
> -	struct xfs_inode	*ip,
> -	struct xfs_iext_cursor	*cur,
> -	xfs_extnum_t		nr_extents,
> -	struct xfs_bmbt_irec	*new,
> -	int			state)
> -{
> -	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> -	int			i;
> -
> -	ASSERT(nr_extents > 0);
> -
> -	for (i = nr_extents - 1; i >= 0; i--) {
> -		__xfs_iext_insert(ifp, cur, new + i);
> -		trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
> -	}
> -}
> -
>  static struct xfs_iext_node *
>  xfs_iext_rebalance_node(
>  	struct xfs_iext_node	*parent,
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 2711ff6ab2b3..dca5dcec2cd4 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -362,7 +362,7 @@ xfs_iformat_extents(
>  			}
>  
>  			xfs_bmbt_disk_get_all(dp, &new);
> -			xfs_iext_insert(ip, &ext, 1, &new, state);
> +			xfs_iext_insert(ip, &ext, &new, state);
>  			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
>  			xfs_iext_next(ifp, &ext);
>  		}
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index 015872caaab4..cc73fea4e37e 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
> @@ -114,7 +114,7 @@ void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
>  
>  xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
>  void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
> -			xfs_extnum_t, struct xfs_bmbt_irec *, int);
> +			struct xfs_bmbt_irec *, int);
>  void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
>  			int, int);
>  void		xfs_iext_destroy(struct xfs_ifork *);
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 18/18] xfs: remove the nr_extents argument to xfs_iext_remove
  2017-10-31 14:22 ` [PATCH 18/18] xfs: remove the nr_extents argument to xfs_iext_remove Christoph Hellwig
@ 2017-10-31 22:37   ` Darrick J. Wong
  0 siblings, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-10-31 22:37 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:30PM +0200, Christoph Hellwig wrote:
> We only have two places that remove 2 extents at the same time, so unroll
> the loop there.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c       | 31 ++++++++++++++-----------------
>  fs/xfs/libxfs/xfs_iext_tree.c  | 40 ++++++++++++----------------------------
>  fs/xfs/libxfs/xfs_inode_fork.h |  2 +-
>  3 files changed, 27 insertions(+), 46 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index b832dc402f40..ce3cedd3f636 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1196,11 +1196,6 @@ xfs_iread_extents(
>  		return -EFSCORRUPTED;
>  	}
>  
> -	ifp->if_bytes = 0;
> -	ifp->if_real_bytes = 0;
> -	ifp->if_u1.if_root = NULL;
> -	ifp->if_height = 0;

Landed in the wrong patch?

Other than that,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

> -
>  	/*
>  	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
>  	 */
> @@ -1649,7 +1644,8 @@ xfs_bmap_add_extent_delay_real(
>  		 */
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
>  
> -		xfs_iext_remove(bma->ip, &bma->ext, 2, state);
> +		xfs_iext_remove(bma->ip, &bma->ext, state);
> +		xfs_iext_remove(bma->ip, &bma->ext, state);
>  		xfs_iext_prev(ifp, &bma->ext);
>  		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
>  		(*nextents)--;
> @@ -1684,7 +1680,7 @@ xfs_bmap_add_extent_delay_real(
>  		old = LEFT;
>  		LEFT.br_blockcount += PREV.br_blockcount;
>  
> -		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
> +		xfs_iext_remove(bma->ip, &bma->ext, state);
>  		xfs_iext_prev(ifp, &bma->ext);
>  		xfs_iext_update_extent(bma->ip, state, &bma->ext, &LEFT);
>  
> @@ -1711,7 +1707,7 @@ xfs_bmap_add_extent_delay_real(
>  		PREV.br_blockcount += RIGHT.br_blockcount;
>  
>  		xfs_iext_next(ifp, &bma->ext);
> -		xfs_iext_remove(bma->ip, &bma->ext, 1, state);
> +		xfs_iext_remove(bma->ip, &bma->ext, state);
>  		xfs_iext_prev(ifp, &bma->ext);
>  		xfs_iext_update_extent(bma->ip, state, &bma->ext, &PREV);
>  
> @@ -2148,7 +2144,8 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		LEFT.br_blockcount += PREV.br_blockcount + RIGHT.br_blockcount;
>  
> -		xfs_iext_remove(ip, ext, 2, state);
> +		xfs_iext_remove(ip, ext, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		xfs_iext_update_extent(ip, state, ext, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
> @@ -2186,7 +2183,7 @@ xfs_bmap_add_extent_unwritten_real(
>  		 */
>  		LEFT.br_blockcount += PREV.br_blockcount;
>  
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		xfs_iext_update_extent(ip, state, ext, &LEFT);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
> @@ -2220,7 +2217,7 @@ xfs_bmap_add_extent_unwritten_real(
>  		PREV.br_state = new->br_state;
>  
>  		xfs_iext_next(ifp, ext);
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		xfs_iext_update_extent(ip, state, ext, &PREV);
>  
> @@ -2587,7 +2584,7 @@ xfs_bmap_add_extent_hole_delay(
>  		left.br_startblock = nullstartblock(newlen);
>  		left.br_blockcount = temp;
>  
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		xfs_iext_update_extent(ip, state, ext, &left);
>  		break;
> @@ -2732,7 +2729,7 @@ xfs_bmap_add_extent_hole_real(
>  		 */
>  		left.br_blockcount += new->br_blockcount + right.br_blockcount;
>  
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		xfs_iext_update_extent(ip, state, ext, &left);
>  
> @@ -4690,7 +4687,7 @@ xfs_bmap_del_extent_delay(
>  		/*
>  		 * Matches the whole extent.  Delete the entry.
>  		 */
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		break;
>  	case BMAP_LEFT_FILLING:
> @@ -4791,7 +4788,7 @@ xfs_bmap_del_extent_cow(
>  		/*
>  		 * Matches the whole extent.  Delete the entry.
>  		 */
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		break;
>  	case BMAP_LEFT_FILLING:
> @@ -4931,7 +4928,7 @@ xfs_bmap_del_extent_real(
>  		/*
>  		 * Matches the whole extent.  Delete the entry.
>  		 */
> -		xfs_iext_remove(ip, ext, 1, state);
> +		xfs_iext_remove(ip, ext, state);
>  		xfs_iext_prev(ifp, ext);
>  		XFS_IFORK_NEXT_SET(ip, whichfork,
>  			XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
> @@ -5557,7 +5554,7 @@ xfs_bmse_merge(
>  		return error;
>  
>  done:
> -	xfs_iext_remove(ip, ext, 1, 0);
> +	xfs_iext_remove(ip, ext, 0);
>  	xfs_iext_prev(XFS_IFORK_PTR(ip, whichfork), ext);
>  	xfs_iext_update_extent(ip, xfs_bmap_fork_to_state(whichfork), ext,
>  			&new);
> diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
> index c109526e52c5..c18d344d46cd 100644
> --- a/fs/xfs/libxfs/xfs_iext_tree.c
> +++ b/fs/xfs/libxfs/xfs_iext_tree.c
> @@ -162,12 +162,10 @@ static inline struct xfs_iext_rec *cur_rec(struct xfs_iext_cursor *cur)
>  static noinline bool xfs_iext_valid(struct xfs_ifork *ifp,
>  		struct xfs_iext_cursor *cur)
>  {
> -	if (cur->pos < 0)
> -		return false;
> -	if (cur->pos >= xfs_iext_max_recs(ifp))
> -		return false;
>  	if (!cur->leaf)
>  		return false;
> +	if (cur->pos < 0 || cur->pos >= xfs_iext_max_recs(ifp))
> +		return false;
>  	if (xfs_iext_rec_is_empty(cur_rec(cur)))
>  		return false;
>  	return true;
> @@ -256,8 +254,8 @@ xfs_iext_next(
>  	ASSERT(cur->pos < xfs_iext_max_recs(ifp));
>  
>  	cur->pos++;
> -	if (!xfs_iext_valid(ifp, cur) && ifp->if_height > 1 &&
> -	    cur->leaf && cur->leaf->next) {
> +	if (ifp->if_height > 1 && !xfs_iext_valid(ifp, cur) &&
> +	    cur->leaf->next) {
>  		cur->leaf = cur->leaf->next;
>  		cur->pos = 0;
>  	}
> @@ -826,15 +824,19 @@ xfs_iext_free_last_leaf(
>  	kmem_free(ifp->if_u1.if_root);
>  }
>  
> -static void
> -__xfs_iext_remove(
> -	struct xfs_ifork	*ifp,
> -	struct xfs_iext_cursor	*cur)
> +void
> +xfs_iext_remove(
> +	struct xfs_inode	*ip,
> +	struct xfs_iext_cursor	*cur,
> +	int			state)
>  {
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
>  	struct xfs_iext_leaf	*leaf = cur->leaf;
>  	xfs_fileoff_t		offset = xfs_iext_leaf_key(leaf, 0);
>  	int			i, nr_entries;
>  
> +	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
> +
>  	ASSERT(ifp->if_height > 0);
>  	ASSERT(ifp->if_u1.if_root != NULL);
>  	ASSERT(xfs_iext_valid(ifp, cur));
> @@ -866,24 +868,6 @@ __xfs_iext_remove(
>  		xfs_iext_free_last_leaf(ifp);
>  }
>  
> -void
> -xfs_iext_remove(
> -	struct xfs_inode	*ip,
> -	struct xfs_iext_cursor	*cur,
> -	int			nr_extents,
> -	int			state)
> -{
> -	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> -	int			i;
> -
> -	ASSERT(nr_extents > 0);
> -
> -	for (i = 0; i < nr_extents; i++) {
> -		trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
> -		__xfs_iext_remove(ifp, cur);
> -	}
> -}
> -
>  /*
>   * Lookup the extent covering bno.
>   *
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index cc73fea4e37e..58da2ec262e3 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
> @@ -116,7 +116,7 @@ xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
>  void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
>  			struct xfs_bmbt_irec *, int);
>  void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
> -			int, int);
> +			int);
>  void		xfs_iext_destroy(struct xfs_ifork *);
>  
>  bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: b+tree for the incore extent list
  2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
                   ` (17 preceding siblings ...)
  2017-10-31 14:22 ` [PATCH 18/18] xfs: remove the nr_extents argument to xfs_iext_remove Christoph Hellwig
@ 2017-11-01  3:08 ` Darrick J. Wong
  18 siblings, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-01  3:08 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:12PM +0200, Christoph Hellwig wrote:
> Hi all,
> 
> this series first updates the incore extent list iteration to use
> a cursor based scheme that hides the implementation details, and then
> switch to use a b+tree to implement the in-core extent list.  This
> reduces the need for a large contiguous allocation that the current
> indirection array requires, and thus avoids stalls during workloads
> using giant extent lists, especially on systems that are long running.
> 
> The algorithms also should be better in general, but due to the fact
> the the operations on the on-disk b+tree have such a high overhead
> not much that effect is seen on the usual benchmarks.
> 
> I also have a git tree available at:
> 
>     git://git.infradead.org/users/hch/xfs.git xfs-incore-btree

FWIW I started xfstesting this but encountered numerous xfs_repair
problems with a -m crc=0 filesystem:

[ 5483.480991] XFS (pmem4): Injecting error (false) at file /raid/home/djwong/cdev/work/linux-dgc/fs/xfs/xfs_trans_ail.c, line 338, on filesystem "pmem4"
[ 5483.483600] XFS (pmem4): Injecting error (false) at file /raid/home/djwong/cdev/work/linux-dgc/fs/xfs/xfs_trans_ail.c, line 338, on filesystem "pmem4"
[ 5485.671575] XFS (pmem4): Intentionally corrupted log record at LSN 0x1a000016f0. Shutdown imminent.
[ 5485.673555] XFS (pmem4): metadata I/O error: block 0x7e728 ("xlog_iodone") error 0 numblks 64
[ 5485.675230] XFS (pmem4): xfs_do_force_shutdown(0x2) called from line 1261 of file /raid/home/djwong/cdev/work/linux-dgc/fs/xfs/xfs_log.c.  Return address = 0xffffffffa0157edb
[ 5485.691317] XFS (pmem4): Log I/O Error Detected.  Shutting down filesystem
[ 5485.691820] XFS (pmem4): xfs_do_force_shutdown(0x1) called from line 236 of file /raid/home/djwong/cdev/work/linux-dgc/fs/xfs/libxfs/xfs_defer.c.  Return address = 0xffffffffa00ea49e
[ 5485.691916] XFS (pmem4): xfs_inactive_ifree: xfs_defer_finish returned error -5
[ 5485.692001] XFS (pmem4): xfs_inactive_ifree: xfs_trans_commit returned error -5
[ 5485.698093] XFS (pmem4): Please umount the filesystem and rectify the problem(s)
[ 5486.628228] XFS (pmem4): Unmounting Filesystem
[ 5589.882261] INFO: task umount:28106 blocked for more than 60 seconds.
[ 5589.883320]       Tainted: G        W       4.14.0-rc6-dgc #2
[ 5589.884473] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[ 5589.886103] umount          D    0 28106  27867 0x00000000
[ 5589.887491] Call Trace:
[ 5589.888158]  ? __schedule+0x3fd/0xb10
[ 5589.889002]  ? __xfs_iflock+0x9f/0x100 [xfs]
[ 5589.889645]  schedule+0x40/0x90
[ 5589.890013]  io_schedule+0x16/0x40
[ 5589.890537]  __xfs_iflock+0xf8/0x100 [xfs]
[ 5589.891124]  ? bit_waitqueue+0x40/0x40
[ 5589.891565]  xfs_reclaim_inode+0x147/0x410 [xfs]
[ 5589.892117]  xfs_reclaim_inodes_ag+0x247/0x400 [xfs]
[ 5589.892683]  xfs_reclaim_inodes+0x1b/0x20 [xfs]
[ 5589.893223]  xfs_unmountfs+0xc0/0x2c0 [xfs]
[ 5589.893734]  xfs_fs_put_super+0x2c/0x90 [xfs]
[ 5589.894299]  generic_shutdown_super+0x64/0x110
[ 5589.895218]  kill_block_super+0x21/0x50
[ 5589.895867]  deactivate_locked_super+0x34/0x60
[ 5589.896377]  cleanup_mnt+0x3b/0x70
[ 5589.896774]  task_work_run+0x79/0xb0
[ 5589.897193]  exit_to_usermode_loop+0x93/0xa0
[ 5589.897703]  syscall_return_slowpath+0xd7/0x100
[ 5589.898312]  entry_SYSCALL_64_fastpath+0xbc/0xbe
[ 5589.899263] RIP: 0033:0x7f7e8b933447

And for whatever reason on the -m rmapbt=1,reflink=1 -i sparse
filesystem with 1k block size xfs_repair blew up all over the place.

--D

> 
> Gitweb:
> 
>     http://git.infradead.org/users/hch/xfs.git/shortlog/refs/heads/xfs-incore-btree
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-10-31 21:15     ` Darrick J. Wong
@ 2017-11-01 13:58       ` Brian Foster
  2017-11-01 23:00         ` Darrick J. Wong
  0 siblings, 1 reply; 73+ messages in thread
From: Brian Foster @ 2017-11-01 13:58 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Tue, Oct 31, 2017 at 02:15:20PM -0700, Darrick J. Wong wrote:
> On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> > On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > > This prepares for getting rid of the current in-memory extent format.
> > > 
> > 
> > Couldn't we port this function over to use whatever the new in-memory
> > extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> > rather than the associated bit in the on-disk format..?
> 
> It's certainly possible, but in general verifiers are supposed to check
> on-disk metadata before they end up in-core, and this would seem to get
> us closer to that, right?
> 

Yeah, but are any of these calls actually made in a buffer verifier
path?

Brian

> --D
> 
> > Brian
> > 
> > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > ---
> > >  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
> > >  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
> > >  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
> > >  3 files changed, 9 insertions(+), 10 deletions(-)
> > > 
> > > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > > index f45f05c45e15..b2b6832b9e6b 100644
> > > --- a/fs/xfs/libxfs/xfs_bmap.c
> > > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > > @@ -1259,14 +1259,14 @@ xfs_iread_extents(
> > >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> > >  		for (j = 0; j < num_recs; j++, i++, frp++) {
> > >  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> > > -			trp->l0 = be64_to_cpu(frp->l0);
> > > -			trp->l1 = be64_to_cpu(frp->l1);
> > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
> > >  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> > >  						 XFS_ERRLEVEL_LOW, mp);
> > >  				error = -EFSCORRUPTED;
> > >  				goto out_brelse;
> > >  			}
> > > +			trp->l0 = be64_to_cpu(frp->l0);
> > > +			trp->l1 = be64_to_cpu(frp->l1);
> > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > >  		}
> > >  		xfs_trans_brelse(tp, bp);
> > > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > index 6f891eeb88f6..2fbfe2a24b15 100644
> > > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> > >   * Check that the extent does not contain an invalid unwritten extent flag.
> > >   */
> > >  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > > -		struct xfs_bmbt_rec_host *ep)
> > > +		struct xfs_bmbt_rec *ep)
> > >  {
> > > -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > >  		return true;
> > >  	if (whichfork == XFS_DATA_FORK &&
> > >  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > > index bb63f38b97cc..abe601b48c9c 100644
> > > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > > @@ -371,13 +371,13 @@ xfs_iformat_extents(
> > >  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> > >  		for (i = 0; i < nex; i++, dp++) {
> > >  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > -			ep->l0 = get_unaligned_be64(&dp->l0);
> > > -			ep->l1 = get_unaligned_be64(&dp->l1);
> > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
> > >  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> > >  						 XFS_ERRLEVEL_LOW, mp);
> > >  				return -EFSCORRUPTED;
> > >  			}
> > > +			ep->l0 = get_unaligned_be64(&dp->l0);
> > > +			ep->l1 = get_unaligned_be64(&dp->l1);
> > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > >  		}
> > >  	}
> > > @@ -764,8 +764,6 @@ xfs_iextents_copy(
> > >  	for (i = 0; i < nrecs; i++) {
> > >  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > >  
> > > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> > > -
> > >  		start_block = xfs_bmbt_get_startblock(ep);
> > >  		if (isnullstartblock(start_block)) {
> > >  			/*
> > > @@ -779,6 +777,7 @@ xfs_iextents_copy(
> > >  		/* Translate to on disk format */
> > >  		put_unaligned_be64(ep->l0, &dp->l0);
> > >  		put_unaligned_be64(ep->l1, &dp->l1);
> > > +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> > >  
> > >  		dp++;
> > >  		copied++;
> > > -- 
> > > 2.14.2
> > > 
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 16/18] xfs: use a b+tree for the in-core extent list
  2017-10-31 14:22 ` [PATCH 16/18] xfs: use a b+tree for the in-core extent list Christoph Hellwig
@ 2017-11-01 18:47   ` Darrick J. Wong
  2017-11-02  0:16     ` Darrick J. Wong
  2017-11-02  0:14   ` Darrick J. Wong
  1 sibling, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-01 18:47 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:28PM +0200, Christoph Hellwig wrote:
> Replace the current linear list and the indirection array for the in-core
> extent list with a b+tree to avoid the need for larger memory allocations
> for the indirection array when lots of extents are present.  The current
> extent list implementations leads to heavy pressure on the memory
> allocator when modifying files with a high extent count, and can lead
> to high latencies because of that.
> 
> The replacement is a b+tree with a few quirks.  The leaf nodes directly
> store the extent record in two u64 values.  The encoding is a little bit
> different from the existing in-core extent records so that the start
> offset and length which are required for lookups can be retreived with
> simple mask operations.  The inner nodes store a 64-bit key containing
> the start offset in the first half of the node, and the pointers to the
> next lower level in the second half.  In either case we walk the node
> from the beginninig to the end and do a linear search, as that is more
> efficient for the low number of cache lines touched during a search
> (2 for the inner nodes, 4 for the leaf nodes) than a binary search.
> We store termination markers (zero length for the leaf nodes, an
> otherwise impossible high bit for the inner nodes) to terminate the key
> list / records instead of storing a count to use the available cache
> lines as efficiently as possible.
> 
> One quirk of the algorithm is that while we normally split a node half and
> half like usual btree implementations we just spill over entries added at
> the very end of the list to a new node on its own.  This means we get a
> 100% fill grade for the common cases of bulk inseration at reading an
> inode into memory, and when only sequentially appending to a file.  The
> downside is a slightly higher chance of splits on the first random
> inserations.
> 
> Both insert and removal manually recurse into the lower levels, but
> the bulk deletion of the whole tree is still implemented as a recursive
> function call, although one limited by the overall depth and with very
> little stack usage in every iteration.
> 
> For the first few extents we dynamically grow the list from a single
> extent to the next powers of two until we have a first full leaf block
> and that building the actual tree.
> 
> The code started out based on the generic lib/btree.c code from Joern
> Engel based on earlier work from Peter Zijlstra, but has since been
> rewritten beyond recognition.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/Makefile                |    1 +
>  fs/xfs/libxfs/xfs_bmap.c       |   19 +-
>  fs/xfs/libxfs/xfs_bmap_btree.c |  103 +---
>  fs/xfs/libxfs/xfs_bmap_btree.h |    7 +-
>  fs/xfs/libxfs/xfs_format.h     |    4 -
>  fs/xfs/libxfs/xfs_iext_tree.c  | 1043 ++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_inode_fork.c | 1035 +--------------------------------------
>  fs/xfs/libxfs/xfs_inode_fork.h |   84 +---
>  fs/xfs/libxfs/xfs_types.h      |    3 +-
>  fs/xfs/scrub/bmap.c            |    5 +-
>  fs/xfs/xfs_inode.c             |    2 +-
>  fs/xfs/xfs_inode_item.c        |    2 -
>  fs/xfs/xfs_trace.h             |   51 +-
>  13 files changed, 1103 insertions(+), 1256 deletions(-)
>  create mode 100644 fs/xfs/libxfs/xfs_iext_tree.c
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index a2a5d046793d..7ceb41a9786a 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -49,6 +49,7 @@ xfs-y				+= $(addprefix libxfs/, \
>  				   xfs_dquot_buf.o \
>  				   xfs_ialloc.o \
>  				   xfs_ialloc_btree.o \
> +				   xfs_iext_tree.o \
>  				   xfs_inode_fork.o \
>  				   xfs_inode_buf.o \
>  				   xfs_log_rlimit.o \
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index e020bd3f8717..46bda00dca79 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -805,6 +805,8 @@ xfs_bmap_local_to_extents_empty(
>  	xfs_bmap_forkoff_reset(ip, whichfork);
>  	ifp->if_flags &= ~XFS_IFINLINE;
>  	ifp->if_flags |= XFS_IFEXTENTS;
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  	XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
>  }
>  
> @@ -846,8 +848,7 @@ xfs_bmap_local_to_extents(
>  
>  	flags = 0;
>  	error = 0;
> -	ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) ==
> -								XFS_IFINLINE);
> +	ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS)) == XFS_IFINLINE);
>  	memset(&args, 0, sizeof(args));
>  	args.tp = tp;
>  	args.mp = ip->i_mount;
> @@ -891,6 +892,9 @@ xfs_bmap_local_to_extents(
>  	xfs_bmap_local_to_extents_empty(ip, whichfork);
>  	flags |= XFS_ILOG_CORE;
>  
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
> +
>  	rec.br_startoff = 0;
>  	rec.br_startblock = args.fsbno;
>  	rec.br_blockcount = 1;
> @@ -1177,6 +1181,7 @@ xfs_iread_extents(
>  	xfs_extnum_t		nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
>  	struct xfs_btree_block	*block = ifp->if_broot;
>  	struct xfs_iext_cursor	ext;
> +	struct xfs_bmbt_irec	new;
>  	xfs_fsblock_t		bno;
>  	struct xfs_buf		*bp;
>  	xfs_extnum_t		i, j;
> @@ -1193,7 +1198,8 @@ xfs_iread_extents(
>  
>  	ifp->if_bytes = 0;
>  	ifp->if_real_bytes = 0;
> -	xfs_iext_add(ifp, 0, nextents);
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  
>  	/*
>  	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
> @@ -1258,16 +1264,15 @@ xfs_iread_extents(
>  		 * Copy records into the extent records.
>  		 */
>  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> -		for (j = 0; j < num_recs; j++, i++, frp++) {
> -			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> +		for (j = 0; j < num_recs; j++, frp++, i++) {
>  			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
>  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				error = -EFSCORRUPTED;
>  				goto out_brelse;
>  			}
> -			trp->l0 = be64_to_cpu(frp->l0);
> -			trp->l1 = be64_to_cpu(frp->l1);
> +			xfs_bmbt_disk_get_all(frp, &new);
> +			xfs_iext_insert(ip, &ext, 1, &new, state);
>  			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
>  			xfs_iext_next(ifp, &ext);
>  		}
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
> index 89260972a0f6..c10aecaaae44 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.c
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.c
> @@ -71,73 +71,21 @@ xfs_bmdr_to_bmbt(
>  	memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
>  }
>  
> -/*
> - * Convert a compressed bmap extent record to an uncompressed form.
> - * This code must be in sync with the routines xfs_bmbt_get_startoff,
> - * xfs_bmbt_get_startblock and xfs_bmbt_get_blockcount.
> - */
> -STATIC void
> -__xfs_bmbt_get_all(
> -		uint64_t l0,
> -		uint64_t l1,
> -		xfs_bmbt_irec_t *s)
> -{
> -	int	ext_flag;
> -	xfs_exntst_t st;
> -
> -	ext_flag = (int)(l0 >> (64 - BMBT_EXNTFLAG_BITLEN));
> -	s->br_startoff = ((xfs_fileoff_t)l0 &
> -			   xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
> -	s->br_startblock = (((xfs_fsblock_t)l0 & xfs_mask64lo(9)) << 43) |
> -			   (((xfs_fsblock_t)l1) >> 21);
> -	s->br_blockcount = (xfs_filblks_t)(l1 & xfs_mask64lo(21));
> -	/* This is xfs_extent_state() in-line */
> -	if (ext_flag) {
> -		ASSERT(s->br_blockcount != 0);	/* saved for DMIG */
> -		st = XFS_EXT_UNWRITTEN;
> -	} else
> -		st = XFS_EXT_NORM;
> -	s->br_state = st;
> -}
> -
>  void
> -xfs_bmbt_get_all(
> -	xfs_bmbt_rec_host_t *r,
> -	xfs_bmbt_irec_t *s)
> -{
> -	__xfs_bmbt_get_all(r->l0, r->l1, s);
> -}
> -
> -/*
> - * Extract the blockcount field from an in memory bmap extent record.
> - */
> -xfs_filblks_t
> -xfs_bmbt_get_blockcount(
> -	xfs_bmbt_rec_host_t	*r)
> -{
> -	return (xfs_filblks_t)(r->l1 & xfs_mask64lo(21));
> -}
> -
> -/*
> - * Extract the startblock field from an in memory bmap extent record.
> - */
> -xfs_fsblock_t
> -xfs_bmbt_get_startblock(
> -	xfs_bmbt_rec_host_t	*r)
> -{
> -	return (((xfs_fsblock_t)r->l0 & xfs_mask64lo(9)) << 43) |
> -	       (((xfs_fsblock_t)r->l1) >> 21);
> -}
> -
> -/*
> - * Extract the startoff field from an in memory bmap extent record.
> - */
> -xfs_fileoff_t
> -xfs_bmbt_get_startoff(
> -	xfs_bmbt_rec_host_t	*r)
> -{
> -	return ((xfs_fileoff_t)r->l0 &
> -		 xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
> +xfs_bmbt_disk_get_all(
> +	struct xfs_bmbt_rec	*rec,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	uint64_t		l0 = get_unaligned_be64(&rec->l0);
> +	uint64_t		l1 = get_unaligned_be64(&rec->l1);
> +
> +	irec->br_startoff = (l0 & xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
> +	irec->br_startblock = ((l0 & xfs_mask64lo(9)) << 43) | (l1 >> 21);
> +	irec->br_blockcount = l1 & xfs_mask64lo(21);
> +	if (l0 >> (64 - BMBT_EXNTFLAG_BITLEN))
> +		irec->br_state = XFS_EXT_UNWRITTEN;
> +	else
> +		irec->br_state = XFS_EXT_NORM;
>  }
>  
>  /*
> @@ -161,29 +109,6 @@ xfs_bmbt_disk_get_startoff(
>  		 xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
>  }
>  
> -/*
> - * Set all the fields in a bmap extent record from the uncompressed form.
> - */
> -void
> -xfs_bmbt_set_all(
> -	struct xfs_bmbt_rec_host *r,
> -	struct xfs_bmbt_irec	*s)
> -{
> -	int			extent_flag = (s->br_state != XFS_EXT_NORM);
> -
> -	ASSERT(s->br_state == XFS_EXT_NORM || s->br_state == XFS_EXT_UNWRITTEN);
> -	ASSERT(!(s->br_startoff & xfs_mask64hi(64-BMBT_STARTOFF_BITLEN)));
> -	ASSERT(!(s->br_blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)));
> -	ASSERT(!(s->br_startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)));
> -
> -	r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
> -		 ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
> -		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43);
> -	r->l1 = ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
> -		 ((xfs_bmbt_rec_base_t)s->br_blockcount &
> -		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21));
> -}
> -
>  /*
>   * Set all the fields in a bmap extent record from the uncompressed form.
>   */
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> index 2fbfe2a24b15..714bfbaf9b2d 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> @@ -98,16 +98,11 @@ struct xfs_trans;
>   */
>  extern void xfs_bmdr_to_bmbt(struct xfs_inode *, xfs_bmdr_block_t *, int,
>  			struct xfs_btree_block *, int);
> -extern void xfs_bmbt_get_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
> -extern xfs_filblks_t xfs_bmbt_get_blockcount(xfs_bmbt_rec_host_t *r);
> -extern xfs_fsblock_t xfs_bmbt_get_startblock(xfs_bmbt_rec_host_t *r);
> -extern xfs_fileoff_t xfs_bmbt_get_startoff(xfs_bmbt_rec_host_t *r);
>  
>  void xfs_bmbt_disk_set_all(struct xfs_bmbt_rec *r, struct xfs_bmbt_irec *s);
>  extern xfs_filblks_t xfs_bmbt_disk_get_blockcount(xfs_bmbt_rec_t *r);
>  extern xfs_fileoff_t xfs_bmbt_disk_get_startoff(xfs_bmbt_rec_t *r);
> -
> -extern void xfs_bmbt_set_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
> +extern void xfs_bmbt_disk_get_all(xfs_bmbt_rec_t *r, xfs_bmbt_irec_t *s);
>  
>  extern void xfs_bmbt_to_bmdr(struct xfs_mount *, struct xfs_btree_block *, int,
>  			xfs_bmdr_block_t *, int);
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index 6470dfa768ee..28d5391d2272 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -1553,10 +1553,6 @@ typedef struct xfs_bmbt_rec {
>  typedef uint64_t	xfs_bmbt_rec_base_t;	/* use this for casts */
>  typedef xfs_bmbt_rec_t xfs_bmdr_rec_t;
>  
> -typedef struct xfs_bmbt_rec_host {
> -	uint64_t		l0, l1;
> -} xfs_bmbt_rec_host_t;
> -
>  /*
>   * Values and macros for delayed-allocation startblock fields.
>   */
> diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
> new file mode 100644
> index 000000000000..acb66c0677e7
> --- /dev/null
> +++ b/fs/xfs/libxfs/xfs_iext_tree.c
> @@ -0,0 +1,1043 @@
> +/*
> + * Copyright (c) 2017 Christoph Hellwig.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + */
> +
> +#include <linux/cache.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include "xfs.h"
> +#include "xfs_format.h"
> +#include "xfs_bit.h"
> +#include "xfs_log_format.h"
> +#include "xfs_inode.h"
> +#include "xfs_inode_fork.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_trace.h"
> +
> +/*

I've not reviewed this patch, so this is a preliminary review comment based
on the test failures I saw last night:

> + * In-core extent record layout:
> + *
> + * +-------+----------------------------+
> + * | 00:51 | all 52 bits of startoff    |
> + * | 52:63 | low 12 bits of startblock  |
> + * +-------+----------------------------+
> + * | 00:20 | all 21 bits of length      |
> + * |    21 | unwritten extent bit       |
> + * | 22:63 | high 42 bits of startblock |
> + * +-------+----------------------------+
> + */
> +#define XFS_IEXT_STARTOFF_MASK		xfs_mask64lo(52)
> +#define XFS_IEXT_LENGTH_MASK		xfs_mask64lo(21)
> +#define XFS_IEXT_STARTBLOCK_MASK	xfs_mask64lo(54)

These definitions are incorrect -- startoff is 54 bits wide and
startblock is 52 bits wide.

#define XFS_IEXT_STARTOFF_MASK		xfs_mask64lo(BMBT_STARTOFF_BITLEN)
#define XFS_IEXT_LENGTH_MASK		xfs_mask64lo(BMBT_BLOCKCOUNT_BITLEN)
#define XFS_IEXT_STARTBLOCK_MASK	xfs_mask64lo(BMBT_STARTBLOCK_BITLEN)

Found via generic/30[34] with a 1k block size since we try to write the
highest possible byte offset in a file.

> +
> +struct xfs_iext_rec {
> +	uint64_t			lo;
> +	uint64_t			hi;
> +};
> +
> +/*
> + * Given that the length can't be a zero, only an empty hi value indicates an
> + * unused record.
> + */
> +static bool xfs_iext_rec_is_empty(struct xfs_iext_rec *rec)
> +{
> +	return rec->hi == 0;
> +}
> +
> +static inline void xfs_iext_rec_clear(struct xfs_iext_rec *rec)
> +{
> +	rec->lo = 0;
> +	rec->hi = 0;
> +}
> +
> +static void
> +xfs_iext_set(
> +	struct xfs_iext_rec	*rec,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	ASSERT((irec->br_startoff & ~XFS_IEXT_STARTOFF_MASK) == 0);
> +	ASSERT((irec->br_blockcount & ~XFS_IEXT_LENGTH_MASK) == 0);
> +	ASSERT((irec->br_startblock & ~XFS_IEXT_STARTBLOCK_MASK) == 0);
> +
> +	rec->lo = irec->br_startoff & XFS_IEXT_STARTOFF_MASK;
> +	rec->hi = irec->br_blockcount & XFS_IEXT_LENGTH_MASK;
> +
> +	rec->lo |= (irec->br_startblock << 52);
> +	rec->hi |= ((irec->br_startblock & ~xfs_mask64lo(12)) << (22 - 12));

At a bare minimum these will need to be fixed to reflect the correct
incore format, here and everywhere else in this file.

Ideally the well known constants here would also use
BMBT_{STARTOFF,STARTBLOCK,BLOCKCOUNT}_BITLEN.

> +
> +	if (irec->br_state == XFS_EXT_UNWRITTEN)
> +		rec->hi |= (1 << 21);
> +}
> +
> +static void
> +xfs_iext_get(
> +	struct xfs_bmbt_irec	*irec,
> +	struct xfs_iext_rec	*rec)
> +{
> +	irec->br_startoff = rec->lo & XFS_IEXT_STARTOFF_MASK;
> +	irec->br_blockcount = rec->hi & XFS_IEXT_LENGTH_MASK;
> +
> +	irec->br_startblock = rec->lo >> 52;
> +	irec->br_startblock |= (rec->hi & xfs_mask64hi(42)) >> (22 - 12);
> +
> +	if (rec->hi & (1 << 21))

Here too.  Though this might be the only place that directly encodes
integer constants?

--D

> +		irec->br_state = XFS_EXT_UNWRITTEN;
> +	else
> +		irec->br_state = XFS_EXT_NORM;
> +}
> +
> +enum {
> +	NODE_SIZE	= L1_CACHE_BYTES * 4,
> +	KEYS_PER_NODE	= NODE_SIZE / (sizeof(uint64_t) + sizeof(void *)),
> +	RECS_PER_LEAF	= (NODE_SIZE - sizeof(uint64_t) - sizeof(uint64_t)) /
> +				sizeof(struct xfs_iext_rec),
> +};
> +
> +/*
> + * In-core extent btree block layout:
> + *
> + * There are two types of blocks in the btree: leaf and inner (non-leaf) blocks.
> + *
> + * The leaf blocks are made up by %KEYS_PER_NODE extent records, which each
> + * contain the startoffset, blockcount, startblock and unwritten extent flag.
> + * See above for the exact format, followed by pointers to the previous and next
> + * leaf blocks (if there are any).
> + *
> + * The inner (non-leaf) blocks first contain KEYS_PER_NODE lookup keys, followed
> + * by an equal number of pointers to the btree blocks at the next lower level.
> + *
> + * Note that we currently always allocate 64-bits worth for pointers in the
> + * inner nodes or the link pointers in the leaf nodes even on 32-bit
> + * architectures, so that we can use consistent addressing using and array of
> + * 64-bit unsigned integers.  If someone still cares for 32-bit architectures
> + * this could be optimized a bit better for them.
> + *
> + *		+-------+-------+-------+-------+-------+----------+----------+
> + * Leaf:	| rec 1 | rec 2 | rec 3 | rec 4 | rec N | prev-ptr | next-ptr |
> + *		+-------+-------+-------+-------+-------+----------+----------+
> + *
> + *		+-------+-------+-------+-------+-------+-------+------+-------+
> + * Inner:	| key 1 | key 2 | key 3 | key N | ptr 1 | ptr 2 | ptr3 | ptr N |
> + *		+-------+-------+-------+-------+-------+-------+------+-------+
> + */
> +struct xfs_iext_node {
> +	uint64_t		keys[KEYS_PER_NODE];
> +#define XFS_IEXT_KEY_INVALID	(1ULL << 63)
> +	void			*ptrs[KEYS_PER_NODE];
> +};
> +
> +struct xfs_iext_leaf {
> +	struct xfs_iext_rec	recs[RECS_PER_LEAF];
> +	struct xfs_iext_leaf	*prev;
> +	struct xfs_iext_leaf	*next;
> +};
> +
> +inline xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp)
> +{
> +	return ifp->if_bytes / sizeof(struct xfs_iext_rec);
> +}
> +
> +static inline int xfs_iext_max_recs(struct xfs_ifork *ifp)
> +{
> +	if (ifp->if_height == 1)
> +		return xfs_iext_count(ifp);
> +	return RECS_PER_LEAF;
> +}
> +
> +static inline struct xfs_iext_rec *cur_rec(struct xfs_iext_cursor *cur)
> +{
> +	return &cur->leaf->recs[cur->pos];
> +}
> +
> +static noinline bool xfs_iext_valid(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur)
> +{
> +	if (cur->pos < 0)
> +		return false;
> +	if (cur->pos >= xfs_iext_max_recs(ifp))
> +		return false;
> +	if (!cur->leaf)
> +		return false;
> +	if (xfs_iext_rec_is_empty(cur_rec(cur)))
> +		return false;
> +	return true;
> +}
> +
> +static void *
> +xfs_iext_find_first_leaf(
> +	struct xfs_ifork	*ifp)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height;
> +
> +	if (!ifp->if_height)
> +		return NULL;
> +
> +	for (height = ifp->if_height; height > 1; height--) {
> +		node = node->ptrs[0];
> +		ASSERT(node);
> +	}
> +
> +	return node;
> +}
> +
> +static void *
> +xfs_iext_find_last_leaf(
> +	struct xfs_ifork	*ifp)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height, i;
> +
> +	if (!ifp->if_height)
> +		return NULL;
> +
> +	for (height = ifp->if_height; height > 1; height--) {
> +		for (i = 1; i < KEYS_PER_NODE; i++)
> +			if (!node->ptrs[i])
> +				break;
> +		node = node->ptrs[i - 1];
> +		ASSERT(node);
> +	}
> +
> +	return node;
> +}
> +
> +void
> +xfs_iext_first(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	cur->pos = 0;
> +	cur->leaf = xfs_iext_find_first_leaf(ifp);
> +}
> +
> +void
> +xfs_iext_last(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	int			i;
> +
> +	cur->leaf = xfs_iext_find_last_leaf(ifp);
> +	if (!cur->leaf) {
> +		cur->pos = 0;
> +		return;
> +	}
> +
> +	for (i = 1; i < xfs_iext_max_recs(ifp); i++) {
> +		if (xfs_iext_rec_is_empty(&cur->leaf->recs[i]))
> +			break;
> +	}
> +	cur->pos = i - 1;
> +}
> +
> +void
> +xfs_iext_next(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	if (!cur->leaf) {
> +		ASSERT(cur->pos <= 0 || cur->pos >= RECS_PER_LEAF);
> +		xfs_iext_first(ifp, cur);
> +		return;
> +	}
> +
> +	ASSERT(cur->pos >= 0);
> +	ASSERT(cur->pos < xfs_iext_max_recs(ifp));
> +
> +	cur->pos++;
> +	if (!xfs_iext_valid(ifp, cur) && ifp->if_height > 1 &&
> +	    cur->leaf && cur->leaf->next) {
> +		cur->leaf = cur->leaf->next;
> +		cur->pos = 0;
> +	}
> +}
> +
> +void
> +xfs_iext_prev(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	if (!cur->leaf) {
> +		ASSERT(cur->pos <= 0 || cur->pos >= RECS_PER_LEAF);
> +		xfs_iext_last(ifp, cur);
> +		return;
> +	}
> +
> +	ASSERT(cur->pos >= 0);
> +	ASSERT(cur->pos <= RECS_PER_LEAF);
> +
> +recurse:
> +	do {
> +		cur->pos--;
> +		if (xfs_iext_valid(ifp, cur))
> +			return;
> +	} while (cur->pos > 0);
> +
> +	if (ifp->if_height > 1 && cur->leaf->prev) {
> +		cur->leaf = cur->leaf->prev;
> +		cur->pos = RECS_PER_LEAF;
> +		goto recurse;
> +	}
> +}
> +
> +static inline int
> +xfs_iext_key_cmp(
> +	struct xfs_iext_node	*node,
> +	int			n,
> +	xfs_fileoff_t		offset)
> +{
> +	if (node->keys[n] > offset)
> +		return 1;
> +	if (node->keys[n] < offset)
> +		return -1;
> +	return 0;
> +}
> +
> +static inline int
> +xfs_iext_rec_cmp(
> +	struct xfs_iext_rec	*rec,
> +	xfs_fileoff_t		offset)
> +{
> +	uint64_t		rec_offset = rec->lo & XFS_IEXT_STARTOFF_MASK;
> +	u32			rec_len = rec->hi & XFS_IEXT_LENGTH_MASK;
> +
> +	if (rec_offset > offset)
> +		return 1;
> +	if (rec_offset + rec_len <= offset)
> +		return -1;
> +	return 0;
> +}
> +
> +static void *
> +xfs_iext_find_level(
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		offset,
> +	int			level)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height, i;
> +
> +	if (!ifp->if_height)
> +		return NULL;
> +
> +	for (height = ifp->if_height; height > level; height--) {
> +		for (i = 1; i < KEYS_PER_NODE; i++)
> +			if (xfs_iext_key_cmp(node, i, offset) > 0)
> +				break;
> +
> +		node = node->ptrs[i - 1];
> +		if (!node)
> +			break;
> +	}
> +
> +	return node;
> +}
> +
> +static int
> +xfs_iext_node_pos(
> +	struct xfs_iext_node	*node,
> +	xfs_fileoff_t		offset)
> +{
> +	int			i;
> +
> +	for (i = 1; i < KEYS_PER_NODE; i++) {
> +		if (xfs_iext_key_cmp(node, i, offset) > 0)
> +			break;
> +	}
> +
> +	return i - 1;
> +}
> +
> +static int
> +xfs_iext_node_insert_pos(
> +	struct xfs_iext_node	*node,
> +	xfs_fileoff_t		offset)
> +{
> +	int			i;
> +
> +	for (i = 0; i < KEYS_PER_NODE; i++) {
> +		if (xfs_iext_key_cmp(node, i, offset) > 0)
> +			return i;
> +	}
> +
> +	return KEYS_PER_NODE;
> +}
> +
> +static int
> +xfs_iext_node_nr_entries(
> +	struct xfs_iext_node	*node,
> +	int			start)
> +{
> +	int			i;
> +
> +	for (i = start; i < KEYS_PER_NODE; i++) {
> +		if (node->keys[i] == XFS_IEXT_KEY_INVALID)
> +			break;
> +	}
> +
> +	return i;
> +}
> +
> +static int
> +xfs_iext_leaf_nr_entries(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_leaf	*leaf,
> +	int			start)
> +{
> +	int			i;
> +
> +	for (i = start; i < xfs_iext_max_recs(ifp); i++) {
> +		if (xfs_iext_rec_is_empty(&leaf->recs[i]))
> +			break;
> +	}
> +
> +	return i;
> +}
> +
> +static inline uint64_t
> +xfs_iext_leaf_key(
> +	struct xfs_iext_leaf	*leaf,
> +	int			n)
> +{
> +	return leaf->recs[n].lo & XFS_IEXT_STARTOFF_MASK;
> +}
> +
> +static void
> +xfs_iext_grow(
> +	struct xfs_ifork	*ifp)
> +{
> +	struct xfs_iext_node	*node = kmem_zalloc(NODE_SIZE, KM_NOFS);
> +	int			i;
> +
> +	if (ifp->if_height == 1) {
> +		struct xfs_iext_leaf *prev = ifp->if_u1.if_root;
> +
> +		node->keys[0] = xfs_iext_leaf_key(prev, 0);
> +		node->ptrs[0] = prev;
> +	} else  {
> +		struct xfs_iext_node *prev = ifp->if_u1.if_root;
> +
> +		ASSERT(ifp->if_height > 1);
> +
> +		node->keys[0] = prev->keys[0];
> +		node->ptrs[0] = prev;
> +	}
> +
> +	for (i = 1; i < KEYS_PER_NODE; i++)
> +		node->keys[i] = XFS_IEXT_KEY_INVALID;
> +
> +	ifp->if_u1.if_root = node;
> +	ifp->if_height++;
> +}
> +
> +static void
> +xfs_iext_update_node(
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		old_offset,
> +	xfs_fileoff_t		new_offset,
> +	int			level,
> +	void			*ptr)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height, i;
> +
> +	for (height = ifp->if_height; height > level; height--) {
> +		for (i = 0; i < KEYS_PER_NODE; i++) {
> +			if (i > 0 && xfs_iext_key_cmp(node, i, old_offset) > 0)
> +				break;
> +			if (node->keys[i] == old_offset)
> +				node->keys[i] = new_offset;
> +		}
> +		node = node->ptrs[i - 1];
> +		ASSERT(node);
> +	}
> +
> +	ASSERT(node == ptr);
> +}
> +
> +static struct xfs_iext_node *
> +xfs_iext_split_node(
> +	struct xfs_iext_node	**nodep,
> +	int			*pos,
> +	int			*nr_entries)
> +{
> +	struct xfs_iext_node	*node = *nodep;
> +	struct xfs_iext_node	*new = kmem_zalloc(NODE_SIZE, KM_NOFS);
> +	const int		nr_move = KEYS_PER_NODE / 2;
> +	int			nr_keep = nr_move + (KEYS_PER_NODE & 1);
> +	int			i = 0;
> +
> +	/* for sequential append operations just spill over into the new node */
> +	if (*pos == KEYS_PER_NODE) {
> +		*nodep = new;
> +		*pos = 0;
> +		*nr_entries = 0;
> +		goto done;
> +	}
> +
> +
> +	for (i = 0; i < nr_move; i++) {
> +		new->keys[i] = node->keys[nr_keep + i];
> +		new->ptrs[i] = node->ptrs[nr_keep + i];
> +
> +		node->keys[nr_keep + i] = XFS_IEXT_KEY_INVALID;
> +		node->ptrs[nr_keep + i] = NULL;
> +	}
> +
> +	if (*pos >= nr_keep) {
> +		*nodep = new;
> +		*pos -= nr_keep;
> +		*nr_entries = nr_move;
> +	} else {
> +		*nr_entries = nr_keep;
> +	}
> +done:
> +	for (; i < KEYS_PER_NODE; i++)
> +		new->keys[i] = XFS_IEXT_KEY_INVALID;
> +	return new;
> +}
> +
> +static void
> +xfs_iext_insert_node(
> +	struct xfs_ifork	*ifp,
> +	uint64_t		offset,
> +	void			*ptr,
> +	int			level)
> +{
> +	struct xfs_iext_node	*node, *new;
> +	int			i, pos, nr_entries;
> +
> +again:
> +	if (ifp->if_height < level)
> +		xfs_iext_grow(ifp);
> +
> +	new = NULL;
> +	node = xfs_iext_find_level(ifp, offset, level);
> +	pos = xfs_iext_node_insert_pos(node, offset);
> +	nr_entries = xfs_iext_node_nr_entries(node, pos);
> +
> +	ASSERT(pos >= nr_entries || xfs_iext_key_cmp(node, pos, offset) != 0);
> +	ASSERT(nr_entries <= KEYS_PER_NODE);
> +
> +	if (nr_entries == KEYS_PER_NODE)
> +		new = xfs_iext_split_node(&node, &pos, &nr_entries);
> +
> +	if (node != new && pos == 0 && nr_entries > 0)
> +		xfs_iext_update_node(ifp, node->keys[0], offset, level, node);
> +
> +	for (i = nr_entries; i > pos; i--) {
> +		node->keys[i] = node->keys[i - 1];
> +		node->ptrs[i] = node->ptrs[i - 1];
> +	}
> +	node->keys[pos] = offset;
> +	node->ptrs[pos] = ptr;
> +
> +	if (new) {
> +		offset = new->keys[0];
> +		ptr = new;
> +		level++;
> +		goto again;
> +	}
> +}
> +
> +static struct xfs_iext_leaf *
> +xfs_iext_split_leaf(
> +	struct xfs_iext_cursor	*cur,
> +	int			*nr_entries)
> +{
> +	struct xfs_iext_leaf	*leaf = cur->leaf;
> +	struct xfs_iext_leaf	*new = kmem_zalloc(NODE_SIZE, KM_NOFS);
> +	const int		nr_move = RECS_PER_LEAF / 2;
> +	int			nr_keep = nr_move + (RECS_PER_LEAF & 1);
> +	int			i;
> +
> +	/* for sequential append operations just spill over into the new node */
> +	if (cur->pos == KEYS_PER_NODE) {
> +		cur->leaf = new;
> +		cur->pos = 0;
> +		*nr_entries = 0;
> +		goto done;
> +	}
> +
> +	if (nr_keep & 1)
> +		nr_keep++;
> +
> +	for (i = 0; i < nr_move; i++) {
> +		new->recs[i] = leaf->recs[nr_keep + i];
> +		xfs_iext_rec_clear(&leaf->recs[nr_keep + i]);
> +	}
> +
> +	if (cur->pos >= nr_keep) {
> +		cur->leaf = new;
> +		cur->pos -= nr_keep;
> +		*nr_entries = nr_move;
> +	} else {
> +		*nr_entries = nr_keep;
> +	}
> +done:
> +	if (leaf->next)
> +		leaf->next->prev = new;
> +	new->next = leaf->next;
> +	new->prev = leaf;
> +	leaf->next = new;
> +	return new;
> +}
> +
> +static void
> +xfs_iext_alloc_root(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	ASSERT(ifp->if_bytes == 0);
> +
> +	ifp->if_u1.if_root = kmem_zalloc(sizeof(struct xfs_iext_rec), KM_NOFS);
> +	ifp->if_height = 1;
> +
> +	/* now that we have a node step into it */
> +	cur->leaf = ifp->if_u1.if_root;
> +	cur->pos = 0;
> +}
> +
> +static void
> +xfs_iext_realloc_root(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	size_t new_size = ifp->if_bytes + sizeof(struct xfs_iext_rec);
> +	void *new;
> +
> +	/* account for the prev/next pointers */
> +	if (new_size / sizeof(struct xfs_iext_rec) == RECS_PER_LEAF)
> +		new_size = NODE_SIZE;
> +
> +	new = kmem_realloc(ifp->if_u1.if_root, new_size, KM_NOFS);
> +	memset(new + ifp->if_bytes, 0, new_size - ifp->if_bytes);
> +	ifp->if_u1.if_root = new;
> +	cur->leaf = new;
> +}
> +
> +static void
> +__xfs_iext_insert(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	xfs_fileoff_t		offset = irec->br_startoff;
> +	struct xfs_iext_leaf	*new = NULL;
> +	int			nr_entries, i;
> +
> +	if (ifp->if_height == 0)
> +		xfs_iext_alloc_root(ifp, cur);
> +	else if (ifp->if_height == 1)
> +		xfs_iext_realloc_root(ifp, cur);
> +
> +	nr_entries = xfs_iext_leaf_nr_entries(ifp, cur->leaf, cur->pos);
> +	ASSERT(nr_entries <= RECS_PER_LEAF);
> +	ASSERT(cur->pos >= nr_entries ||
> +	       xfs_iext_rec_cmp(cur_rec(cur), irec->br_startoff) != 0);
> +
> +	if (nr_entries == RECS_PER_LEAF)
> +		new = xfs_iext_split_leaf(cur, &nr_entries);
> +
> +	if (cur->leaf != new && cur->pos == 0 && nr_entries > 0) {
> +		xfs_iext_update_node(ifp, xfs_iext_leaf_key(cur->leaf, 0), offset, 1,
> +				cur->leaf);
> +	}
> +
> +	for (i = nr_entries; i > cur->pos; i--)
> +		cur->leaf->recs[i] = cur->leaf->recs[i - 1];
> +	xfs_iext_set(cur_rec(cur), irec);
> +	ifp->if_bytes += sizeof(struct xfs_iext_rec);
> +
> +	if (new)
> +		xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
> +}
> +
> +void
> +xfs_iext_insert(
> +	struct xfs_inode	*ip,
> +	struct xfs_iext_cursor	*cur,
> +	xfs_extnum_t		nr_extents,
> +	struct xfs_bmbt_irec	*new,
> +	int			state)
> +{
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> +	int			i;
> +
> +	ASSERT(nr_extents > 0);
> +
> +	for (i = nr_extents - 1; i >= 0; i--) {
> +		__xfs_iext_insert(ifp, cur, new + i);
> +		trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
> +	}
> +}
> +
> +static struct xfs_iext_node *
> +xfs_iext_rebalance_node(
> +	struct xfs_iext_node	*parent,
> +	int			*pos,
> +	struct xfs_iext_node	*node,
> +	int			nr_entries)
> +{
> +	if (nr_entries == 0)
> +		return node;
> +
> +	if (*pos > 0) {
> +		struct xfs_iext_node *prev = parent->ptrs[*pos - 1];
> +		int nr_prev = xfs_iext_node_nr_entries(prev, 0), i;
> +
> +		if (nr_prev + nr_entries <= KEYS_PER_NODE) {
> +			for (i = 0; i < nr_entries; i++) {
> +				prev->keys[nr_prev + i] = node->keys[i];
> +				prev->ptrs[nr_prev + i] = node->ptrs[i];
> +			}
> +			return node;
> +		}
> +	}
> +
> +	if (*pos + 1 < xfs_iext_node_nr_entries(parent, *pos)) {
> +		struct xfs_iext_node *next = parent->ptrs[*pos + 1];
> +		int nr_next = xfs_iext_node_nr_entries(next, 0), i;
> +
> +		if (nr_entries + nr_next <= KEYS_PER_NODE) {
> +			for (i = 0; i < nr_next; i++) {
> +				node->keys[nr_entries + i] = next->keys[i];
> +				node->ptrs[nr_entries + i] = next->ptrs[i];
> +			}
> +
> +			++*pos;
> +			return next;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +static void
> +xfs_iext_remove_node(
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		offset,
> +	void			*victim)
> +{
> +	struct xfs_iext_node	*node, *parent;
> +	int			level = 2, pos, nr_entries, i;
> +
> +	ASSERT(level <= ifp->if_height);
> +	node = xfs_iext_find_level(ifp, offset, level);
> +	pos = xfs_iext_node_pos(node, offset);
> +again:
> +	ASSERT(node->ptrs[pos]);
> +	ASSERT(node->ptrs[pos] == victim);
> +	kmem_free(victim);
> +
> +	nr_entries = xfs_iext_node_nr_entries(node, pos) - 1;
> +	offset = node->keys[0];
> +	for (i = pos; i < nr_entries; i++) {
> +		node->keys[i] = node->keys[i + 1];
> +		node->ptrs[i] = node->ptrs[i + 1];
> +	}
> +	node->keys[nr_entries] = XFS_IEXT_KEY_INVALID;
> +	node->ptrs[nr_entries] = NULL;
> +
> +	if (pos == 0 && nr_entries > 0) {
> +		xfs_iext_update_node(ifp, offset, node->keys[0], level,
> +				node);
> +		offset = node->keys[0];
> +	}
> +
> +	if (nr_entries >= KEYS_PER_NODE / 2)
> +		return;
> +
> +	if (level < ifp->if_height) {
> +		level++;
> +		parent = xfs_iext_find_level(ifp, offset, level);
> +		pos = xfs_iext_node_pos(parent, offset);
> +
> +		ASSERT(pos != KEYS_PER_NODE);
> +		ASSERT(parent->ptrs[pos] == node);
> +
> +		node = xfs_iext_rebalance_node(parent, &pos, node, nr_entries);
> +		if (node) {
> +			offset = node->keys[0];
> +			victim = node;
> +			node = parent;
> +			goto again;
> +		}
> +	} else if (nr_entries == 1) {
> +		ASSERT(node == ifp->if_u1.if_root);
> +		ifp->if_u1.if_root = node->ptrs[0];
> +		ifp->if_height--;
> +		kmem_free(node);
> +	}
> +}
> +
> +static void
> +xfs_iext_rebalance_leaf(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_iext_leaf	*leaf,
> +	xfs_fileoff_t		offset,
> +	int			fill)
> +{
> +	if (leaf->prev) {
> +		int nr_prev = xfs_iext_leaf_nr_entries(ifp, leaf->prev, 0), i;
> +
> +		if (nr_prev + fill <= RECS_PER_LEAF) {
> +			for (i = 0; i < fill; i++)
> +				leaf->prev->recs[nr_prev + i] = leaf->recs[i];
> +
> +			if (cur->leaf == leaf) {
> +				cur->leaf = leaf->prev;
> +				cur->pos += nr_prev;
> +			}
> +			goto remove_node;
> +		}
> +	}
> +
> +	if (leaf->next) {
> +		int nr_next = xfs_iext_leaf_nr_entries(ifp, leaf->next, 0), i;
> +
> +		if (fill + nr_next <= RECS_PER_LEAF) {
> +			for (i = 0; i < nr_next; i++)
> +				leaf->recs[fill + i] = leaf->next->recs[i];
> +
> +			if (cur->leaf == leaf->next) {
> +				cur->leaf = leaf;
> +				cur->pos += fill;
> +			}
> +
> +			offset = xfs_iext_leaf_key(leaf->next, 0);
> +			leaf = leaf->next;
> +			goto remove_node;
> +		}
> +	}
> +
> +	return;
> +remove_node:
> +	if (leaf->prev)
> +		leaf->prev->next = leaf->next;
> +	if (leaf->next)
> +		leaf->next->prev = leaf->prev;
> +	xfs_iext_remove_node(ifp, offset, leaf);
> +}
> +
> +static void
> +xfs_iext_free_last_leaf(
> +	struct xfs_ifork	*ifp)
> +{
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height--;
> +	kmem_free(ifp->if_u1.if_root);
> +}
> +
> +static void
> +__xfs_iext_remove(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	struct xfs_iext_leaf	*leaf = cur->leaf;
> +	xfs_fileoff_t		offset = xfs_iext_leaf_key(leaf, 0);
> +	int			i, nr_entries;
> +
> +	ASSERT(ifp->if_height > 0);
> +	ASSERT(ifp->if_u1.if_root != NULL);
> +	ASSERT(xfs_iext_valid(ifp, cur));
> +
> +	nr_entries = xfs_iext_leaf_nr_entries(ifp, leaf, cur->pos) - 1;
> +	for (i = cur->pos; i < nr_entries; i++)
> +		leaf->recs[i] = leaf->recs[i + 1];
> +	xfs_iext_rec_clear(&leaf->recs[nr_entries]);
> +	ifp->if_bytes -= sizeof(struct xfs_iext_rec);
> +
> +	if (cur->pos == 0 && nr_entries > 0) {
> +		xfs_iext_update_node(ifp, offset, xfs_iext_leaf_key(leaf, 0), 1,
> +				leaf);
> +		offset = xfs_iext_leaf_key(leaf, 0);
> +	} else if (cur->pos == nr_entries) {
> +		if (ifp->if_height > 1 && leaf->next)
> +			cur->leaf = leaf->next;
> +		else
> +			cur->leaf = NULL;
> +		cur->pos = 0;
> +	}
> +
> +	if (nr_entries >= RECS_PER_LEAF / 2)
> +		return;
> +
> +	if (ifp->if_height > 1)
> +		xfs_iext_rebalance_leaf(ifp, cur, leaf, offset, nr_entries);
> +	else if (nr_entries == 0)
> +		xfs_iext_free_last_leaf(ifp);
> +}
> +
> +void
> +xfs_iext_remove(
> +	struct xfs_inode	*ip,
> +	struct xfs_iext_cursor	*cur,
> +	int			nr_extents,
> +	int			state)
> +{
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> +	int			i;
> +
> +	ASSERT(nr_extents > 0);
> +
> +	for (i = 0; i < nr_extents; i++) {
> +		trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
> +		__xfs_iext_remove(ifp, cur);
> +	}
> +}
> +
> +/*
> + * Lookup the extent covering bno.
> + *
> + * If there is an extent covering bno return the extent index, and store the
> + * expanded extent structure in *gotp, and the extent cursor in *cur.
> + * If there is no extent covering bno, but there is an extent after it (e.g.
> + * it lies in a hole) return that extent in *gotp and its cursor in *cur
> + * instead.
> + * If bno is beyond the last extent return false, and return an invalid
> + * cursor value.
> + */
> +bool
> +xfs_iext_lookup_extent(
> +	struct xfs_inode	*ip,
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		offset,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*gotp)
> +{
> +	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
> +
> +	cur->leaf = xfs_iext_find_level(ifp, offset, 1);
> +	if (!cur->leaf) {
> +		cur->pos = 0;
> +		return false;
> +	}
> +
> +	for (cur->pos = 0; cur->pos < xfs_iext_max_recs(ifp); cur->pos++) {
> +		struct xfs_iext_rec *rec = cur_rec(cur);
> +
> +		if (xfs_iext_rec_is_empty(rec))
> +			break;
> +		if (xfs_iext_rec_cmp(rec, offset) >= 0)
> +			goto found;
> +	}
> +
> +	/* Try looking in the next node for an entry > offset */
> +	if (ifp->if_height == 1 || !cur->leaf->next)
> +		return false;
> +	cur->leaf = cur->leaf->next;
> +	cur->pos = 0;
> +	if (!xfs_iext_valid(ifp, cur))
> +		return false;
> +found:
> +	xfs_iext_get(gotp, cur_rec(cur));
> +	return true;
> +}
> +
> +/*
> + * Returns the last extent before end, and if this extent doesn't cover
> + * end, update end to the end of the extent.
> + */
> +bool
> +xfs_iext_lookup_extent_before(
> +	struct xfs_inode	*ip,
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		*end,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*gotp)
> +{
> +	/* could be optimized to not even look up the next on a match.. */
> +	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
> +	    gotp->br_startoff <= *end - 1)
> +		return true;
> +	if (!xfs_iext_prev_extent(ifp, cur, gotp))
> +		return false;
> +	*end = gotp->br_startoff + gotp->br_blockcount;
> +	return true;
> +}
> +
> +void
> +xfs_iext_update_extent(
> +	struct xfs_inode	*ip,
> +	int			state,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*new)
> +{
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> +
> +	if (cur->pos == 0) {
> +		struct xfs_bmbt_irec	old;
> +
> +		xfs_iext_get(&old, cur_rec(cur));
> +		if (new->br_startoff != old.br_startoff) {
> +			xfs_iext_update_node(ifp, old.br_startoff,
> +					new->br_startoff, 1, cur->leaf);
> +		}
> +	}
> +
> +	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
> +	xfs_iext_set(cur_rec(cur), new);
> +	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
> +}
> +
> +/*
> + * Return true if there is an extent at cursor cur and return the expanded
> + * extent structure at cur in gotp in that case.  Else return false.
> + */
> +bool
> +xfs_iext_get_extent(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*gotp)
> +{
> +	if (!xfs_iext_valid(ifp, cur))
> +		return false;
> +	xfs_iext_get(gotp, cur_rec(cur));
> +	return true;
> +}
> +
> +/*
> + * This is a recursive function, because of that we need to be extremely
> + * careful with stack usage.
> + */
> +static void
> +xfs_iext_destroy_node(
> +	struct xfs_iext_node	*node,
> +	int			level)
> +{
> +	int			i;
> +
> +	if (level > 1) {
> +		for (i = 0; i < KEYS_PER_NODE; i++) {
> +			if (node->keys[i] == XFS_IEXT_KEY_INVALID)
> +				break;
> +			xfs_iext_destroy_node(node->ptrs[i], level - 1);
> +		}
> +	}
> +
> +	kmem_free(node);
> +}
> +
> +void
> +xfs_iext_destroy(
> +	struct xfs_ifork	*ifp)
> +{
> +	xfs_iext_destroy_node(ifp->if_u1.if_root, ifp->if_height);
> +
> +	ifp->if_bytes = 0;
> +	ifp->if_height = 0;
> +	ifp->if_u1.if_root = NULL;
> +}
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 5ac341d2b093..2711ff6ab2b3 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -331,6 +331,7 @@ xfs_iformat_extents(
>  	int			size = nex * sizeof(xfs_bmbt_rec_t);
>  	struct xfs_iext_cursor	ext;
>  	struct xfs_bmbt_rec	*dp;
> +	struct xfs_bmbt_irec	new;
>  	int			i;
>  
>  	/*
> @@ -346,27 +347,22 @@ xfs_iformat_extents(
>  	}
>  
>  	ifp->if_real_bytes = 0;
> -	if (nex == 0)
> -		ifp->if_u1.if_extents = NULL;
> -	else
> -		xfs_iext_add(ifp, 0, nex);
> -
> -	ifp->if_bytes = size;
> +	ifp->if_bytes = 0;
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  	if (size) {
>  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
>  
>  		xfs_iext_first(ifp, &ext);
>  		for (i = 0; i < nex; i++, dp++) {
> -			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> -
>  			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
>  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				return -EFSCORRUPTED;
>  			}
>  
> -			ep->l0 = get_unaligned_be64(&dp->l0);
> -			ep->l1 = get_unaligned_be64(&dp->l1);
> +			xfs_bmbt_disk_get_all(dp, &new);
> +			xfs_iext_insert(ip, &ext, 1, &new, state);
>  			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
>  			xfs_iext_next(ifp, &ext);
>  		}
> @@ -435,6 +431,10 @@ xfs_iformat_btree(
>  	ifp->if_flags &= ~XFS_IFEXTENTS;
>  	ifp->if_flags |= XFS_IFBROOT;
>  
> +	ifp->if_real_bytes = 0;
> +	ifp->if_bytes = 0;
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  	return 0;
>  }
>  
> @@ -662,14 +662,12 @@ xfs_idestroy_fork(
>  			ifp->if_u1.if_data = NULL;
>  			ifp->if_real_bytes = 0;
>  		}
> -	} else if ((ifp->if_flags & XFS_IFEXTENTS) &&
> -		   ((ifp->if_flags & XFS_IFEXTIREC) ||
> -		    (ifp->if_u1.if_extents != NULL))) {
> -		ASSERT(ifp->if_real_bytes != 0);
> +	} else if ((ifp->if_flags & XFS_IFEXTENTS) && ifp->if_height) {
>  		xfs_iext_destroy(ifp);
>  	}
> -	ASSERT(ifp->if_u1.if_extents == NULL);
> +
>  	ASSERT(ifp->if_real_bytes == 0);
> +
>  	if (whichfork == XFS_ATTR_FORK) {
>  		kmem_zone_free(xfs_ifork_zone, ip->i_afp);
>  		ip->i_afp = NULL;
> @@ -679,13 +677,6 @@ xfs_idestroy_fork(
>  	}
>  }
>  
> -/* Count number of incore extents based on if_bytes */
> -xfs_extnum_t
> -xfs_iext_count(struct xfs_ifork *ifp)
> -{
> -	return ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
> -}
> -
>  /*
>   * Convert in-core extents to on-disk form
>   *
> @@ -780,7 +771,6 @@ xfs_iflush_fork(
>  		       !(iip->ili_fields & extflag[whichfork]));
>  		if ((iip->ili_fields & extflag[whichfork]) &&
>  		    (ifp->if_bytes > 0)) {
> -			ASSERT(xfs_iext_get_ext(ifp, 0));
>  			ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
>  			(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
>  				whichfork);
> @@ -812,33 +802,6 @@ xfs_iflush_fork(
>  	}
>  }
>  
> -/*
> - * Return a pointer to the extent record at file index idx.
> - */
> -xfs_bmbt_rec_host_t *
> -xfs_iext_get_ext(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx)		/* index of target extent */
> -{
> -	ASSERT(idx >= 0);
> -	ASSERT(idx < xfs_iext_count(ifp));
> -
> -	if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
> -		return ifp->if_u1.if_ext_irec->er_extbuf;
> -	} else if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_ext_irec_t	*erp;		/* irec pointer */
> -		int		erp_idx = 0;	/* irec index */
> -		xfs_extnum_t	page_idx = idx;	/* ext index in target list */
> -
> -		erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0);
> -		return &erp->er_extbuf[page_idx];
> -	} else if (ifp->if_bytes) {
> -		return &ifp->if_u1.if_extents[idx];
> -	} else {
> -		return NULL;
> -	}
> -}
> -
>  /* Convert bmap state flags to an inode fork. */
>  struct xfs_ifork *
>  xfs_iext_state_to_fork(
> @@ -852,894 +815,6 @@ xfs_iext_state_to_fork(
>  	return &ip->i_df;
>  }
>  
> -/*
> - * Insert new item(s) into the extent records for incore inode
> - * fork 'ifp'.  'count' new items are inserted at index 'idx'.
> - */
> -void
> -xfs_iext_insert(
> -	xfs_inode_t	*ip,		/* incore inode pointer */
> -	struct xfs_iext_cursor *cur,
> -	xfs_extnum_t	count,		/* number of inserted items */
> -	xfs_bmbt_irec_t	*new,		/* items to insert */
> -	int		state)		/* type of extent conversion */
> -{
> -	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
> -	xfs_extnum_t	i;		/* extent record index */
> -
> -	trace_xfs_iext_insert(ip, cur->idx, new, state, _RET_IP_);
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
> -	xfs_iext_add(ifp, cur->idx, count);
> -	for (i = 0; i < count; i++, new++)
> -		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx + i), new);
> -}
> -
> -/*
> - * This is called when the amount of space required for incore file
> - * extents needs to be increased. The ext_diff parameter stores the
> - * number of new extents being added and the idx parameter contains
> - * the extent index where the new extents will be added. If the new
> - * extents are being appended, then we just need to (re)allocate and
> - * initialize the space. Otherwise, if the new extents are being
> - * inserted into the middle of the existing entries, a bit more work
> - * is required to make room for the new extents to be inserted. The
> - * caller is responsible for filling in the new extent entries upon
> - * return.
> - */
> -void
> -xfs_iext_add(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin adding exts */
> -	int		ext_diff)	/* number of extents to add */
> -{
> -	int		byte_diff;	/* new bytes being added */
> -	int		new_size;	/* size of extents after adding */
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT((idx >= 0) && (idx <= nextents));
> -	byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
> -	new_size = ifp->if_bytes + byte_diff;
> -
> -	/*
> -	 * Use a linear (direct) extent list.
> -	 * If the extents are currently inside the inode,
> -	 * xfs_iext_realloc_direct will switch us from
> -	 * inline to direct extent allocation mode.
> -	 */
> -	if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
> -		xfs_iext_realloc_direct(ifp, new_size);
> -		if (idx < nextents) {
> -			memmove(&ifp->if_u1.if_extents[idx + ext_diff],
> -				&ifp->if_u1.if_extents[idx],
> -				(nextents - idx) * sizeof(xfs_bmbt_rec_t));
> -			memset(&ifp->if_u1.if_extents[idx], 0, byte_diff);
> -		}
> -	}
> -	/* Indirection array */
> -	else {
> -		xfs_ext_irec_t	*erp;
> -		int		erp_idx = 0;
> -		int		page_idx = idx;
> -
> -		ASSERT(nextents + ext_diff > XFS_LINEAR_EXTS);
> -		if (ifp->if_flags & XFS_IFEXTIREC) {
> -			erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 1);
> -		} else {
> -			xfs_iext_irec_init(ifp);
> -			ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -			erp = ifp->if_u1.if_ext_irec;
> -		}
> -		/* Extents fit in target extent page */
> -		if (erp && erp->er_extcount + ext_diff <= XFS_LINEAR_EXTS) {
> -			if (page_idx < erp->er_extcount) {
> -				memmove(&erp->er_extbuf[page_idx + ext_diff],
> -					&erp->er_extbuf[page_idx],
> -					(erp->er_extcount - page_idx) *
> -					sizeof(xfs_bmbt_rec_t));
> -				memset(&erp->er_extbuf[page_idx], 0, byte_diff);
> -			}
> -			erp->er_extcount += ext_diff;
> -			xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
> -		}
> -		/* Insert a new extent page */
> -		else if (erp) {
> -			xfs_iext_add_indirect_multi(ifp,
> -				erp_idx, page_idx, ext_diff);
> -		}
> -		/*
> -		 * If extent(s) are being appended to the last page in
> -		 * the indirection array and the new extent(s) don't fit
> -		 * in the page, then erp is NULL and erp_idx is set to
> -		 * the next index needed in the indirection array.
> -		 */
> -		else {
> -			uint	count = ext_diff;
> -
> -			while (count) {
> -				erp = xfs_iext_irec_new(ifp, erp_idx);
> -				erp->er_extcount = min(count, XFS_LINEAR_EXTS);
> -				count -= erp->er_extcount;
> -				if (count)
> -					erp_idx++;
> -			}
> -		}
> -	}
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * This is called when incore extents are being added to the indirection
> - * array and the new extents do not fit in the target extent list. The
> - * erp_idx parameter contains the irec index for the target extent list
> - * in the indirection array, and the idx parameter contains the extent
> - * index within the list. The number of extents being added is stored
> - * in the count parameter.
> - *
> - *    |-------|   |-------|
> - *    |       |   |       |    idx - number of extents before idx
> - *    |  idx  |   | count |
> - *    |       |   |       |    count - number of extents being inserted at idx
> - *    |-------|   |-------|
> - *    | count |   | nex2  |    nex2 - number of extents after idx + count
> - *    |-------|   |-------|
> - */
> -void
> -xfs_iext_add_indirect_multi(
> -	xfs_ifork_t	*ifp,			/* inode fork pointer */
> -	int		erp_idx,		/* target extent irec index */
> -	xfs_extnum_t	idx,			/* index within target list */
> -	int		count)			/* new extents being added */
> -{
> -	int		byte_diff;		/* new bytes being added */
> -	xfs_ext_irec_t	*erp;			/* pointer to irec entry */
> -	xfs_extnum_t	ext_diff;		/* number of extents to add */
> -	xfs_extnum_t	ext_cnt;		/* new extents still needed */
> -	xfs_extnum_t	nex2;			/* extents after idx + count */
> -	xfs_bmbt_rec_t	*nex2_ep = NULL;	/* temp list for nex2 extents */
> -	int		nlists;			/* number of irec's (lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -	nex2 = erp->er_extcount - idx;
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -
> -	/*
> -	 * Save second part of target extent list
> -	 * (all extents past */
> -	if (nex2) {
> -		byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
> -		nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_NOFS);
> -		memmove(nex2_ep, &erp->er_extbuf[idx], byte_diff);
> -		erp->er_extcount -= nex2;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -nex2);
> -		memset(&erp->er_extbuf[idx], 0, byte_diff);
> -	}
> -
> -	/*
> -	 * Add the new extents to the end of the target
> -	 * list, then allocate new irec record(s) and
> -	 * extent buffer(s) as needed to store the rest
> -	 * of the new extents.
> -	 */
> -	ext_cnt = count;
> -	ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS - erp->er_extcount);
> -	if (ext_diff) {
> -		erp->er_extcount += ext_diff;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
> -		ext_cnt -= ext_diff;
> -	}
> -	while (ext_cnt) {
> -		erp_idx++;
> -		erp = xfs_iext_irec_new(ifp, erp_idx);
> -		ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS);
> -		erp->er_extcount = ext_diff;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
> -		ext_cnt -= ext_diff;
> -	}
> -
> -	/* Add nex2 extents back to indirection array */
> -	if (nex2) {
> -		xfs_extnum_t	ext_avail;
> -		int		i;
> -
> -		byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
> -		ext_avail = XFS_LINEAR_EXTS - erp->er_extcount;
> -		i = 0;
> -		/*
> -		 * If nex2 extents fit in the current page, append
> -		 * nex2_ep after the new extents.
> -		 */
> -		if (nex2 <= ext_avail) {
> -			i = erp->er_extcount;
> -		}
> -		/*
> -		 * Otherwise, check if space is available in the
> -		 * next page.
> -		 */
> -		else if ((erp_idx < nlists - 1) &&
> -			 (nex2 <= (ext_avail = XFS_LINEAR_EXTS -
> -			  ifp->if_u1.if_ext_irec[erp_idx+1].er_extcount))) {
> -			erp_idx++;
> -			erp++;
> -			/* Create a hole for nex2 extents */
> -			memmove(&erp->er_extbuf[nex2], erp->er_extbuf,
> -				erp->er_extcount * sizeof(xfs_bmbt_rec_t));
> -		}
> -		/*
> -		 * Final choice, create a new extent page for
> -		 * nex2 extents.
> -		 */
> -		else {
> -			erp_idx++;
> -			erp = xfs_iext_irec_new(ifp, erp_idx);
> -		}
> -		memmove(&erp->er_extbuf[i], nex2_ep, byte_diff);
> -		kmem_free(nex2_ep);
> -		erp->er_extcount += nex2;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2);
> -	}
> -}
> -
> -/*
> - * This is called when the amount of space required for incore file
> - * extents needs to be decreased. The ext_diff parameter stores the
> - * number of extents to be removed and the idx parameter contains
> - * the extent index where the extents will be removed from.
> - *
> - * If the amount of space needed has decreased below the linear
> - * limit, XFS_IEXT_BUFSZ, then switch to using the contiguous
> - * extent array.  Otherwise, use kmem_realloc() to adjust the
> - * size to what is needed.
> - */
> -void
> -xfs_iext_remove(
> -	xfs_inode_t	*ip,		/* incore inode pointer */
> -	struct xfs_iext_cursor *cur,
> -	int		ext_diff,	/* number of extents to remove */
> -	int		state)		/* type of extent conversion */
> -{
> -	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		new_size;	/* size of extents after removal */
> -
> -	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
> -
> -	ASSERT(ext_diff > 0);
> -	nextents = xfs_iext_count(ifp);
> -	new_size = (nextents - ext_diff) * sizeof(xfs_bmbt_rec_t);
> -
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
> -	} else if (ifp->if_real_bytes) {
> -		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
> -	}
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * This removes ext_diff extents from a linear (direct) extent list,
> - * beginning at extent index idx. If the extents are being removed
> - * from the end of the list (ie. truncate) then we just need to re-
> - * allocate the list to remove the extra space. Otherwise, if the
> - * extents are being removed from the middle of the existing extent
> - * entries, then we first need to move the extent records beginning
> - * at idx + ext_diff up in the list to overwrite the records being
> - * removed, then remove the extra space via kmem_realloc.
> - */
> -void
> -xfs_iext_remove_direct(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin removing exts */
> -	int		ext_diff)	/* number of extents to remove */
> -{
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		new_size;	/* size of extents after removal */
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
> -	new_size = ifp->if_bytes -
> -		(ext_diff * sizeof(xfs_bmbt_rec_t));
> -	nextents = xfs_iext_count(ifp);
> -
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -		return;
> -	}
> -	/* Move extents up in the list (if needed) */
> -	if (idx + ext_diff < nextents) {
> -		memmove(&ifp->if_u1.if_extents[idx],
> -			&ifp->if_u1.if_extents[idx + ext_diff],
> -			(nextents - (idx + ext_diff)) *
> -			 sizeof(xfs_bmbt_rec_t));
> -	}
> -	memset(&ifp->if_u1.if_extents[nextents - ext_diff],
> -		0, ext_diff * sizeof(xfs_bmbt_rec_t));
> -	/*
> -	 * Reallocate the direct extent list. If the extents
> -	 * will fit inside the inode then xfs_iext_realloc_direct
> -	 * will switch from direct to inline extent allocation
> -	 * mode for us.
> -	 */
> -	xfs_iext_realloc_direct(ifp, new_size);
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * This is called when incore extents are being removed from the
> - * indirection array and the extents being removed span multiple extent
> - * buffers. The idx parameter contains the file extent index where we
> - * want to begin removing extents, and the count parameter contains
> - * how many extents need to be removed.
> - *
> - *    |-------|   |-------|
> - *    | nex1  |   |       |    nex1 - number of extents before idx
> - *    |-------|   | count |
> - *    |       |   |       |    count - number of extents being removed at idx
> - *    | count |   |-------|
> - *    |       |   | nex2  |    nex2 - number of extents after idx + count
> - *    |-------|   |-------|
> - */
> -void
> -xfs_iext_remove_indirect(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin removing extents */
> -	int		count)		/* number of extents to remove */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	int		erp_idx = 0;	/* indirection array index */
> -	xfs_extnum_t	ext_cnt;	/* extents left to remove */
> -	xfs_extnum_t	ext_diff;	/* extents to remove in current list */
> -	xfs_extnum_t	nex1;		/* number of extents before idx */
> -	xfs_extnum_t	nex2;		/* extents after idx + count */
> -	int		page_idx = idx;	/* index in target extent list */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	erp = xfs_iext_idx_to_irec(ifp,  &page_idx, &erp_idx, 0);
> -	ASSERT(erp != NULL);
> -	nex1 = page_idx;
> -	ext_cnt = count;
> -	while (ext_cnt) {
> -		nex2 = MAX((erp->er_extcount - (nex1 + ext_cnt)), 0);
> -		ext_diff = MIN(ext_cnt, (erp->er_extcount - nex1));
> -		/*
> -		 * Check for deletion of entire list;
> -		 * xfs_iext_irec_remove() updates extent offsets.
> -		 */
> -		if (ext_diff == erp->er_extcount) {
> -			xfs_iext_irec_remove(ifp, erp_idx);
> -			ext_cnt -= ext_diff;
> -			nex1 = 0;
> -			if (ext_cnt) {
> -				ASSERT(erp_idx < ifp->if_real_bytes /
> -					XFS_IEXT_BUFSZ);
> -				erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -				nex1 = 0;
> -				continue;
> -			} else {
> -				break;
> -			}
> -		}
> -		/* Move extents up (if needed) */
> -		if (nex2) {
> -			memmove(&erp->er_extbuf[nex1],
> -				&erp->er_extbuf[nex1 + ext_diff],
> -				nex2 * sizeof(xfs_bmbt_rec_t));
> -		}
> -		/* Zero out rest of page */
> -		memset(&erp->er_extbuf[nex1 + nex2], 0, (XFS_IEXT_BUFSZ -
> -			((nex1 + nex2) * sizeof(xfs_bmbt_rec_t))));
> -		/* Update remaining counters */
> -		erp->er_extcount -= ext_diff;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -ext_diff);
> -		ext_cnt -= ext_diff;
> -		nex1 = 0;
> -		erp_idx++;
> -		erp++;
> -	}
> -	ifp->if_bytes -= count * sizeof(xfs_bmbt_rec_t);
> -	xfs_iext_irec_compact(ifp);
> -}
> -
> -/*
> - * Create, destroy, or resize a linear (direct) block of extents.
> - */
> -void
> -xfs_iext_realloc_direct(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		new_size)	/* new size of extents after adding */
> -{
> -	int		rnew_size;	/* real new size of extents */
> -
> -	rnew_size = new_size;
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC) ||
> -		((new_size >= 0) && (new_size <= XFS_IEXT_BUFSZ) &&
> -		 (new_size != ifp->if_real_bytes)));
> -
> -	/* Free extent records */
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else {
> -		if (!is_power_of_2(new_size)){
> -			rnew_size = roundup_pow_of_two(new_size);
> -		}
> -		if (rnew_size != ifp->if_real_bytes) {
> -			ifp->if_u1.if_extents =
> -				kmem_realloc(ifp->if_u1.if_extents,
> -						rnew_size, KM_NOFS);
> -		}
> -		if (rnew_size > ifp->if_real_bytes) {
> -			memset(&ifp->if_u1.if_extents[ifp->if_bytes /
> -				(uint)sizeof(xfs_bmbt_rec_t)], 0,
> -				rnew_size - ifp->if_real_bytes);
> -		}
> -	}
> -	ifp->if_real_bytes = rnew_size;
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * Resize an extent indirection array to new_size bytes.
> - */
> -STATIC void
> -xfs_iext_realloc_indirect(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		new_size)	/* new indirection array size */
> -{
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	ASSERT(ifp->if_real_bytes);
> -	ASSERT((new_size >= 0) &&
> -	       (new_size != ((ifp->if_real_bytes / XFS_IEXT_BUFSZ) *
> -			     sizeof(xfs_ext_irec_t))));
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else {
> -		ifp->if_u1.if_ext_irec =
> -			kmem_realloc(ifp->if_u1.if_ext_irec, new_size, KM_NOFS);
> -	}
> -}
> -
> -/*
> - * Switch from indirection array to linear (direct) extent allocations.
> - */
> -STATIC void
> -xfs_iext_indirect_to_direct(
> -	 xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_bmbt_rec_host_t *ep;	/* extent record pointer */
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		size;		/* size of file extents */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT(nextents <= XFS_LINEAR_EXTS);
> -	size = nextents * sizeof(xfs_bmbt_rec_t);
> -
> -	xfs_iext_irec_compact_pages(ifp);
> -	ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ);
> -
> -	ep = ifp->if_u1.if_ext_irec->er_extbuf;
> -	kmem_free(ifp->if_u1.if_ext_irec);
> -	ifp->if_flags &= ~XFS_IFEXTIREC;
> -	ifp->if_u1.if_extents = ep;
> -	ifp->if_bytes = size;
> -	if (nextents < XFS_LINEAR_EXTS) {
> -		xfs_iext_realloc_direct(ifp, size);
> -	}
> -}
> -
> -/*
> - * Remove all records from the indirection array.
> - */
> -STATIC void
> -xfs_iext_irec_remove_all(
> -	struct xfs_ifork *ifp)
> -{
> -	int		nlists;
> -	int		i;
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	for (i = 0; i < nlists; i++)
> -		kmem_free(ifp->if_u1.if_ext_irec[i].er_extbuf);
> -	kmem_free(ifp->if_u1.if_ext_irec);
> -	ifp->if_flags &= ~XFS_IFEXTIREC;
> -}
> -
> -/*
> - * Free incore file extents.
> - */
> -void
> -xfs_iext_destroy(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_iext_irec_remove_all(ifp);
> -	} else if (ifp->if_real_bytes) {
> -		kmem_free(ifp->if_u1.if_extents);
> -	}
> -	ifp->if_u1.if_extents = NULL;
> -	ifp->if_real_bytes = 0;
> -	ifp->if_bytes = 0;
> -}
> -
> -/*
> - * Return a pointer to the extent record for file system block bno.
> - */
> -xfs_bmbt_rec_host_t *			/* pointer to found extent record */
> -xfs_iext_bno_to_ext(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_fileoff_t	bno,		/* block number to search for */
> -	xfs_extnum_t	*idxp)		/* index of target extent */
> -{
> -	xfs_bmbt_rec_host_t *base;	/* pointer to first extent */
> -	xfs_filblks_t	blockcount = 0;	/* number of blocks in extent */
> -	xfs_bmbt_rec_host_t *ep = NULL;	/* pointer to target extent */
> -	xfs_ext_irec_t	*erp = NULL;	/* indirection array pointer */
> -	int		high;		/* upper boundary in search */
> -	xfs_extnum_t	idx = 0;	/* index of target extent */
> -	int		low;		/* lower boundary in search */
> -	xfs_extnum_t	nextents;	/* number of file extents */
> -	xfs_fileoff_t	startoff = 0;	/* start offset of extent */
> -
> -	nextents = xfs_iext_count(ifp);
> -	if (nextents == 0) {
> -		*idxp = 0;
> -		return NULL;
> -	}
> -	low = 0;
> -	if (ifp->if_flags & XFS_IFEXTIREC) {
> -		/* Find target extent list */
> -		int	erp_idx = 0;
> -		erp = xfs_iext_bno_to_irec(ifp, bno, &erp_idx);
> -		base = erp->er_extbuf;
> -		high = erp->er_extcount - 1;
> -	} else {
> -		base = ifp->if_u1.if_extents;
> -		high = nextents - 1;
> -	}
> -	/* Binary search extent records */
> -	while (low <= high) {
> -		idx = (low + high) >> 1;
> -		ep = base + idx;
> -		startoff = xfs_bmbt_get_startoff(ep);
> -		blockcount = xfs_bmbt_get_blockcount(ep);
> -		if (bno < startoff) {
> -			high = idx - 1;
> -		} else if (bno >= startoff + blockcount) {
> -			low = idx + 1;
> -		} else {
> -			/* Convert back to file-based extent index */
> -			if (ifp->if_flags & XFS_IFEXTIREC) {
> -				idx += erp->er_extoff;
> -			}
> -			*idxp = idx;
> -			return ep;
> -		}
> -	}
> -	/* Convert back to file-based extent index */
> -	if (ifp->if_flags & XFS_IFEXTIREC) {
> -		idx += erp->er_extoff;
> -	}
> -	if (bno >= startoff + blockcount) {
> -		if (++idx == nextents) {
> -			ep = NULL;
> -		} else {
> -			ep = xfs_iext_get_ext(ifp, idx);
> -		}
> -	}
> -	*idxp = idx;
> -	return ep;
> -}
> -
> -/*
> - * Return a pointer to the indirection array entry containing the
> - * extent record for filesystem block bno. Store the index of the
> - * target irec in *erp_idxp.
> - */
> -xfs_ext_irec_t *			/* pointer to found extent record */
> -xfs_iext_bno_to_irec(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_fileoff_t	bno,		/* block number to search for */
> -	int		*erp_idxp)	/* irec index of target ext list */
> -{
> -	xfs_ext_irec_t	*erp = NULL;	/* indirection array pointer */
> -	xfs_ext_irec_t	*erp_next;	/* next indirection array entry */
> -	int		erp_idx;	/* indirection array index */
> -	int		nlists;		/* number of extent irec's (lists) */
> -	int		high;		/* binary search upper limit */
> -	int		low;		/* binary search lower limit */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	erp_idx = 0;
> -	low = 0;
> -	high = nlists - 1;
> -	while (low <= high) {
> -		erp_idx = (low + high) >> 1;
> -		erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -		erp_next = erp_idx < nlists - 1 ? erp + 1 : NULL;
> -		if (bno < xfs_bmbt_get_startoff(erp->er_extbuf)) {
> -			high = erp_idx - 1;
> -		} else if (erp_next && bno >=
> -			   xfs_bmbt_get_startoff(erp_next->er_extbuf)) {
> -			low = erp_idx + 1;
> -		} else {
> -			break;
> -		}
> -	}
> -	*erp_idxp = erp_idx;
> -	return erp;
> -}
> -
> -/*
> - * Return a pointer to the indirection array entry containing the
> - * extent record at file extent index *idxp. Store the index of the
> - * target irec in *erp_idxp and store the page index of the target
> - * extent record in *idxp.
> - */
> -xfs_ext_irec_t *
> -xfs_iext_idx_to_irec(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	*idxp,		/* extent index (file -> page) */
> -	int		*erp_idxp,	/* pointer to target irec */
> -	int		realloc)	/* new bytes were just added */
> -{
> -	xfs_ext_irec_t	*prev;		/* pointer to previous irec */
> -	xfs_ext_irec_t	*erp = NULL;	/* pointer to current irec */
> -	int		erp_idx;	/* indirection array index */
> -	int		nlists;		/* number of irec's (ex lists) */
> -	int		high;		/* binary search upper limit */
> -	int		low;		/* binary search lower limit */
> -	xfs_extnum_t	page_idx = *idxp; /* extent index in target list */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	ASSERT(page_idx >= 0);
> -	ASSERT(page_idx <= xfs_iext_count(ifp));
> -	ASSERT(page_idx < xfs_iext_count(ifp) || realloc);
> -
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	erp_idx = 0;
> -	low = 0;
> -	high = nlists - 1;
> -
> -	/* Binary search extent irec's */
> -	while (low <= high) {
> -		erp_idx = (low + high) >> 1;
> -		erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -		prev = erp_idx > 0 ? erp - 1 : NULL;
> -		if (page_idx < erp->er_extoff || (page_idx == erp->er_extoff &&
> -		     realloc && prev && prev->er_extcount < XFS_LINEAR_EXTS)) {
> -			high = erp_idx - 1;
> -		} else if (page_idx > erp->er_extoff + erp->er_extcount ||
> -			   (page_idx == erp->er_extoff + erp->er_extcount &&
> -			    !realloc)) {
> -			low = erp_idx + 1;
> -		} else if (page_idx == erp->er_extoff + erp->er_extcount &&
> -			   erp->er_extcount == XFS_LINEAR_EXTS) {
> -			ASSERT(realloc);
> -			page_idx = 0;
> -			erp_idx++;
> -			erp = erp_idx < nlists ? erp + 1 : NULL;
> -			break;
> -		} else {
> -			page_idx -= erp->er_extoff;
> -			break;
> -		}
> -	}
> -	*idxp = page_idx;
> -	*erp_idxp = erp_idx;
> -	return erp;
> -}
> -
> -/*
> - * Allocate and initialize an indirection array once the space needed
> - * for incore extents increases above XFS_IEXT_BUFSZ.
> - */
> -void
> -xfs_iext_irec_init(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT(nextents <= XFS_LINEAR_EXTS);
> -
> -	erp = kmem_alloc(sizeof(xfs_ext_irec_t), KM_NOFS);
> -
> -	if (nextents == 0) {
> -		ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
> -	} else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
> -		xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
> -	}
> -	erp->er_extbuf = ifp->if_u1.if_extents;
> -	erp->er_extcount = nextents;
> -	erp->er_extoff = 0;
> -
> -	ifp->if_flags |= XFS_IFEXTIREC;
> -	ifp->if_real_bytes = XFS_IEXT_BUFSZ;
> -	ifp->if_bytes = nextents * sizeof(xfs_bmbt_rec_t);
> -	ifp->if_u1.if_ext_irec = erp;
> -
> -	return;
> -}
> -
> -/*
> - * Allocate and initialize a new entry in the indirection array.
> - */
> -xfs_ext_irec_t *
> -xfs_iext_irec_new(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		erp_idx)	/* index for new irec */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	int		i;		/* loop counter */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -
> -	/* Resize indirection array */
> -	xfs_iext_realloc_indirect(ifp, ++nlists *
> -				  sizeof(xfs_ext_irec_t));
> -	/*
> -	 * Move records down in the array so the
> -	 * new page can use erp_idx.
> -	 */
> -	erp = ifp->if_u1.if_ext_irec;
> -	for (i = nlists - 1; i > erp_idx; i--) {
> -		memmove(&erp[i], &erp[i-1], sizeof(xfs_ext_irec_t));
> -	}
> -	ASSERT(i == erp_idx);
> -
> -	/* Initialize new extent record */
> -	erp = ifp->if_u1.if_ext_irec;
> -	erp[erp_idx].er_extbuf = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
> -	ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
> -	memset(erp[erp_idx].er_extbuf, 0, XFS_IEXT_BUFSZ);
> -	erp[erp_idx].er_extcount = 0;
> -	erp[erp_idx].er_extoff = erp_idx > 0 ?
> -		erp[erp_idx-1].er_extoff + erp[erp_idx-1].er_extcount : 0;
> -	return (&erp[erp_idx]);
> -}
> -
> -/*
> - * Remove a record from the indirection array.
> - */
> -void
> -xfs_iext_irec_remove(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		erp_idx)	/* irec index to remove */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	int		i;		/* loop counter */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -	if (erp->er_extbuf) {
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1,
> -			-erp->er_extcount);
> -		kmem_free(erp->er_extbuf);
> -	}
> -	/* Compact extent records */
> -	erp = ifp->if_u1.if_ext_irec;
> -	for (i = erp_idx; i < nlists - 1; i++) {
> -		memmove(&erp[i], &erp[i+1], sizeof(xfs_ext_irec_t));
> -	}
> -	/*
> -	 * Manually free the last extent record from the indirection
> -	 * array.  A call to xfs_iext_realloc_indirect() with a size
> -	 * of zero would result in a call to xfs_iext_destroy() which
> -	 * would in turn call this function again, creating a nasty
> -	 * infinite loop.
> -	 */
> -	if (--nlists) {
> -		xfs_iext_realloc_indirect(ifp,
> -			nlists * sizeof(xfs_ext_irec_t));
> -	} else {
> -		kmem_free(ifp->if_u1.if_ext_irec);
> -	}
> -	ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
> -}
> -
> -/*
> - * This is called to clean up large amounts of unused memory allocated
> - * by the indirection array.  Before compacting anything though, verify
> - * that the indirection array is still needed and switch back to the
> - * linear extent list (or even the inline buffer) if possible.  The
> - * compaction policy is as follows:
> - *
> - *    Full Compaction: Extents fit into a single page (or inline buffer)
> - * Partial Compaction: Extents occupy less than 50% of allocated space
> - *      No Compaction: Extents occupy at least 50% of allocated space
> - */
> -void
> -xfs_iext_irec_compact(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	nextents = xfs_iext_count(ifp);
> -
> -	if (nextents == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else if (nextents <= XFS_LINEAR_EXTS) {
> -		xfs_iext_indirect_to_direct(ifp);
> -	} else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
> -		xfs_iext_irec_compact_pages(ifp);
> -	}
> -}
> -
> -/*
> - * Combine extents from neighboring extent pages.
> - */
> -void
> -xfs_iext_irec_compact_pages(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_ext_irec_t	*erp, *erp_next;/* pointers to irec entries */
> -	int		erp_idx = 0;	/* indirection array index */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	while (erp_idx < nlists - 1) {
> -		erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -		erp_next = erp + 1;
> -		if (erp_next->er_extcount <=
> -		    (XFS_LINEAR_EXTS - erp->er_extcount)) {
> -			memcpy(&erp->er_extbuf[erp->er_extcount],
> -				erp_next->er_extbuf, erp_next->er_extcount *
> -				sizeof(xfs_bmbt_rec_t));
> -			erp->er_extcount += erp_next->er_extcount;
> -			/*
> -			 * Free page before removing extent record
> -			 * so er_extoffs don't get modified in
> -			 * xfs_iext_irec_remove.
> -			 */
> -			kmem_free(erp_next->er_extbuf);
> -			erp_next->er_extbuf = NULL;
> -			xfs_iext_irec_remove(ifp, erp_idx + 1);
> -			nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -		} else {
> -			erp_idx++;
> -		}
> -	}
> -}
> -
> -/*
> - * This is called to update the er_extoff field in the indirection
> - * array when extents have been added or removed from one of the
> - * extent lists. erp_idx contains the irec index to begin updating
> - * at and ext_diff contains the number of extents that were added
> - * or removed.
> - */
> -void
> -xfs_iext_irec_update_extoffs(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		erp_idx,	/* irec index to update */
> -	int		ext_diff)	/* number of new extents */
> -{
> -	int		i;		/* loop counter */
> -	int		nlists;		/* number of irec's (ex lists */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	for (i = erp_idx; i < nlists; i++) {
> -		ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
> -	}
> -}
> -
>  /*
>   * Initialize an inode's copy-on-write fork.
>   */
> @@ -1756,87 +831,3 @@ xfs_ifork_init_cow(
>  	ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
>  	ip->i_cnextents = 0;
>  }
> -
> -/*
> - * Lookup the extent covering bno.
> - *
> - * If there is an extent covering bno return the extent index, and store the
> - * expanded extent structure in *gotp, and the extent cursor in *cur.
> - * If there is no extent covering bno, but there is an extent after it (e.g.
> - * it lies in a hole) return that extent in *gotp and its cursor in *cur
> - * instead.
> - * If bno is beyond the last extent return false, and return an invalid
> - * cursor value.
> - */
> -bool
> -xfs_iext_lookup_extent(
> -	struct xfs_inode	*ip,
> -	struct xfs_ifork	*ifp,
> -	xfs_fileoff_t		bno,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	struct xfs_bmbt_rec_host *ep;
> -
> -	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
> -
> -	ep = xfs_iext_bno_to_ext(ifp, bno, &cur->idx);
> -	if (!ep)
> -		return false;
> -	xfs_bmbt_get_all(ep, gotp);
> -	return true;
> -}
> -
> -/*
> - * Returns the last extent before end, and if this extent doesn't cover
> - * end, update end to the end of the extent.
> - */
> -bool
> -xfs_iext_lookup_extent_before(
> -	struct xfs_inode	*ip,
> -	struct xfs_ifork	*ifp,
> -	xfs_fileoff_t		*end,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
> -	    gotp->br_startoff <= *end - 1)
> -		return true;
> -	if (!xfs_iext_prev_extent(ifp, cur, gotp))
> -		return false;
> -	*end = gotp->br_startoff + gotp->br_blockcount;
> -	return true;
> -}
> -
> -/*
> - * Return true if there is an extent at cursor cur and return the expanded
> - * extent structure at cur in gotp in that case.  Else return false.
> - */
> -bool
> -xfs_iext_get_extent(
> -	struct xfs_ifork	*ifp,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	if (cur->idx < 0 || cur->idx >= xfs_iext_count(ifp))
> -		return false;
> -	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
> -	return true;
> -}
> -
> -void
> -xfs_iext_update_extent(
> -	struct xfs_inode	*ip,
> -	int			state,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> -
> -	ASSERT(cur->idx >= 0);
> -	ASSERT(cur->idx < xfs_iext_count(ifp));
> -
> -	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
> -	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
> -	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
> -}
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index 508f13784334..015872caaab4 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
> @@ -21,45 +21,18 @@
>  struct xfs_inode_log_item;
>  struct xfs_dinode;
>  
> -/*
> - * The following xfs_ext_irec_t struct introduces a second (top) level
> - * to the in-core extent allocation scheme. These structs are allocated
> - * in a contiguous block, creating an indirection array where each entry
> - * (irec) contains a pointer to a buffer of in-core extent records which
> - * it manages. Each extent buffer is 4k in size, since 4k is the system
> - * page size on Linux i386 and systems with larger page sizes don't seem
> - * to gain much, if anything, by using their native page size as the
> - * extent buffer size. Also, using 4k extent buffers everywhere provides
> - * a consistent interface for CXFS across different platforms.
> - *
> - * There is currently no limit on the number of irec's (extent lists)
> - * allowed, so heavily fragmented files may require an indirection array
> - * which spans multiple system pages of memory. The number of extents
> - * which would require this amount of contiguous memory is very large
> - * and should not cause problems in the foreseeable future. However,
> - * if the memory needed for the contiguous array ever becomes a problem,
> - * it is possible that a third level of indirection may be required.
> - */
> -typedef struct xfs_ext_irec {
> -	xfs_bmbt_rec_host_t *er_extbuf;	/* block of extent records */
> -	xfs_extnum_t	er_extoff;	/* extent offset in file */
> -	xfs_extnum_t	er_extcount;	/* number of extents in page/block */
> -} xfs_ext_irec_t;
> -
>  /*
>   * File incore extent information, present for each of data & attr forks.
>   */
> -#define	XFS_IEXT_BUFSZ		4096
> -#define	XFS_LINEAR_EXTS		(XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t))
>  typedef struct xfs_ifork {
>  	int			if_bytes;	/* bytes in if_u1 */
>  	int			if_real_bytes;	/* bytes allocated in if_u1 */
>  	struct xfs_btree_block	*if_broot;	/* file's incore btree root */
>  	short			if_broot_bytes;	/* bytes allocated for root */
>  	unsigned char		if_flags;	/* per-fork flags */
> +	int			if_height;	/* height of the extent tree */
>  	union {
> -		xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */
> -		xfs_ext_irec_t	*if_ext_irec;	/* irec map file exts */
> +		void		*if_root;	/* extent tree root */
>  		char		*if_data;	/* inline file data */
>  	} if_u1;
>  } xfs_ifork_t;
> @@ -70,7 +43,6 @@ typedef struct xfs_ifork {
>  #define	XFS_IFINLINE	0x01	/* Inline data is read in */
>  #define	XFS_IFEXTENTS	0x02	/* All extent pointers are read in */
>  #define	XFS_IFBROOT	0x04	/* i_broot points to the bmap b-tree root */
> -#define	XFS_IFEXTIREC	0x08	/* Indirection array of extent blocks */
>  
>  /*
>   * Fork handling.
> @@ -140,35 +112,12 @@ int		xfs_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *,
>  				  int);
>  void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
>  
> -struct xfs_bmbt_rec_host *
> -		xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
> -xfs_extnum_t	xfs_iext_count(struct xfs_ifork *);
> +xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
>  void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
>  			xfs_extnum_t, struct xfs_bmbt_irec *, int);
> -void		xfs_iext_add(struct xfs_ifork *, xfs_extnum_t, int);
> -void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
> -					    xfs_extnum_t, int);
>  void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
>  			int, int);
> -void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
> -void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
> -void		xfs_iext_realloc_direct(struct xfs_ifork *, int);
>  void		xfs_iext_destroy(struct xfs_ifork *);
> -struct xfs_bmbt_rec_host *
> -		xfs_iext_bno_to_ext(struct xfs_ifork *, xfs_fileoff_t, int *);
> -struct xfs_ext_irec *
> -		xfs_iext_bno_to_irec(struct xfs_ifork *, xfs_fileoff_t, int *);
> -struct xfs_ext_irec *
> -		xfs_iext_idx_to_irec(struct xfs_ifork *, xfs_extnum_t *, int *,
> -				     int);
> -void		xfs_iext_irec_init(struct xfs_ifork *);
> -struct xfs_ext_irec *
> -		xfs_iext_irec_new(struct xfs_ifork *, int);
> -void		xfs_iext_irec_remove(struct xfs_ifork *, int);
> -void		xfs_iext_irec_compact(struct xfs_ifork *);
> -void		xfs_iext_irec_compact_pages(struct xfs_ifork *);
> -void		xfs_iext_irec_compact_full(struct xfs_ifork *);
> -void		xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
>  
>  bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
>  			struct xfs_ifork *ifp, xfs_fileoff_t bno,
> @@ -185,29 +134,10 @@ void		xfs_iext_update_extent(struct xfs_inode *ip, int state,
>  			struct xfs_iext_cursor *cur,
>  			struct xfs_bmbt_irec *gotp);
>  
> -static inline void xfs_iext_first(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx = 0;
> -}
> -
> -static inline void xfs_iext_last(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx = xfs_iext_count(ifp) - 1;
> -}
> -
> -static inline void xfs_iext_next(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx++;
> -}
> -
> -static inline void xfs_iext_prev(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx--;
> -}
> +void		xfs_iext_first(struct xfs_ifork *, struct xfs_iext_cursor *);
> +void		xfs_iext_last(struct xfs_ifork *, struct xfs_iext_cursor *);
> +void		xfs_iext_next(struct xfs_ifork *, struct xfs_iext_cursor *);
> +void		xfs_iext_prev(struct xfs_ifork *, struct xfs_iext_cursor *);
>  
>  static inline bool xfs_iext_next_extent(struct xfs_ifork *ifp,
>  		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
> diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
> index 5da6382bdaf1..983878019097 100644
> --- a/fs/xfs/libxfs/xfs_types.h
> +++ b/fs/xfs/libxfs/xfs_types.h
> @@ -143,7 +143,8 @@ typedef uint32_t	xfs_dqid_t;
>  #define	XFS_WORDMASK	((1 << XFS_WORDLOG) - 1)
>  
>  struct xfs_iext_cursor {
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_leaf	*leaf;
> +	int			pos;
>  };
>  
>  #endif	/* __XFS_TYPES_H__ */
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index 778b709dbd0c..72d753d898d3 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -168,7 +168,6 @@ xfs_scrub_bmapbt_rec(
>  	struct xfs_scrub_btree		*bs,
>  	union xfs_btree_rec		*rec)
>  {
> -	struct xfs_bmbt_rec_host	ihost;
>  	struct xfs_bmbt_irec		irec;
>  	struct xfs_scrub_bmap_info	*info = bs->private;
>  	struct xfs_inode		*ip = bs->cur->bc_private.b.ip;
> @@ -193,9 +192,7 @@ xfs_scrub_bmapbt_rec(
>  	}
>  
>  	/* Set up the in-core record and scrub it. */
> -	ihost.l0 = be64_to_cpu(rec->bmbt.l0);
> -	ihost.l1 = be64_to_cpu(rec->bmbt.l1);
> -	xfs_bmbt_get_all(&ihost, &irec);
> +	xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
>  	return xfs_scrub_bmap_extent(ip, bs->cur, info, &irec);
>  }
>  
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index a929ca72fa8e..5b5128ed0f18 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -933,7 +933,7 @@ xfs_ialloc(
>  		ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
>  		ip->i_df.if_flags = XFS_IFEXTENTS;
>  		ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
> -		ip->i_df.if_u1.if_extents = NULL;
> +		ip->i_df.if_u1.if_root = NULL;
>  		break;
>  	default:
>  		ASSERT(0);
> diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
> index eb6f4f7c9520..6ee5c3bf19ad 100644
> --- a/fs/xfs/xfs_inode_item.c
> +++ b/fs/xfs/xfs_inode_item.c
> @@ -162,7 +162,6 @@ xfs_inode_item_format_data_fork(
>  		    ip->i_df.if_bytes > 0) {
>  			struct xfs_bmbt_rec *p;
>  
> -			ASSERT(ip->i_df.if_u1.if_extents != NULL);
>  			ASSERT(xfs_iext_count(&ip->i_df) > 0);
>  
>  			p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IEXT);
> @@ -252,7 +251,6 @@ xfs_inode_item_format_attr_fork(
>  
>  			ASSERT(xfs_iext_count(ip->i_afp) ==
>  				ip->i_d.di_anextents);
> -			ASSERT(ip->i_afp->if_u1.if_extents != NULL);
>  
>  			p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_EXT);
>  			data_bytes = xfs_iextents_copy(ip, p, XFS_ATTR_FORK);
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index 667bfce802cd..515ba042d75c 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -218,45 +218,6 @@ TRACE_EVENT(xfs_attr_list_node_descend,
>  		   __entry->bt_before)
>  );
>  
> -TRACE_EVENT(xfs_iext_insert,
> -	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx,
> -		 struct xfs_bmbt_irec *r, int state, unsigned long caller_ip),
> -	TP_ARGS(ip, idx, r, state, caller_ip),
> -	TP_STRUCT__entry(
> -		__field(dev_t, dev)
> -		__field(xfs_ino_t, ino)
> -		__field(xfs_extnum_t, idx)
> -		__field(xfs_fileoff_t, startoff)
> -		__field(xfs_fsblock_t, startblock)
> -		__field(xfs_filblks_t, blockcount)
> -		__field(xfs_exntst_t, state)
> -		__field(int, bmap_state)
> -		__field(unsigned long, caller_ip)
> -	),
> -	TP_fast_assign(
> -		__entry->dev = VFS_I(ip)->i_sb->s_dev;
> -		__entry->ino = ip->i_ino;
> -		__entry->idx = idx;
> -		__entry->startoff = r->br_startoff;
> -		__entry->startblock = r->br_startblock;
> -		__entry->blockcount = r->br_blockcount;
> -		__entry->state = r->br_state;
> -		__entry->bmap_state = state;
> -		__entry->caller_ip = caller_ip;
> -	),
> -	TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
> -		  "offset %lld block %lld count %lld flag %d caller %ps",
> -		  MAJOR(__entry->dev), MINOR(__entry->dev),
> -		  __entry->ino,
> -		  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
> -		  (long)__entry->idx,
> -		  __entry->startoff,
> -		  (int64_t)__entry->startblock,
> -		  __entry->blockcount,
> -		  __entry->state,
> -		  (char *)__entry->caller_ip)
> -);
> -
>  DECLARE_EVENT_CLASS(xfs_bmap_class,
>  	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state,
>  		 unsigned long caller_ip),
> @@ -264,7 +225,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  	TP_STRUCT__entry(
>  		__field(dev_t, dev)
>  		__field(xfs_ino_t, ino)
> -		__field(xfs_extnum_t, idx)
> +		__field(void *, leaf);
> +		__field(int, pos);
>  		__field(xfs_fileoff_t, startoff)
>  		__field(xfs_fsblock_t, startblock)
>  		__field(xfs_filblks_t, blockcount)
> @@ -280,7 +242,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  		xfs_iext_get_extent(ifp, cur, &r);
>  		__entry->dev = VFS_I(ip)->i_sb->s_dev;
>  		__entry->ino = ip->i_ino;
> -		__entry->idx = cur->idx;
> +		__entry->leaf = cur->leaf;
> +		__entry->pos = cur->pos;
>  		__entry->startoff = r.br_startoff;
>  		__entry->startblock = r.br_startblock;
>  		__entry->blockcount = r.br_blockcount;
> @@ -288,12 +251,13 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  		__entry->bmap_state = state;
>  		__entry->caller_ip = caller_ip;
>  	),
> -	TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
> +	TP_printk("dev %d:%d ino 0x%llx state %s cur 0x%p/%d "
>  		  "offset %lld block %lld count %lld flag %d caller %ps",
>  		  MAJOR(__entry->dev), MINOR(__entry->dev),
>  		  __entry->ino,
>  		  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
> -		  (long)__entry->idx,
> +		  __entry->leaf,
> +		  __entry->pos,
>  		  __entry->startoff,
>  		  (int64_t)__entry->startblock,
>  		  __entry->blockcount,
> @@ -306,6 +270,7 @@ DEFINE_EVENT(xfs_bmap_class, name, \
>  	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state, \
>  		 unsigned long caller_ip), \
>  	TP_ARGS(ip, cur, state, caller_ip))
> +DEFINE_BMAP_EVENT(xfs_iext_insert);
>  DEFINE_BMAP_EVENT(xfs_iext_remove);
>  DEFINE_BMAP_EVENT(xfs_bmap_pre_update);
>  DEFINE_BMAP_EVENT(xfs_bmap_post_update);
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-01 13:58       ` Brian Foster
@ 2017-11-01 23:00         ` Darrick J. Wong
  2017-11-02 11:57           ` Brian Foster
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-01 23:00 UTC (permalink / raw)
  To: Brian Foster; +Cc: Christoph Hellwig, linux-xfs

On Wed, Nov 01, 2017 at 09:58:55AM -0400, Brian Foster wrote:
> On Tue, Oct 31, 2017 at 02:15:20PM -0700, Darrick J. Wong wrote:
> > On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> > > On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > > > This prepares for getting rid of the current in-memory extent format.
> > > > 
> > > 
> > > Couldn't we port this function over to use whatever the new in-memory
> > > extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> > > rather than the associated bit in the on-disk format..?
> > 
> > It's certainly possible, but in general verifiers are supposed to check
> > on-disk metadata before they end up in-core, and this would seem to get
> > us closer to that, right?
> > 
> 
> Yeah, but are any of these calls actually made in a buffer verifier
> path?

No.  They probably ought to be -- we can add them to the bmbt verifier and
the inode fork verifier (in the magical future when those exist :P), but
for now I think xfs_iread_extents is the closest we come to an "obvious"
place where we load disk data into/out of its in-core representation.

--D

> Brian
> 
> > --D
> > 
> > > Brian
> > > 
> > > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > > ---
> > > >  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
> > > >  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
> > > >  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
> > > >  3 files changed, 9 insertions(+), 10 deletions(-)
> > > > 
> > > > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > > > index f45f05c45e15..b2b6832b9e6b 100644
> > > > --- a/fs/xfs/libxfs/xfs_bmap.c
> > > > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > > > @@ -1259,14 +1259,14 @@ xfs_iread_extents(
> > > >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> > > >  		for (j = 0; j < num_recs; j++, i++, frp++) {
> > > >  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> > > > -			trp->l0 = be64_to_cpu(frp->l0);
> > > > -			trp->l1 = be64_to_cpu(frp->l1);
> > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
> > > >  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > >  				error = -EFSCORRUPTED;
> > > >  				goto out_brelse;
> > > >  			}
> > > > +			trp->l0 = be64_to_cpu(frp->l0);
> > > > +			trp->l1 = be64_to_cpu(frp->l1);
> > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > >  		}
> > > >  		xfs_trans_brelse(tp, bp);
> > > > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > index 6f891eeb88f6..2fbfe2a24b15 100644
> > > > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> > > >   * Check that the extent does not contain an invalid unwritten extent flag.
> > > >   */
> > > >  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > > > -		struct xfs_bmbt_rec_host *ep)
> > > > +		struct xfs_bmbt_rec *ep)
> > > >  {
> > > > -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > >  		return true;
> > > >  	if (whichfork == XFS_DATA_FORK &&
> > > >  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > > > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > index bb63f38b97cc..abe601b48c9c 100644
> > > > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > > > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > @@ -371,13 +371,13 @@ xfs_iformat_extents(
> > > >  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> > > >  		for (i = 0; i < nex; i++, dp++) {
> > > >  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > -			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > -			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
> > > >  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > >  				return -EFSCORRUPTED;
> > > >  			}
> > > > +			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > +			ep->l1 = get_unaligned_be64(&dp->l1);
> > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > >  		}
> > > >  	}
> > > > @@ -764,8 +764,6 @@ xfs_iextents_copy(
> > > >  	for (i = 0; i < nrecs; i++) {
> > > >  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > >  
> > > > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> > > > -
> > > >  		start_block = xfs_bmbt_get_startblock(ep);
> > > >  		if (isnullstartblock(start_block)) {
> > > >  			/*
> > > > @@ -779,6 +777,7 @@ xfs_iextents_copy(
> > > >  		/* Translate to on disk format */
> > > >  		put_unaligned_be64(ep->l0, &dp->l0);
> > > >  		put_unaligned_be64(ep->l1, &dp->l1);
> > > > +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> > > >  
> > > >  		dp++;
> > > >  		copied++;
> > > > -- 
> > > > 2.14.2
> > > > 
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 16/18] xfs: use a b+tree for the in-core extent list
  2017-10-31 14:22 ` [PATCH 16/18] xfs: use a b+tree for the in-core extent list Christoph Hellwig
  2017-11-01 18:47   ` Darrick J. Wong
@ 2017-11-02  0:14   ` Darrick J. Wong
  2017-11-02 19:09     ` Christoph Hellwig
  1 sibling, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-02  0:14 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:28PM +0200, Christoph Hellwig wrote:
> Replace the current linear list and the indirection array for the in-core
> extent list with a b+tree to avoid the need for larger memory allocations
> for the indirection array when lots of extents are present.  The current
> extent list implementations leads to heavy pressure on the memory
> allocator when modifying files with a high extent count, and can lead
> to high latencies because of that.
> 
> The replacement is a b+tree with a few quirks.  The leaf nodes directly
> store the extent record in two u64 values.  The encoding is a little bit
> different from the existing in-core extent records so that the start
> offset and length which are required for lookups can be retreived with
> simple mask operations.  The inner nodes store a 64-bit key containing
> the start offset in the first half of the node, and the pointers to the
> next lower level in the second half.  In either case we walk the node
> from the beginninig to the end and do a linear search, as that is more
> efficient for the low number of cache lines touched during a search
> (2 for the inner nodes, 4 for the leaf nodes) than a binary search.
> We store termination markers (zero length for the leaf nodes, an
> otherwise impossible high bit for the inner nodes) to terminate the key
> list / records instead of storing a count to use the available cache
> lines as efficiently as possible.
> 
> One quirk of the algorithm is that while we normally split a node half and
> half like usual btree implementations we just spill over entries added at
> the very end of the list to a new node on its own.  This means we get a
> 100% fill grade for the common cases of bulk inseration at reading an
> inode into memory, and when only sequentially appending to a file.  The
> downside is a slightly higher chance of splits on the first random
> inserations.
> 
> Both insert and removal manually recurse into the lower levels, but
> the bulk deletion of the whole tree is still implemented as a recursive
> function call, although one limited by the overall depth and with very
> little stack usage in every iteration.
> 
> For the first few extents we dynamically grow the list from a single
> extent to the next powers of two until we have a first full leaf block
> and that building the actual tree.
> 
> The code started out based on the generic lib/btree.c code from Joern
> Engel based on earlier work from Peter Zijlstra, but has since been
> rewritten beyond recognition.

Ok, time for a real review.

> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/Makefile                |    1 +
>  fs/xfs/libxfs/xfs_bmap.c       |   19 +-
>  fs/xfs/libxfs/xfs_bmap_btree.c |  103 +---
>  fs/xfs/libxfs/xfs_bmap_btree.h |    7 +-
>  fs/xfs/libxfs/xfs_format.h     |    4 -
>  fs/xfs/libxfs/xfs_iext_tree.c  | 1043 ++++++++++++++++++++++++++++++++++++++++
>  fs/xfs/libxfs/xfs_inode_fork.c | 1035 +--------------------------------------
>  fs/xfs/libxfs/xfs_inode_fork.h |   84 +---
>  fs/xfs/libxfs/xfs_types.h      |    3 +-
>  fs/xfs/scrub/bmap.c            |    5 +-
>  fs/xfs/xfs_inode.c             |    2 +-
>  fs/xfs/xfs_inode_item.c        |    2 -
>  fs/xfs/xfs_trace.h             |   51 +-
>  13 files changed, 1103 insertions(+), 1256 deletions(-)
>  create mode 100644 fs/xfs/libxfs/xfs_iext_tree.c
> 
> diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
> index a2a5d046793d..7ceb41a9786a 100644
> --- a/fs/xfs/Makefile
> +++ b/fs/xfs/Makefile
> @@ -49,6 +49,7 @@ xfs-y				+= $(addprefix libxfs/, \
>  				   xfs_dquot_buf.o \
>  				   xfs_ialloc.o \
>  				   xfs_ialloc_btree.o \
> +				   xfs_iext_tree.o \
>  				   xfs_inode_fork.o \
>  				   xfs_inode_buf.o \
>  				   xfs_log_rlimit.o \
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index e020bd3f8717..46bda00dca79 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -805,6 +805,8 @@ xfs_bmap_local_to_extents_empty(
>  	xfs_bmap_forkoff_reset(ip, whichfork);
>  	ifp->if_flags &= ~XFS_IFINLINE;
>  	ifp->if_flags |= XFS_IFEXTENTS;
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  	XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
>  }
>  
> @@ -846,8 +848,7 @@ xfs_bmap_local_to_extents(
>  
>  	flags = 0;
>  	error = 0;
> -	ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) ==
> -								XFS_IFINLINE);
> +	ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS)) == XFS_IFINLINE);
>  	memset(&args, 0, sizeof(args));
>  	args.tp = tp;
>  	args.mp = ip->i_mount;
> @@ -891,6 +892,9 @@ xfs_bmap_local_to_extents(
>  	xfs_bmap_local_to_extents_empty(ip, whichfork);
>  	flags |= XFS_ILOG_CORE;
>  
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
> +
>  	rec.br_startoff = 0;
>  	rec.br_startblock = args.fsbno;
>  	rec.br_blockcount = 1;
> @@ -1177,6 +1181,7 @@ xfs_iread_extents(
>  	xfs_extnum_t		nextents = XFS_IFORK_NEXTENTS(ip, whichfork);
>  	struct xfs_btree_block	*block = ifp->if_broot;
>  	struct xfs_iext_cursor	ext;
> +	struct xfs_bmbt_irec	new;
>  	xfs_fsblock_t		bno;
>  	struct xfs_buf		*bp;
>  	xfs_extnum_t		i, j;
> @@ -1193,7 +1198,8 @@ xfs_iread_extents(
>  
>  	ifp->if_bytes = 0;
>  	ifp->if_real_bytes = 0;
> -	xfs_iext_add(ifp, 0, nextents);
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  
>  	/*
>  	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
> @@ -1258,16 +1264,15 @@ xfs_iread_extents(
>  		 * Copy records into the extent records.
>  		 */
>  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> -		for (j = 0; j < num_recs; j++, i++, frp++) {
> -			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> +		for (j = 0; j < num_recs; j++, frp++, i++) {
>  			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
>  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				error = -EFSCORRUPTED;
>  				goto out_brelse;
>  			}
> -			trp->l0 = be64_to_cpu(frp->l0);
> -			trp->l1 = be64_to_cpu(frp->l1);
> +			xfs_bmbt_disk_get_all(frp, &new);
> +			xfs_iext_insert(ip, &ext, 1, &new, state);
>  			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
>  			xfs_iext_next(ifp, &ext);
>  		}
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
> index 89260972a0f6..c10aecaaae44 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.c
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.c
> @@ -71,73 +71,21 @@ xfs_bmdr_to_bmbt(
>  	memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
>  }
>  
> -/*
> - * Convert a compressed bmap extent record to an uncompressed form.
> - * This code must be in sync with the routines xfs_bmbt_get_startoff,
> - * xfs_bmbt_get_startblock and xfs_bmbt_get_blockcount.
> - */
> -STATIC void
> -__xfs_bmbt_get_all(
> -		uint64_t l0,
> -		uint64_t l1,
> -		xfs_bmbt_irec_t *s)
> -{
> -	int	ext_flag;
> -	xfs_exntst_t st;
> -
> -	ext_flag = (int)(l0 >> (64 - BMBT_EXNTFLAG_BITLEN));
> -	s->br_startoff = ((xfs_fileoff_t)l0 &
> -			   xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
> -	s->br_startblock = (((xfs_fsblock_t)l0 & xfs_mask64lo(9)) << 43) |
> -			   (((xfs_fsblock_t)l1) >> 21);
> -	s->br_blockcount = (xfs_filblks_t)(l1 & xfs_mask64lo(21));
> -	/* This is xfs_extent_state() in-line */
> -	if (ext_flag) {
> -		ASSERT(s->br_blockcount != 0);	/* saved for DMIG */
> -		st = XFS_EXT_UNWRITTEN;
> -	} else
> -		st = XFS_EXT_NORM;
> -	s->br_state = st;
> -}
> -
>  void
> -xfs_bmbt_get_all(
> -	xfs_bmbt_rec_host_t *r,
> -	xfs_bmbt_irec_t *s)
> -{
> -	__xfs_bmbt_get_all(r->l0, r->l1, s);
> -}
> -
> -/*
> - * Extract the blockcount field from an in memory bmap extent record.
> - */
> -xfs_filblks_t
> -xfs_bmbt_get_blockcount(
> -	xfs_bmbt_rec_host_t	*r)
> -{
> -	return (xfs_filblks_t)(r->l1 & xfs_mask64lo(21));
> -}
> -
> -/*
> - * Extract the startblock field from an in memory bmap extent record.
> - */
> -xfs_fsblock_t
> -xfs_bmbt_get_startblock(
> -	xfs_bmbt_rec_host_t	*r)
> -{
> -	return (((xfs_fsblock_t)r->l0 & xfs_mask64lo(9)) << 43) |
> -	       (((xfs_fsblock_t)r->l1) >> 21);
> -}
> -
> -/*
> - * Extract the startoff field from an in memory bmap extent record.
> - */
> -xfs_fileoff_t
> -xfs_bmbt_get_startoff(
> -	xfs_bmbt_rec_host_t	*r)
> -{
> -	return ((xfs_fileoff_t)r->l0 &
> -		 xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
> +xfs_bmbt_disk_get_all(
> +	struct xfs_bmbt_rec	*rec,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	uint64_t		l0 = get_unaligned_be64(&rec->l0);
> +	uint64_t		l1 = get_unaligned_be64(&rec->l1);
> +
> +	irec->br_startoff = (l0 & xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
> +	irec->br_startblock = ((l0 & xfs_mask64lo(9)) << 43) | (l1 >> 21);
> +	irec->br_blockcount = l1 & xfs_mask64lo(21);
> +	if (l0 >> (64 - BMBT_EXNTFLAG_BITLEN))
> +		irec->br_state = XFS_EXT_UNWRITTEN;
> +	else
> +		irec->br_state = XFS_EXT_NORM;
>  }
>  
>  /*
> @@ -161,29 +109,6 @@ xfs_bmbt_disk_get_startoff(
>  		 xfs_mask64lo(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
>  }
>  
> -/*
> - * Set all the fields in a bmap extent record from the uncompressed form.
> - */
> -void
> -xfs_bmbt_set_all(
> -	struct xfs_bmbt_rec_host *r,
> -	struct xfs_bmbt_irec	*s)
> -{
> -	int			extent_flag = (s->br_state != XFS_EXT_NORM);
> -
> -	ASSERT(s->br_state == XFS_EXT_NORM || s->br_state == XFS_EXT_UNWRITTEN);
> -	ASSERT(!(s->br_startoff & xfs_mask64hi(64-BMBT_STARTOFF_BITLEN)));
> -	ASSERT(!(s->br_blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)));
> -	ASSERT(!(s->br_startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)));
> -
> -	r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
> -		 ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
> -		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43);
> -	r->l1 = ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
> -		 ((xfs_bmbt_rec_base_t)s->br_blockcount &
> -		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21));
> -}
> -
>  /*
>   * Set all the fields in a bmap extent record from the uncompressed form.
>   */
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> index 2fbfe2a24b15..714bfbaf9b2d 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> @@ -98,16 +98,11 @@ struct xfs_trans;
>   */
>  extern void xfs_bmdr_to_bmbt(struct xfs_inode *, xfs_bmdr_block_t *, int,
>  			struct xfs_btree_block *, int);
> -extern void xfs_bmbt_get_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
> -extern xfs_filblks_t xfs_bmbt_get_blockcount(xfs_bmbt_rec_host_t *r);
> -extern xfs_fsblock_t xfs_bmbt_get_startblock(xfs_bmbt_rec_host_t *r);
> -extern xfs_fileoff_t xfs_bmbt_get_startoff(xfs_bmbt_rec_host_t *r);
>  
>  void xfs_bmbt_disk_set_all(struct xfs_bmbt_rec *r, struct xfs_bmbt_irec *s);
>  extern xfs_filblks_t xfs_bmbt_disk_get_blockcount(xfs_bmbt_rec_t *r);
>  extern xfs_fileoff_t xfs_bmbt_disk_get_startoff(xfs_bmbt_rec_t *r);
> -
> -extern void xfs_bmbt_set_all(xfs_bmbt_rec_host_t *r, xfs_bmbt_irec_t *s);
> +extern void xfs_bmbt_disk_get_all(xfs_bmbt_rec_t *r, xfs_bmbt_irec_t *s);
>  
>  extern void xfs_bmbt_to_bmdr(struct xfs_mount *, struct xfs_btree_block *, int,
>  			xfs_bmdr_block_t *, int);
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index 6470dfa768ee..28d5391d2272 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -1553,10 +1553,6 @@ typedef struct xfs_bmbt_rec {
>  typedef uint64_t	xfs_bmbt_rec_base_t;	/* use this for casts */
>  typedef xfs_bmbt_rec_t xfs_bmdr_rec_t;
>  
> -typedef struct xfs_bmbt_rec_host {
> -	uint64_t		l0, l1;
> -} xfs_bmbt_rec_host_t;
> -
>  /*
>   * Values and macros for delayed-allocation startblock fields.
>   */
> diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
> new file mode 100644
> index 000000000000..acb66c0677e7
> --- /dev/null
> +++ b/fs/xfs/libxfs/xfs_iext_tree.c
> @@ -0,0 +1,1043 @@
> +/*
> + * Copyright (c) 2017 Christoph Hellwig.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + */
> +
> +#include <linux/cache.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include "xfs.h"
> +#include "xfs_format.h"
> +#include "xfs_bit.h"
> +#include "xfs_log_format.h"
> +#include "xfs_inode.h"
> +#include "xfs_inode_fork.h"
> +#include "xfs_trans_resv.h"
> +#include "xfs_mount.h"
> +#include "xfs_trace.h"
> +
> +/*
> + * In-core extent record layout:
> + *
> + * +-------+----------------------------+
> + * | 00:51 | all 52 bits of startoff    |
> + * | 52:63 | low 12 bits of startblock  |
> + * +-------+----------------------------+
> + * | 00:20 | all 21 bits of length      |
> + * |    21 | unwritten extent bit       |
> + * | 22:63 | high 42 bits of startblock |
> + * +-------+----------------------------+
> + */
> +#define XFS_IEXT_STARTOFF_MASK		xfs_mask64lo(52)
> +#define XFS_IEXT_LENGTH_MASK		xfs_mask64lo(21)
> +#define XFS_IEXT_STARTBLOCK_MASK	xfs_mask64lo(54)
> +
> +struct xfs_iext_rec {
> +	uint64_t			lo;
> +	uint64_t			hi;
> +};
> +
> +/*
> + * Given that the length can't be a zero, only an empty hi value indicates an
> + * unused record.
> + */
> +static bool xfs_iext_rec_is_empty(struct xfs_iext_rec *rec)
> +{
> +	return rec->hi == 0;
> +}
> +
> +static inline void xfs_iext_rec_clear(struct xfs_iext_rec *rec)
> +{
> +	rec->lo = 0;
> +	rec->hi = 0;
> +}
> +
> +static void
> +xfs_iext_set(
> +	struct xfs_iext_rec	*rec,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	ASSERT((irec->br_startoff & ~XFS_IEXT_STARTOFF_MASK) == 0);
> +	ASSERT((irec->br_blockcount & ~XFS_IEXT_LENGTH_MASK) == 0);
> +	ASSERT((irec->br_startblock & ~XFS_IEXT_STARTBLOCK_MASK) == 0);
> +
> +	rec->lo = irec->br_startoff & XFS_IEXT_STARTOFF_MASK;
> +	rec->hi = irec->br_blockcount & XFS_IEXT_LENGTH_MASK;
> +
> +	rec->lo |= (irec->br_startblock << 52);
> +	rec->hi |= ((irec->br_startblock & ~xfs_mask64lo(12)) << (22 - 12));
> +
> +	if (irec->br_state == XFS_EXT_UNWRITTEN)
> +		rec->hi |= (1 << 21);
> +}
> +
> +static void
> +xfs_iext_get(
> +	struct xfs_bmbt_irec	*irec,
> +	struct xfs_iext_rec	*rec)
> +{
> +	irec->br_startoff = rec->lo & XFS_IEXT_STARTOFF_MASK;
> +	irec->br_blockcount = rec->hi & XFS_IEXT_LENGTH_MASK;
> +
> +	irec->br_startblock = rec->lo >> 52;
> +	irec->br_startblock |= (rec->hi & xfs_mask64hi(42)) >> (22 - 12);
> +
> +	if (rec->hi & (1 << 21))
> +		irec->br_state = XFS_EXT_UNWRITTEN;
> +	else
> +		irec->br_state = XFS_EXT_NORM;
> +}
> +
> +enum {
> +	NODE_SIZE	= L1_CACHE_BYTES * 4,
> +	KEYS_PER_NODE	= NODE_SIZE / (sizeof(uint64_t) + sizeof(void *)),
> +	RECS_PER_LEAF	= (NODE_SIZE - sizeof(uint64_t) - sizeof(uint64_t)) /
> +				sizeof(struct xfs_iext_rec),
> +};
> +
> +/*
> + * In-core extent btree block layout:
> + *
> + * There are two types of blocks in the btree: leaf and inner (non-leaf) blocks.
> + *
> + * The leaf blocks are made up by %KEYS_PER_NODE extent records, which each
> + * contain the startoffset, blockcount, startblock and unwritten extent flag.
> + * See above for the exact format, followed by pointers to the previous and next
> + * leaf blocks (if there are any).
> + *
> + * The inner (non-leaf) blocks first contain KEYS_PER_NODE lookup keys, followed
> + * by an equal number of pointers to the btree blocks at the next lower level.
> + *
> + * Note that we currently always allocate 64-bits worth for pointers in the
> + * inner nodes or the link pointers in the leaf nodes even on 32-bit

We do?  sizeof(void *) == 4 on 32-bit; on a i686 machine this evaluates to....

NODE_SIZE = 64 * 4,
KEYS_PER_NODE = 256 / (8 + 4) = 21

While on x64 this becomes:

KEYS_PER_NODE = 256 / (8 + 8) = 16

...right?

RECS_PER_LEAF = (256 - 8 - 8) / 16 = 15

Does RECS_PER_LEAF reserve two uint64_t for the prev and next pointers
in struct xfs_iext_leaf?  It'd be a little more clear if the definition
was:

RECS_PER_LEAF = (NODE_SIZE - (2 * sizeof(void *))) / sizeof(xfs_iext_rec)

Frankly I wonder why not just have:

(NODE_SIZE - sizeof(xfs_iext_leaf_tail)) / sizeof(iext_rec)

Though I suppose practically speaking none of this changes RECS_PER_LEAF.

> + * architectures, so that we can use consistent addressing using and array of
> + * 64-bit unsigned integers.  If someone still cares for 32-bit architectures
> + * this could be optimized a bit better for them.
> + *
> + *		+-------+-------+-------+-------+-------+----------+----------+
> + * Leaf:	| rec 1 | rec 2 | rec 3 | rec 4 | rec N | prev-ptr | next-ptr |
> + *		+-------+-------+-------+-------+-------+----------+----------+
> + *
> + *		+-------+-------+-------+-------+-------+-------+------+-------+
> + * Inner:	| key 1 | key 2 | key 3 | key N | ptr 1 | ptr 2 | ptr3 | ptr N |
> + *		+-------+-------+-------+-------+-------+-------+------+-------+
> + */
> +struct xfs_iext_node {
> +	uint64_t		keys[KEYS_PER_NODE];
> +#define XFS_IEXT_KEY_INVALID	(1ULL << 63)
> +	void			*ptrs[KEYS_PER_NODE];
> +};

Hm, ok, let's do the math here.  The data forks can have at most 2^32
extents, and the attr fork can have at most 2^16 extents.

The L1 cache sizes range from 4 bytes (h8300) to 256 bytes (s390), with
x64 at 64 bytes and arm64/ppc64 at 128.

This means that this is broken on h8300 because it's not possible to
create a leaf containing even one record.  Not that even a 32-byte leaf
is acceptable in terms of overhead.  Perhaps NODE_SIZE should have a
minimum?

For the data fork on x64, the largest our tree can get is:

2^32 max extent records
RECS_PER_LEAF = 15
KEYS_PER_NODE = 16

level 0 = 286,331,154 leaf blocks
level 1 = 17,895,698 nodes
level 2 = 1,118,482 nodes
level 3 = 69,906 nodes
level 4 = 4,370 nodes
level 5 = 274 nodes
level 6 = 18 nodes
level 7 = 2 nodes
level 8 = 1 node
== 305,419,904 blocks, or 78GB of RAM?  The leaves eat 68G, the nodes eat 10G.

Now let's try s390:

RECS_PER_LEAF = 63
KEYS_PER_NODE = 64

level 0 = 68,174,085 leaf blocks
level 1 = 1,065,221 nodes
level 2 = 16,645 nodes
level 3 = 261 nodes
level 4 = 5 nodes
level 5 = 1 node
== 69,256,218 blocks, or 71GB, or 3G tree overhead.

Hm, I suppose ~80G of RAM to map ~16T of space isn't bad, assuming
each extent maps a single 4k block.

I guess it's pretty horrible if each block is instead 512b.

However, prior to this code we'd just fall over dead. :)

> +
> +struct xfs_iext_leaf {
> +	struct xfs_iext_rec	recs[RECS_PER_LEAF];
> +	struct xfs_iext_leaf	*prev;
> +	struct xfs_iext_leaf	*next;
> +};
> +
> +inline xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp)

static inline?

> +{
> +	return ifp->if_bytes / sizeof(struct xfs_iext_rec);
> +}
> +
> +static inline int xfs_iext_max_recs(struct xfs_ifork *ifp)
> +{
> +	if (ifp->if_height == 1)
> +		return xfs_iext_count(ifp);
> +	return RECS_PER_LEAF;
> +}
> +
> +static inline struct xfs_iext_rec *cur_rec(struct xfs_iext_cursor *cur)
> +{
> +	return &cur->leaf->recs[cur->pos];
> +}
> +
> +static noinline bool xfs_iext_valid(struct xfs_ifork *ifp,
> +		struct xfs_iext_cursor *cur)
> +{
> +	if (cur->pos < 0)
> +		return false;
> +	if (cur->pos >= xfs_iext_max_recs(ifp))
> +		return false;
> +	if (!cur->leaf)
> +		return false;
> +	if (xfs_iext_rec_is_empty(cur_rec(cur)))
> +		return false;
> +	return true;
> +}
> +
> +static void *
> +xfs_iext_find_first_leaf(
> +	struct xfs_ifork	*ifp)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height;
> +
> +	if (!ifp->if_height)
> +		return NULL;
> +
> +	for (height = ifp->if_height; height > 1; height--) {
> +		node = node->ptrs[0];
> +		ASSERT(node);
> +	}
> +
> +	return node;
> +}
> +
> +static void *
> +xfs_iext_find_last_leaf(
> +	struct xfs_ifork	*ifp)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height, i;
> +
> +	if (!ifp->if_height)
> +		return NULL;
> +
> +	for (height = ifp->if_height; height > 1; height--) {
> +		for (i = 1; i < KEYS_PER_NODE; i++)
> +			if (!node->ptrs[i])
> +				break;
> +		node = node->ptrs[i - 1];
> +		ASSERT(node);
> +	}
> +
> +	return node;
> +}
> +
> +void
> +xfs_iext_first(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	cur->pos = 0;
> +	cur->leaf = xfs_iext_find_first_leaf(ifp);
> +}
> +
> +void
> +xfs_iext_last(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	int			i;
> +
> +	cur->leaf = xfs_iext_find_last_leaf(ifp);
> +	if (!cur->leaf) {
> +		cur->pos = 0;
> +		return;
> +	}
> +
> +	for (i = 1; i < xfs_iext_max_recs(ifp); i++) {
> +		if (xfs_iext_rec_is_empty(&cur->leaf->recs[i]))
> +			break;
> +	}
> +	cur->pos = i - 1;
> +}
> +
> +void
> +xfs_iext_next(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	if (!cur->leaf) {
> +		ASSERT(cur->pos <= 0 || cur->pos >= RECS_PER_LEAF);
> +		xfs_iext_first(ifp, cur);
> +		return;
> +	}
> +
> +	ASSERT(cur->pos >= 0);
> +	ASSERT(cur->pos < xfs_iext_max_recs(ifp));
> +
> +	cur->pos++;
> +	if (!xfs_iext_valid(ifp, cur) && ifp->if_height > 1 &&
> +	    cur->leaf && cur->leaf->next) {
> +		cur->leaf = cur->leaf->next;
> +		cur->pos = 0;
> +	}
> +}
> +
> +void
> +xfs_iext_prev(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	if (!cur->leaf) {
> +		ASSERT(cur->pos <= 0 || cur->pos >= RECS_PER_LEAF);
> +		xfs_iext_last(ifp, cur);
> +		return;
> +	}
> +
> +	ASSERT(cur->pos >= 0);
> +	ASSERT(cur->pos <= RECS_PER_LEAF);
> +
> +recurse:
> +	do {
> +		cur->pos--;
> +		if (xfs_iext_valid(ifp, cur))
> +			return;
> +	} while (cur->pos > 0);
> +
> +	if (ifp->if_height > 1 && cur->leaf->prev) {
> +		cur->leaf = cur->leaf->prev;
> +		cur->pos = RECS_PER_LEAF;
> +		goto recurse;
> +	}
> +}
> +
> +static inline int
> +xfs_iext_key_cmp(
> +	struct xfs_iext_node	*node,
> +	int			n,
> +	xfs_fileoff_t		offset)
> +{
> +	if (node->keys[n] > offset)
> +		return 1;
> +	if (node->keys[n] < offset)
> +		return -1;
> +	return 0;
> +}
> +
> +static inline int
> +xfs_iext_rec_cmp(
> +	struct xfs_iext_rec	*rec,
> +	xfs_fileoff_t		offset)
> +{
> +	uint64_t		rec_offset = rec->lo & XFS_IEXT_STARTOFF_MASK;
> +	u32			rec_len = rec->hi & XFS_IEXT_LENGTH_MASK;
> +
> +	if (rec_offset > offset)
> +		return 1;
> +	if (rec_offset + rec_len <= offset)
> +		return -1;
> +	return 0;
> +}
> +
> +static void *
> +xfs_iext_find_level(
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		offset,
> +	int			level)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height, i;
> +
> +	if (!ifp->if_height)
> +		return NULL;
> +
> +	for (height = ifp->if_height; height > level; height--) {
> +		for (i = 1; i < KEYS_PER_NODE; i++)
> +			if (xfs_iext_key_cmp(node, i, offset) > 0)
> +				break;
> +
> +		node = node->ptrs[i - 1];
> +		if (!node)
> +			break;
> +	}
> +
> +	return node;
> +}
> +
> +static int
> +xfs_iext_node_pos(
> +	struct xfs_iext_node	*node,
> +	xfs_fileoff_t		offset)
> +{
> +	int			i;
> +
> +	for (i = 1; i < KEYS_PER_NODE; i++) {
> +		if (xfs_iext_key_cmp(node, i, offset) > 0)
> +			break;
> +	}
> +
> +	return i - 1;
> +}
> +
> +static int
> +xfs_iext_node_insert_pos(
> +	struct xfs_iext_node	*node,
> +	xfs_fileoff_t		offset)
> +{
> +	int			i;
> +
> +	for (i = 0; i < KEYS_PER_NODE; i++) {
> +		if (xfs_iext_key_cmp(node, i, offset) > 0)
> +			return i;
> +	}
> +
> +	return KEYS_PER_NODE;
> +}
> +
> +static int
> +xfs_iext_node_nr_entries(
> +	struct xfs_iext_node	*node,
> +	int			start)
> +{
> +	int			i;
> +
> +	for (i = start; i < KEYS_PER_NODE; i++) {
> +		if (node->keys[i] == XFS_IEXT_KEY_INVALID)
> +			break;
> +	}
> +
> +	return i;
> +}
> +
> +static int
> +xfs_iext_leaf_nr_entries(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_leaf	*leaf,
> +	int			start)
> +{
> +	int			i;
> +
> +	for (i = start; i < xfs_iext_max_recs(ifp); i++) {
> +		if (xfs_iext_rec_is_empty(&leaf->recs[i]))
> +			break;
> +	}
> +
> +	return i;
> +}
> +
> +static inline uint64_t
> +xfs_iext_leaf_key(
> +	struct xfs_iext_leaf	*leaf,
> +	int			n)
> +{
> +	return leaf->recs[n].lo & XFS_IEXT_STARTOFF_MASK;
> +}
> +
> +static void
> +xfs_iext_grow(
> +	struct xfs_ifork	*ifp)
> +{
> +	struct xfs_iext_node	*node = kmem_zalloc(NODE_SIZE, KM_NOFS);
> +	int			i;
> +
> +	if (ifp->if_height == 1) {
> +		struct xfs_iext_leaf *prev = ifp->if_u1.if_root;
> +
> +		node->keys[0] = xfs_iext_leaf_key(prev, 0);
> +		node->ptrs[0] = prev;
> +	} else  {
> +		struct xfs_iext_node *prev = ifp->if_u1.if_root;
> +
> +		ASSERT(ifp->if_height > 1);
> +
> +		node->keys[0] = prev->keys[0];
> +		node->ptrs[0] = prev;
> +	}
> +
> +	for (i = 1; i < KEYS_PER_NODE; i++)
> +		node->keys[i] = XFS_IEXT_KEY_INVALID;
> +
> +	ifp->if_u1.if_root = node;
> +	ifp->if_height++;
> +}
> +
> +static void
> +xfs_iext_update_node(
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		old_offset,
> +	xfs_fileoff_t		new_offset,
> +	int			level,
> +	void			*ptr)
> +{
> +	struct xfs_iext_node	*node = ifp->if_u1.if_root;
> +	int			height, i;
> +
> +	for (height = ifp->if_height; height > level; height--) {
> +		for (i = 0; i < KEYS_PER_NODE; i++) {
> +			if (i > 0 && xfs_iext_key_cmp(node, i, old_offset) > 0)
> +				break;
> +			if (node->keys[i] == old_offset)
> +				node->keys[i] = new_offset;
> +		}
> +		node = node->ptrs[i - 1];
> +		ASSERT(node);
> +	}
> +
> +	ASSERT(node == ptr);
> +}
> +
> +static struct xfs_iext_node *
> +xfs_iext_split_node(
> +	struct xfs_iext_node	**nodep,
> +	int			*pos,
> +	int			*nr_entries)
> +{
> +	struct xfs_iext_node	*node = *nodep;
> +	struct xfs_iext_node	*new = kmem_zalloc(NODE_SIZE, KM_NOFS);
> +	const int		nr_move = KEYS_PER_NODE / 2;
> +	int			nr_keep = nr_move + (KEYS_PER_NODE & 1);
> +	int			i = 0;
> +
> +	/* for sequential append operations just spill over into the new node */
> +	if (*pos == KEYS_PER_NODE) {
> +		*nodep = new;
> +		*pos = 0;
> +		*nr_entries = 0;
> +		goto done;
> +	}
> +
> +
> +	for (i = 0; i < nr_move; i++) {
> +		new->keys[i] = node->keys[nr_keep + i];
> +		new->ptrs[i] = node->ptrs[nr_keep + i];
> +
> +		node->keys[nr_keep + i] = XFS_IEXT_KEY_INVALID;
> +		node->ptrs[nr_keep + i] = NULL;
> +	}
> +
> +	if (*pos >= nr_keep) {
> +		*nodep = new;
> +		*pos -= nr_keep;
> +		*nr_entries = nr_move;
> +	} else {
> +		*nr_entries = nr_keep;
> +	}
> +done:
> +	for (; i < KEYS_PER_NODE; i++)
> +		new->keys[i] = XFS_IEXT_KEY_INVALID;
> +	return new;
> +}
> +
> +static void
> +xfs_iext_insert_node(
> +	struct xfs_ifork	*ifp,
> +	uint64_t		offset,
> +	void			*ptr,
> +	int			level)
> +{
> +	struct xfs_iext_node	*node, *new;
> +	int			i, pos, nr_entries;
> +
> +again:
> +	if (ifp->if_height < level)
> +		xfs_iext_grow(ifp);
> +
> +	new = NULL;
> +	node = xfs_iext_find_level(ifp, offset, level);
> +	pos = xfs_iext_node_insert_pos(node, offset);
> +	nr_entries = xfs_iext_node_nr_entries(node, pos);
> +
> +	ASSERT(pos >= nr_entries || xfs_iext_key_cmp(node, pos, offset) != 0);
> +	ASSERT(nr_entries <= KEYS_PER_NODE);
> +
> +	if (nr_entries == KEYS_PER_NODE)
> +		new = xfs_iext_split_node(&node, &pos, &nr_entries);
> +
> +	if (node != new && pos == 0 && nr_entries > 0)
> +		xfs_iext_update_node(ifp, node->keys[0], offset, level, node);
> +
> +	for (i = nr_entries; i > pos; i--) {
> +		node->keys[i] = node->keys[i - 1];
> +		node->ptrs[i] = node->ptrs[i - 1];
> +	}

/me wonders if memmove is more appropriate here, but I'm guessing the
loop came out of lib/btree.c?

--D

> +	node->keys[pos] = offset;
> +	node->ptrs[pos] = ptr;
> +
> +	if (new) {
> +		offset = new->keys[0];
> +		ptr = new;
> +		level++;
> +		goto again;
> +	}
> +}
> +
> +static struct xfs_iext_leaf *
> +xfs_iext_split_leaf(
> +	struct xfs_iext_cursor	*cur,
> +	int			*nr_entries)
> +{
> +	struct xfs_iext_leaf	*leaf = cur->leaf;
> +	struct xfs_iext_leaf	*new = kmem_zalloc(NODE_SIZE, KM_NOFS);
> +	const int		nr_move = RECS_PER_LEAF / 2;
> +	int			nr_keep = nr_move + (RECS_PER_LEAF & 1);
> +	int			i;
> +
> +	/* for sequential append operations just spill over into the new node */
> +	if (cur->pos == KEYS_PER_NODE) {
> +		cur->leaf = new;
> +		cur->pos = 0;
> +		*nr_entries = 0;
> +		goto done;
> +	}
> +
> +	if (nr_keep & 1)
> +		nr_keep++;
> +
> +	for (i = 0; i < nr_move; i++) {
> +		new->recs[i] = leaf->recs[nr_keep + i];
> +		xfs_iext_rec_clear(&leaf->recs[nr_keep + i]);
> +	}
> +
> +	if (cur->pos >= nr_keep) {
> +		cur->leaf = new;
> +		cur->pos -= nr_keep;
> +		*nr_entries = nr_move;
> +	} else {
> +		*nr_entries = nr_keep;
> +	}
> +done:
> +	if (leaf->next)
> +		leaf->next->prev = new;
> +	new->next = leaf->next;
> +	new->prev = leaf;
> +	leaf->next = new;
> +	return new;
> +}
> +
> +static void
> +xfs_iext_alloc_root(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	ASSERT(ifp->if_bytes == 0);
> +
> +	ifp->if_u1.if_root = kmem_zalloc(sizeof(struct xfs_iext_rec), KM_NOFS);
> +	ifp->if_height = 1;
> +
> +	/* now that we have a node step into it */
> +	cur->leaf = ifp->if_u1.if_root;
> +	cur->pos = 0;
> +}
> +
> +static void
> +xfs_iext_realloc_root(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	size_t new_size = ifp->if_bytes + sizeof(struct xfs_iext_rec);
> +	void *new;
> +
> +	/* account for the prev/next pointers */
> +	if (new_size / sizeof(struct xfs_iext_rec) == RECS_PER_LEAF)
> +		new_size = NODE_SIZE;
> +
> +	new = kmem_realloc(ifp->if_u1.if_root, new_size, KM_NOFS);
> +	memset(new + ifp->if_bytes, 0, new_size - ifp->if_bytes);
> +	ifp->if_u1.if_root = new;
> +	cur->leaf = new;
> +}
> +
> +static void
> +__xfs_iext_insert(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*irec)
> +{
> +	xfs_fileoff_t		offset = irec->br_startoff;
> +	struct xfs_iext_leaf	*new = NULL;
> +	int			nr_entries, i;
> +
> +	if (ifp->if_height == 0)
> +		xfs_iext_alloc_root(ifp, cur);
> +	else if (ifp->if_height == 1)
> +		xfs_iext_realloc_root(ifp, cur);
> +
> +	nr_entries = xfs_iext_leaf_nr_entries(ifp, cur->leaf, cur->pos);
> +	ASSERT(nr_entries <= RECS_PER_LEAF);
> +	ASSERT(cur->pos >= nr_entries ||
> +	       xfs_iext_rec_cmp(cur_rec(cur), irec->br_startoff) != 0);
> +
> +	if (nr_entries == RECS_PER_LEAF)
> +		new = xfs_iext_split_leaf(cur, &nr_entries);
> +
> +	if (cur->leaf != new && cur->pos == 0 && nr_entries > 0) {
> +		xfs_iext_update_node(ifp, xfs_iext_leaf_key(cur->leaf, 0), offset, 1,
> +				cur->leaf);
> +	}
> +
> +	for (i = nr_entries; i > cur->pos; i--)
> +		cur->leaf->recs[i] = cur->leaf->recs[i - 1];
> +	xfs_iext_set(cur_rec(cur), irec);
> +	ifp->if_bytes += sizeof(struct xfs_iext_rec);
> +
> +	if (new)
> +		xfs_iext_insert_node(ifp, xfs_iext_leaf_key(new, 0), new, 2);
> +}
> +
> +void
> +xfs_iext_insert(
> +	struct xfs_inode	*ip,
> +	struct xfs_iext_cursor	*cur,
> +	xfs_extnum_t		nr_extents,
> +	struct xfs_bmbt_irec	*new,
> +	int			state)
> +{
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> +	int			i;
> +
> +	ASSERT(nr_extents > 0);
> +
> +	for (i = nr_extents - 1; i >= 0; i--) {
> +		__xfs_iext_insert(ifp, cur, new + i);
> +		trace_xfs_iext_insert(ip, cur, state, _RET_IP_);
> +	}
> +}
> +
> +static struct xfs_iext_node *
> +xfs_iext_rebalance_node(
> +	struct xfs_iext_node	*parent,
> +	int			*pos,
> +	struct xfs_iext_node	*node,
> +	int			nr_entries)
> +{
> +	if (nr_entries == 0)
> +		return node;
> +
> +	if (*pos > 0) {
> +		struct xfs_iext_node *prev = parent->ptrs[*pos - 1];
> +		int nr_prev = xfs_iext_node_nr_entries(prev, 0), i;
> +
> +		if (nr_prev + nr_entries <= KEYS_PER_NODE) {
> +			for (i = 0; i < nr_entries; i++) {
> +				prev->keys[nr_prev + i] = node->keys[i];
> +				prev->ptrs[nr_prev + i] = node->ptrs[i];
> +			}
> +			return node;
> +		}
> +	}
> +
> +	if (*pos + 1 < xfs_iext_node_nr_entries(parent, *pos)) {
> +		struct xfs_iext_node *next = parent->ptrs[*pos + 1];
> +		int nr_next = xfs_iext_node_nr_entries(next, 0), i;
> +
> +		if (nr_entries + nr_next <= KEYS_PER_NODE) {
> +			for (i = 0; i < nr_next; i++) {
> +				node->keys[nr_entries + i] = next->keys[i];
> +				node->ptrs[nr_entries + i] = next->ptrs[i];
> +			}
> +
> +			++*pos;
> +			return next;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +static void
> +xfs_iext_remove_node(
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		offset,
> +	void			*victim)
> +{
> +	struct xfs_iext_node	*node, *parent;
> +	int			level = 2, pos, nr_entries, i;
> +
> +	ASSERT(level <= ifp->if_height);
> +	node = xfs_iext_find_level(ifp, offset, level);
> +	pos = xfs_iext_node_pos(node, offset);
> +again:
> +	ASSERT(node->ptrs[pos]);
> +	ASSERT(node->ptrs[pos] == victim);
> +	kmem_free(victim);
> +
> +	nr_entries = xfs_iext_node_nr_entries(node, pos) - 1;
> +	offset = node->keys[0];
> +	for (i = pos; i < nr_entries; i++) {
> +		node->keys[i] = node->keys[i + 1];
> +		node->ptrs[i] = node->ptrs[i + 1];
> +	}
> +	node->keys[nr_entries] = XFS_IEXT_KEY_INVALID;
> +	node->ptrs[nr_entries] = NULL;
> +
> +	if (pos == 0 && nr_entries > 0) {
> +		xfs_iext_update_node(ifp, offset, node->keys[0], level,
> +				node);
> +		offset = node->keys[0];
> +	}
> +
> +	if (nr_entries >= KEYS_PER_NODE / 2)
> +		return;
> +
> +	if (level < ifp->if_height) {
> +		level++;
> +		parent = xfs_iext_find_level(ifp, offset, level);
> +		pos = xfs_iext_node_pos(parent, offset);
> +
> +		ASSERT(pos != KEYS_PER_NODE);
> +		ASSERT(parent->ptrs[pos] == node);
> +
> +		node = xfs_iext_rebalance_node(parent, &pos, node, nr_entries);
> +		if (node) {
> +			offset = node->keys[0];
> +			victim = node;
> +			node = parent;
> +			goto again;
> +		}
> +	} else if (nr_entries == 1) {
> +		ASSERT(node == ifp->if_u1.if_root);
> +		ifp->if_u1.if_root = node->ptrs[0];
> +		ifp->if_height--;
> +		kmem_free(node);
> +	}
> +}
> +
> +static void
> +xfs_iext_rebalance_leaf(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_iext_leaf	*leaf,
> +	xfs_fileoff_t		offset,
> +	int			fill)
> +{
> +	if (leaf->prev) {
> +		int nr_prev = xfs_iext_leaf_nr_entries(ifp, leaf->prev, 0), i;
> +
> +		if (nr_prev + fill <= RECS_PER_LEAF) {
> +			for (i = 0; i < fill; i++)
> +				leaf->prev->recs[nr_prev + i] = leaf->recs[i];
> +
> +			if (cur->leaf == leaf) {
> +				cur->leaf = leaf->prev;
> +				cur->pos += nr_prev;
> +			}
> +			goto remove_node;
> +		}
> +	}
> +
> +	if (leaf->next) {
> +		int nr_next = xfs_iext_leaf_nr_entries(ifp, leaf->next, 0), i;
> +
> +		if (fill + nr_next <= RECS_PER_LEAF) {
> +			for (i = 0; i < nr_next; i++)
> +				leaf->recs[fill + i] = leaf->next->recs[i];
> +
> +			if (cur->leaf == leaf->next) {
> +				cur->leaf = leaf;
> +				cur->pos += fill;
> +			}
> +
> +			offset = xfs_iext_leaf_key(leaf->next, 0);
> +			leaf = leaf->next;
> +			goto remove_node;
> +		}
> +	}
> +
> +	return;
> +remove_node:
> +	if (leaf->prev)
> +		leaf->prev->next = leaf->next;
> +	if (leaf->next)
> +		leaf->next->prev = leaf->prev;
> +	xfs_iext_remove_node(ifp, offset, leaf);
> +}
> +
> +static void
> +xfs_iext_free_last_leaf(
> +	struct xfs_ifork	*ifp)
> +{
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height--;
> +	kmem_free(ifp->if_u1.if_root);
> +}
> +
> +static void
> +__xfs_iext_remove(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur)
> +{
> +	struct xfs_iext_leaf	*leaf = cur->leaf;
> +	xfs_fileoff_t		offset = xfs_iext_leaf_key(leaf, 0);
> +	int			i, nr_entries;
> +
> +	ASSERT(ifp->if_height > 0);
> +	ASSERT(ifp->if_u1.if_root != NULL);
> +	ASSERT(xfs_iext_valid(ifp, cur));
> +
> +	nr_entries = xfs_iext_leaf_nr_entries(ifp, leaf, cur->pos) - 1;
> +	for (i = cur->pos; i < nr_entries; i++)
> +		leaf->recs[i] = leaf->recs[i + 1];
> +	xfs_iext_rec_clear(&leaf->recs[nr_entries]);
> +	ifp->if_bytes -= sizeof(struct xfs_iext_rec);
> +
> +	if (cur->pos == 0 && nr_entries > 0) {
> +		xfs_iext_update_node(ifp, offset, xfs_iext_leaf_key(leaf, 0), 1,
> +				leaf);
> +		offset = xfs_iext_leaf_key(leaf, 0);
> +	} else if (cur->pos == nr_entries) {
> +		if (ifp->if_height > 1 && leaf->next)
> +			cur->leaf = leaf->next;
> +		else
> +			cur->leaf = NULL;
> +		cur->pos = 0;
> +	}
> +
> +	if (nr_entries >= RECS_PER_LEAF / 2)
> +		return;
> +
> +	if (ifp->if_height > 1)
> +		xfs_iext_rebalance_leaf(ifp, cur, leaf, offset, nr_entries);
> +	else if (nr_entries == 0)
> +		xfs_iext_free_last_leaf(ifp);
> +}
> +
> +void
> +xfs_iext_remove(
> +	struct xfs_inode	*ip,
> +	struct xfs_iext_cursor	*cur,
> +	int			nr_extents,
> +	int			state)
> +{
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> +	int			i;
> +
> +	ASSERT(nr_extents > 0);
> +
> +	for (i = 0; i < nr_extents; i++) {
> +		trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
> +		__xfs_iext_remove(ifp, cur);
> +	}
> +}
> +
> +/*
> + * Lookup the extent covering bno.
> + *
> + * If there is an extent covering bno return the extent index, and store the
> + * expanded extent structure in *gotp, and the extent cursor in *cur.
> + * If there is no extent covering bno, but there is an extent after it (e.g.
> + * it lies in a hole) return that extent in *gotp and its cursor in *cur
> + * instead.
> + * If bno is beyond the last extent return false, and return an invalid
> + * cursor value.
> + */
> +bool
> +xfs_iext_lookup_extent(
> +	struct xfs_inode	*ip,
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		offset,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*gotp)
> +{
> +	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
> +
> +	cur->leaf = xfs_iext_find_level(ifp, offset, 1);
> +	if (!cur->leaf) {
> +		cur->pos = 0;
> +		return false;
> +	}
> +
> +	for (cur->pos = 0; cur->pos < xfs_iext_max_recs(ifp); cur->pos++) {
> +		struct xfs_iext_rec *rec = cur_rec(cur);
> +
> +		if (xfs_iext_rec_is_empty(rec))
> +			break;
> +		if (xfs_iext_rec_cmp(rec, offset) >= 0)
> +			goto found;
> +	}
> +
> +	/* Try looking in the next node for an entry > offset */
> +	if (ifp->if_height == 1 || !cur->leaf->next)
> +		return false;
> +	cur->leaf = cur->leaf->next;
> +	cur->pos = 0;
> +	if (!xfs_iext_valid(ifp, cur))
> +		return false;
> +found:
> +	xfs_iext_get(gotp, cur_rec(cur));
> +	return true;
> +}
> +
> +/*
> + * Returns the last extent before end, and if this extent doesn't cover
> + * end, update end to the end of the extent.
> + */
> +bool
> +xfs_iext_lookup_extent_before(
> +	struct xfs_inode	*ip,
> +	struct xfs_ifork	*ifp,
> +	xfs_fileoff_t		*end,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*gotp)
> +{
> +	/* could be optimized to not even look up the next on a match.. */
> +	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
> +	    gotp->br_startoff <= *end - 1)
> +		return true;
> +	if (!xfs_iext_prev_extent(ifp, cur, gotp))
> +		return false;
> +	*end = gotp->br_startoff + gotp->br_blockcount;
> +	return true;
> +}
> +
> +void
> +xfs_iext_update_extent(
> +	struct xfs_inode	*ip,
> +	int			state,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*new)
> +{
> +	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> +
> +	if (cur->pos == 0) {
> +		struct xfs_bmbt_irec	old;
> +
> +		xfs_iext_get(&old, cur_rec(cur));
> +		if (new->br_startoff != old.br_startoff) {
> +			xfs_iext_update_node(ifp, old.br_startoff,
> +					new->br_startoff, 1, cur->leaf);
> +		}
> +	}
> +
> +	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
> +	xfs_iext_set(cur_rec(cur), new);
> +	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
> +}
> +
> +/*
> + * Return true if there is an extent at cursor cur and return the expanded
> + * extent structure at cur in gotp in that case.  Else return false.
> + */
> +bool
> +xfs_iext_get_extent(
> +	struct xfs_ifork	*ifp,
> +	struct xfs_iext_cursor	*cur,
> +	struct xfs_bmbt_irec	*gotp)
> +{
> +	if (!xfs_iext_valid(ifp, cur))
> +		return false;
> +	xfs_iext_get(gotp, cur_rec(cur));
> +	return true;
> +}
> +
> +/*
> + * This is a recursive function, because of that we need to be extremely
> + * careful with stack usage.
> + */
> +static void
> +xfs_iext_destroy_node(
> +	struct xfs_iext_node	*node,
> +	int			level)
> +{
> +	int			i;
> +
> +	if (level > 1) {
> +		for (i = 0; i < KEYS_PER_NODE; i++) {
> +			if (node->keys[i] == XFS_IEXT_KEY_INVALID)
> +				break;
> +			xfs_iext_destroy_node(node->ptrs[i], level - 1);
> +		}
> +	}
> +
> +	kmem_free(node);
> +}
> +
> +void
> +xfs_iext_destroy(
> +	struct xfs_ifork	*ifp)
> +{
> +	xfs_iext_destroy_node(ifp->if_u1.if_root, ifp->if_height);
> +
> +	ifp->if_bytes = 0;
> +	ifp->if_height = 0;
> +	ifp->if_u1.if_root = NULL;
> +}
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 5ac341d2b093..2711ff6ab2b3 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -331,6 +331,7 @@ xfs_iformat_extents(
>  	int			size = nex * sizeof(xfs_bmbt_rec_t);
>  	struct xfs_iext_cursor	ext;
>  	struct xfs_bmbt_rec	*dp;
> +	struct xfs_bmbt_irec	new;
>  	int			i;
>  
>  	/*
> @@ -346,27 +347,22 @@ xfs_iformat_extents(
>  	}
>  
>  	ifp->if_real_bytes = 0;
> -	if (nex == 0)
> -		ifp->if_u1.if_extents = NULL;
> -	else
> -		xfs_iext_add(ifp, 0, nex);
> -
> -	ifp->if_bytes = size;
> +	ifp->if_bytes = 0;
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  	if (size) {
>  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
>  
>  		xfs_iext_first(ifp, &ext);
>  		for (i = 0; i < nex; i++, dp++) {
> -			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> -
>  			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
>  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
>  						 XFS_ERRLEVEL_LOW, mp);
>  				return -EFSCORRUPTED;
>  			}
>  
> -			ep->l0 = get_unaligned_be64(&dp->l0);
> -			ep->l1 = get_unaligned_be64(&dp->l1);
> +			xfs_bmbt_disk_get_all(dp, &new);
> +			xfs_iext_insert(ip, &ext, 1, &new, state);
>  			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
>  			xfs_iext_next(ifp, &ext);
>  		}
> @@ -435,6 +431,10 @@ xfs_iformat_btree(
>  	ifp->if_flags &= ~XFS_IFEXTENTS;
>  	ifp->if_flags |= XFS_IFBROOT;
>  
> +	ifp->if_real_bytes = 0;
> +	ifp->if_bytes = 0;
> +	ifp->if_u1.if_root = NULL;
> +	ifp->if_height = 0;
>  	return 0;
>  }
>  
> @@ -662,14 +662,12 @@ xfs_idestroy_fork(
>  			ifp->if_u1.if_data = NULL;
>  			ifp->if_real_bytes = 0;
>  		}
> -	} else if ((ifp->if_flags & XFS_IFEXTENTS) &&
> -		   ((ifp->if_flags & XFS_IFEXTIREC) ||
> -		    (ifp->if_u1.if_extents != NULL))) {
> -		ASSERT(ifp->if_real_bytes != 0);
> +	} else if ((ifp->if_flags & XFS_IFEXTENTS) && ifp->if_height) {
>  		xfs_iext_destroy(ifp);
>  	}
> -	ASSERT(ifp->if_u1.if_extents == NULL);
> +
>  	ASSERT(ifp->if_real_bytes == 0);
> +
>  	if (whichfork == XFS_ATTR_FORK) {
>  		kmem_zone_free(xfs_ifork_zone, ip->i_afp);
>  		ip->i_afp = NULL;
> @@ -679,13 +677,6 @@ xfs_idestroy_fork(
>  	}
>  }
>  
> -/* Count number of incore extents based on if_bytes */
> -xfs_extnum_t
> -xfs_iext_count(struct xfs_ifork *ifp)
> -{
> -	return ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
> -}
> -
>  /*
>   * Convert in-core extents to on-disk form
>   *
> @@ -780,7 +771,6 @@ xfs_iflush_fork(
>  		       !(iip->ili_fields & extflag[whichfork]));
>  		if ((iip->ili_fields & extflag[whichfork]) &&
>  		    (ifp->if_bytes > 0)) {
> -			ASSERT(xfs_iext_get_ext(ifp, 0));
>  			ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
>  			(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
>  				whichfork);
> @@ -812,33 +802,6 @@ xfs_iflush_fork(
>  	}
>  }
>  
> -/*
> - * Return a pointer to the extent record at file index idx.
> - */
> -xfs_bmbt_rec_host_t *
> -xfs_iext_get_ext(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx)		/* index of target extent */
> -{
> -	ASSERT(idx >= 0);
> -	ASSERT(idx < xfs_iext_count(ifp));
> -
> -	if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
> -		return ifp->if_u1.if_ext_irec->er_extbuf;
> -	} else if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_ext_irec_t	*erp;		/* irec pointer */
> -		int		erp_idx = 0;	/* irec index */
> -		xfs_extnum_t	page_idx = idx;	/* ext index in target list */
> -
> -		erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0);
> -		return &erp->er_extbuf[page_idx];
> -	} else if (ifp->if_bytes) {
> -		return &ifp->if_u1.if_extents[idx];
> -	} else {
> -		return NULL;
> -	}
> -}
> -
>  /* Convert bmap state flags to an inode fork. */
>  struct xfs_ifork *
>  xfs_iext_state_to_fork(
> @@ -852,894 +815,6 @@ xfs_iext_state_to_fork(
>  	return &ip->i_df;
>  }
>  
> -/*
> - * Insert new item(s) into the extent records for incore inode
> - * fork 'ifp'.  'count' new items are inserted at index 'idx'.
> - */
> -void
> -xfs_iext_insert(
> -	xfs_inode_t	*ip,		/* incore inode pointer */
> -	struct xfs_iext_cursor *cur,
> -	xfs_extnum_t	count,		/* number of inserted items */
> -	xfs_bmbt_irec_t	*new,		/* items to insert */
> -	int		state)		/* type of extent conversion */
> -{
> -	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
> -	xfs_extnum_t	i;		/* extent record index */
> -
> -	trace_xfs_iext_insert(ip, cur->idx, new, state, _RET_IP_);
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTENTS);
> -	xfs_iext_add(ifp, cur->idx, count);
> -	for (i = 0; i < count; i++, new++)
> -		xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx + i), new);
> -}
> -
> -/*
> - * This is called when the amount of space required for incore file
> - * extents needs to be increased. The ext_diff parameter stores the
> - * number of new extents being added and the idx parameter contains
> - * the extent index where the new extents will be added. If the new
> - * extents are being appended, then we just need to (re)allocate and
> - * initialize the space. Otherwise, if the new extents are being
> - * inserted into the middle of the existing entries, a bit more work
> - * is required to make room for the new extents to be inserted. The
> - * caller is responsible for filling in the new extent entries upon
> - * return.
> - */
> -void
> -xfs_iext_add(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin adding exts */
> -	int		ext_diff)	/* number of extents to add */
> -{
> -	int		byte_diff;	/* new bytes being added */
> -	int		new_size;	/* size of extents after adding */
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT((idx >= 0) && (idx <= nextents));
> -	byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
> -	new_size = ifp->if_bytes + byte_diff;
> -
> -	/*
> -	 * Use a linear (direct) extent list.
> -	 * If the extents are currently inside the inode,
> -	 * xfs_iext_realloc_direct will switch us from
> -	 * inline to direct extent allocation mode.
> -	 */
> -	if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
> -		xfs_iext_realloc_direct(ifp, new_size);
> -		if (idx < nextents) {
> -			memmove(&ifp->if_u1.if_extents[idx + ext_diff],
> -				&ifp->if_u1.if_extents[idx],
> -				(nextents - idx) * sizeof(xfs_bmbt_rec_t));
> -			memset(&ifp->if_u1.if_extents[idx], 0, byte_diff);
> -		}
> -	}
> -	/* Indirection array */
> -	else {
> -		xfs_ext_irec_t	*erp;
> -		int		erp_idx = 0;
> -		int		page_idx = idx;
> -
> -		ASSERT(nextents + ext_diff > XFS_LINEAR_EXTS);
> -		if (ifp->if_flags & XFS_IFEXTIREC) {
> -			erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 1);
> -		} else {
> -			xfs_iext_irec_init(ifp);
> -			ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -			erp = ifp->if_u1.if_ext_irec;
> -		}
> -		/* Extents fit in target extent page */
> -		if (erp && erp->er_extcount + ext_diff <= XFS_LINEAR_EXTS) {
> -			if (page_idx < erp->er_extcount) {
> -				memmove(&erp->er_extbuf[page_idx + ext_diff],
> -					&erp->er_extbuf[page_idx],
> -					(erp->er_extcount - page_idx) *
> -					sizeof(xfs_bmbt_rec_t));
> -				memset(&erp->er_extbuf[page_idx], 0, byte_diff);
> -			}
> -			erp->er_extcount += ext_diff;
> -			xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
> -		}
> -		/* Insert a new extent page */
> -		else if (erp) {
> -			xfs_iext_add_indirect_multi(ifp,
> -				erp_idx, page_idx, ext_diff);
> -		}
> -		/*
> -		 * If extent(s) are being appended to the last page in
> -		 * the indirection array and the new extent(s) don't fit
> -		 * in the page, then erp is NULL and erp_idx is set to
> -		 * the next index needed in the indirection array.
> -		 */
> -		else {
> -			uint	count = ext_diff;
> -
> -			while (count) {
> -				erp = xfs_iext_irec_new(ifp, erp_idx);
> -				erp->er_extcount = min(count, XFS_LINEAR_EXTS);
> -				count -= erp->er_extcount;
> -				if (count)
> -					erp_idx++;
> -			}
> -		}
> -	}
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * This is called when incore extents are being added to the indirection
> - * array and the new extents do not fit in the target extent list. The
> - * erp_idx parameter contains the irec index for the target extent list
> - * in the indirection array, and the idx parameter contains the extent
> - * index within the list. The number of extents being added is stored
> - * in the count parameter.
> - *
> - *    |-------|   |-------|
> - *    |       |   |       |    idx - number of extents before idx
> - *    |  idx  |   | count |
> - *    |       |   |       |    count - number of extents being inserted at idx
> - *    |-------|   |-------|
> - *    | count |   | nex2  |    nex2 - number of extents after idx + count
> - *    |-------|   |-------|
> - */
> -void
> -xfs_iext_add_indirect_multi(
> -	xfs_ifork_t	*ifp,			/* inode fork pointer */
> -	int		erp_idx,		/* target extent irec index */
> -	xfs_extnum_t	idx,			/* index within target list */
> -	int		count)			/* new extents being added */
> -{
> -	int		byte_diff;		/* new bytes being added */
> -	xfs_ext_irec_t	*erp;			/* pointer to irec entry */
> -	xfs_extnum_t	ext_diff;		/* number of extents to add */
> -	xfs_extnum_t	ext_cnt;		/* new extents still needed */
> -	xfs_extnum_t	nex2;			/* extents after idx + count */
> -	xfs_bmbt_rec_t	*nex2_ep = NULL;	/* temp list for nex2 extents */
> -	int		nlists;			/* number of irec's (lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -	nex2 = erp->er_extcount - idx;
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -
> -	/*
> -	 * Save second part of target extent list
> -	 * (all extents past */
> -	if (nex2) {
> -		byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
> -		nex2_ep = (xfs_bmbt_rec_t *) kmem_alloc(byte_diff, KM_NOFS);
> -		memmove(nex2_ep, &erp->er_extbuf[idx], byte_diff);
> -		erp->er_extcount -= nex2;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -nex2);
> -		memset(&erp->er_extbuf[idx], 0, byte_diff);
> -	}
> -
> -	/*
> -	 * Add the new extents to the end of the target
> -	 * list, then allocate new irec record(s) and
> -	 * extent buffer(s) as needed to store the rest
> -	 * of the new extents.
> -	 */
> -	ext_cnt = count;
> -	ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS - erp->er_extcount);
> -	if (ext_diff) {
> -		erp->er_extcount += ext_diff;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
> -		ext_cnt -= ext_diff;
> -	}
> -	while (ext_cnt) {
> -		erp_idx++;
> -		erp = xfs_iext_irec_new(ifp, erp_idx);
> -		ext_diff = MIN(ext_cnt, (int)XFS_LINEAR_EXTS);
> -		erp->er_extcount = ext_diff;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, ext_diff);
> -		ext_cnt -= ext_diff;
> -	}
> -
> -	/* Add nex2 extents back to indirection array */
> -	if (nex2) {
> -		xfs_extnum_t	ext_avail;
> -		int		i;
> -
> -		byte_diff = nex2 * sizeof(xfs_bmbt_rec_t);
> -		ext_avail = XFS_LINEAR_EXTS - erp->er_extcount;
> -		i = 0;
> -		/*
> -		 * If nex2 extents fit in the current page, append
> -		 * nex2_ep after the new extents.
> -		 */
> -		if (nex2 <= ext_avail) {
> -			i = erp->er_extcount;
> -		}
> -		/*
> -		 * Otherwise, check if space is available in the
> -		 * next page.
> -		 */
> -		else if ((erp_idx < nlists - 1) &&
> -			 (nex2 <= (ext_avail = XFS_LINEAR_EXTS -
> -			  ifp->if_u1.if_ext_irec[erp_idx+1].er_extcount))) {
> -			erp_idx++;
> -			erp++;
> -			/* Create a hole for nex2 extents */
> -			memmove(&erp->er_extbuf[nex2], erp->er_extbuf,
> -				erp->er_extcount * sizeof(xfs_bmbt_rec_t));
> -		}
> -		/*
> -		 * Final choice, create a new extent page for
> -		 * nex2 extents.
> -		 */
> -		else {
> -			erp_idx++;
> -			erp = xfs_iext_irec_new(ifp, erp_idx);
> -		}
> -		memmove(&erp->er_extbuf[i], nex2_ep, byte_diff);
> -		kmem_free(nex2_ep);
> -		erp->er_extcount += nex2;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2);
> -	}
> -}
> -
> -/*
> - * This is called when the amount of space required for incore file
> - * extents needs to be decreased. The ext_diff parameter stores the
> - * number of extents to be removed and the idx parameter contains
> - * the extent index where the extents will be removed from.
> - *
> - * If the amount of space needed has decreased below the linear
> - * limit, XFS_IEXT_BUFSZ, then switch to using the contiguous
> - * extent array.  Otherwise, use kmem_realloc() to adjust the
> - * size to what is needed.
> - */
> -void
> -xfs_iext_remove(
> -	xfs_inode_t	*ip,		/* incore inode pointer */
> -	struct xfs_iext_cursor *cur,
> -	int		ext_diff,	/* number of extents to remove */
> -	int		state)		/* type of extent conversion */
> -{
> -	xfs_ifork_t	*ifp = xfs_iext_state_to_fork(ip, state);
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		new_size;	/* size of extents after removal */
> -
> -	trace_xfs_iext_remove(ip, cur, state, _RET_IP_);
> -
> -	ASSERT(ext_diff > 0);
> -	nextents = xfs_iext_count(ifp);
> -	new_size = (nextents - ext_diff) * sizeof(xfs_bmbt_rec_t);
> -
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
> -	} else if (ifp->if_real_bytes) {
> -		xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
> -	}
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * This removes ext_diff extents from a linear (direct) extent list,
> - * beginning at extent index idx. If the extents are being removed
> - * from the end of the list (ie. truncate) then we just need to re-
> - * allocate the list to remove the extra space. Otherwise, if the
> - * extents are being removed from the middle of the existing extent
> - * entries, then we first need to move the extent records beginning
> - * at idx + ext_diff up in the list to overwrite the records being
> - * removed, then remove the extra space via kmem_realloc.
> - */
> -void
> -xfs_iext_remove_direct(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin removing exts */
> -	int		ext_diff)	/* number of extents to remove */
> -{
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		new_size;	/* size of extents after removal */
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
> -	new_size = ifp->if_bytes -
> -		(ext_diff * sizeof(xfs_bmbt_rec_t));
> -	nextents = xfs_iext_count(ifp);
> -
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -		return;
> -	}
> -	/* Move extents up in the list (if needed) */
> -	if (idx + ext_diff < nextents) {
> -		memmove(&ifp->if_u1.if_extents[idx],
> -			&ifp->if_u1.if_extents[idx + ext_diff],
> -			(nextents - (idx + ext_diff)) *
> -			 sizeof(xfs_bmbt_rec_t));
> -	}
> -	memset(&ifp->if_u1.if_extents[nextents - ext_diff],
> -		0, ext_diff * sizeof(xfs_bmbt_rec_t));
> -	/*
> -	 * Reallocate the direct extent list. If the extents
> -	 * will fit inside the inode then xfs_iext_realloc_direct
> -	 * will switch from direct to inline extent allocation
> -	 * mode for us.
> -	 */
> -	xfs_iext_realloc_direct(ifp, new_size);
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * This is called when incore extents are being removed from the
> - * indirection array and the extents being removed span multiple extent
> - * buffers. The idx parameter contains the file extent index where we
> - * want to begin removing extents, and the count parameter contains
> - * how many extents need to be removed.
> - *
> - *    |-------|   |-------|
> - *    | nex1  |   |       |    nex1 - number of extents before idx
> - *    |-------|   | count |
> - *    |       |   |       |    count - number of extents being removed at idx
> - *    | count |   |-------|
> - *    |       |   | nex2  |    nex2 - number of extents after idx + count
> - *    |-------|   |-------|
> - */
> -void
> -xfs_iext_remove_indirect(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	idx,		/* index to begin removing extents */
> -	int		count)		/* number of extents to remove */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	int		erp_idx = 0;	/* indirection array index */
> -	xfs_extnum_t	ext_cnt;	/* extents left to remove */
> -	xfs_extnum_t	ext_diff;	/* extents to remove in current list */
> -	xfs_extnum_t	nex1;		/* number of extents before idx */
> -	xfs_extnum_t	nex2;		/* extents after idx + count */
> -	int		page_idx = idx;	/* index in target extent list */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	erp = xfs_iext_idx_to_irec(ifp,  &page_idx, &erp_idx, 0);
> -	ASSERT(erp != NULL);
> -	nex1 = page_idx;
> -	ext_cnt = count;
> -	while (ext_cnt) {
> -		nex2 = MAX((erp->er_extcount - (nex1 + ext_cnt)), 0);
> -		ext_diff = MIN(ext_cnt, (erp->er_extcount - nex1));
> -		/*
> -		 * Check for deletion of entire list;
> -		 * xfs_iext_irec_remove() updates extent offsets.
> -		 */
> -		if (ext_diff == erp->er_extcount) {
> -			xfs_iext_irec_remove(ifp, erp_idx);
> -			ext_cnt -= ext_diff;
> -			nex1 = 0;
> -			if (ext_cnt) {
> -				ASSERT(erp_idx < ifp->if_real_bytes /
> -					XFS_IEXT_BUFSZ);
> -				erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -				nex1 = 0;
> -				continue;
> -			} else {
> -				break;
> -			}
> -		}
> -		/* Move extents up (if needed) */
> -		if (nex2) {
> -			memmove(&erp->er_extbuf[nex1],
> -				&erp->er_extbuf[nex1 + ext_diff],
> -				nex2 * sizeof(xfs_bmbt_rec_t));
> -		}
> -		/* Zero out rest of page */
> -		memset(&erp->er_extbuf[nex1 + nex2], 0, (XFS_IEXT_BUFSZ -
> -			((nex1 + nex2) * sizeof(xfs_bmbt_rec_t))));
> -		/* Update remaining counters */
> -		erp->er_extcount -= ext_diff;
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, -ext_diff);
> -		ext_cnt -= ext_diff;
> -		nex1 = 0;
> -		erp_idx++;
> -		erp++;
> -	}
> -	ifp->if_bytes -= count * sizeof(xfs_bmbt_rec_t);
> -	xfs_iext_irec_compact(ifp);
> -}
> -
> -/*
> - * Create, destroy, or resize a linear (direct) block of extents.
> - */
> -void
> -xfs_iext_realloc_direct(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		new_size)	/* new size of extents after adding */
> -{
> -	int		rnew_size;	/* real new size of extents */
> -
> -	rnew_size = new_size;
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC) ||
> -		((new_size >= 0) && (new_size <= XFS_IEXT_BUFSZ) &&
> -		 (new_size != ifp->if_real_bytes)));
> -
> -	/* Free extent records */
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else {
> -		if (!is_power_of_2(new_size)){
> -			rnew_size = roundup_pow_of_two(new_size);
> -		}
> -		if (rnew_size != ifp->if_real_bytes) {
> -			ifp->if_u1.if_extents =
> -				kmem_realloc(ifp->if_u1.if_extents,
> -						rnew_size, KM_NOFS);
> -		}
> -		if (rnew_size > ifp->if_real_bytes) {
> -			memset(&ifp->if_u1.if_extents[ifp->if_bytes /
> -				(uint)sizeof(xfs_bmbt_rec_t)], 0,
> -				rnew_size - ifp->if_real_bytes);
> -		}
> -	}
> -	ifp->if_real_bytes = rnew_size;
> -	ifp->if_bytes = new_size;
> -}
> -
> -/*
> - * Resize an extent indirection array to new_size bytes.
> - */
> -STATIC void
> -xfs_iext_realloc_indirect(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		new_size)	/* new indirection array size */
> -{
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	ASSERT(ifp->if_real_bytes);
> -	ASSERT((new_size >= 0) &&
> -	       (new_size != ((ifp->if_real_bytes / XFS_IEXT_BUFSZ) *
> -			     sizeof(xfs_ext_irec_t))));
> -	if (new_size == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else {
> -		ifp->if_u1.if_ext_irec =
> -			kmem_realloc(ifp->if_u1.if_ext_irec, new_size, KM_NOFS);
> -	}
> -}
> -
> -/*
> - * Switch from indirection array to linear (direct) extent allocations.
> - */
> -STATIC void
> -xfs_iext_indirect_to_direct(
> -	 xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_bmbt_rec_host_t *ep;	/* extent record pointer */
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		size;		/* size of file extents */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT(nextents <= XFS_LINEAR_EXTS);
> -	size = nextents * sizeof(xfs_bmbt_rec_t);
> -
> -	xfs_iext_irec_compact_pages(ifp);
> -	ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ);
> -
> -	ep = ifp->if_u1.if_ext_irec->er_extbuf;
> -	kmem_free(ifp->if_u1.if_ext_irec);
> -	ifp->if_flags &= ~XFS_IFEXTIREC;
> -	ifp->if_u1.if_extents = ep;
> -	ifp->if_bytes = size;
> -	if (nextents < XFS_LINEAR_EXTS) {
> -		xfs_iext_realloc_direct(ifp, size);
> -	}
> -}
> -
> -/*
> - * Remove all records from the indirection array.
> - */
> -STATIC void
> -xfs_iext_irec_remove_all(
> -	struct xfs_ifork *ifp)
> -{
> -	int		nlists;
> -	int		i;
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	for (i = 0; i < nlists; i++)
> -		kmem_free(ifp->if_u1.if_ext_irec[i].er_extbuf);
> -	kmem_free(ifp->if_u1.if_ext_irec);
> -	ifp->if_flags &= ~XFS_IFEXTIREC;
> -}
> -
> -/*
> - * Free incore file extents.
> - */
> -void
> -xfs_iext_destroy(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	if (ifp->if_flags & XFS_IFEXTIREC) {
> -		xfs_iext_irec_remove_all(ifp);
> -	} else if (ifp->if_real_bytes) {
> -		kmem_free(ifp->if_u1.if_extents);
> -	}
> -	ifp->if_u1.if_extents = NULL;
> -	ifp->if_real_bytes = 0;
> -	ifp->if_bytes = 0;
> -}
> -
> -/*
> - * Return a pointer to the extent record for file system block bno.
> - */
> -xfs_bmbt_rec_host_t *			/* pointer to found extent record */
> -xfs_iext_bno_to_ext(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_fileoff_t	bno,		/* block number to search for */
> -	xfs_extnum_t	*idxp)		/* index of target extent */
> -{
> -	xfs_bmbt_rec_host_t *base;	/* pointer to first extent */
> -	xfs_filblks_t	blockcount = 0;	/* number of blocks in extent */
> -	xfs_bmbt_rec_host_t *ep = NULL;	/* pointer to target extent */
> -	xfs_ext_irec_t	*erp = NULL;	/* indirection array pointer */
> -	int		high;		/* upper boundary in search */
> -	xfs_extnum_t	idx = 0;	/* index of target extent */
> -	int		low;		/* lower boundary in search */
> -	xfs_extnum_t	nextents;	/* number of file extents */
> -	xfs_fileoff_t	startoff = 0;	/* start offset of extent */
> -
> -	nextents = xfs_iext_count(ifp);
> -	if (nextents == 0) {
> -		*idxp = 0;
> -		return NULL;
> -	}
> -	low = 0;
> -	if (ifp->if_flags & XFS_IFEXTIREC) {
> -		/* Find target extent list */
> -		int	erp_idx = 0;
> -		erp = xfs_iext_bno_to_irec(ifp, bno, &erp_idx);
> -		base = erp->er_extbuf;
> -		high = erp->er_extcount - 1;
> -	} else {
> -		base = ifp->if_u1.if_extents;
> -		high = nextents - 1;
> -	}
> -	/* Binary search extent records */
> -	while (low <= high) {
> -		idx = (low + high) >> 1;
> -		ep = base + idx;
> -		startoff = xfs_bmbt_get_startoff(ep);
> -		blockcount = xfs_bmbt_get_blockcount(ep);
> -		if (bno < startoff) {
> -			high = idx - 1;
> -		} else if (bno >= startoff + blockcount) {
> -			low = idx + 1;
> -		} else {
> -			/* Convert back to file-based extent index */
> -			if (ifp->if_flags & XFS_IFEXTIREC) {
> -				idx += erp->er_extoff;
> -			}
> -			*idxp = idx;
> -			return ep;
> -		}
> -	}
> -	/* Convert back to file-based extent index */
> -	if (ifp->if_flags & XFS_IFEXTIREC) {
> -		idx += erp->er_extoff;
> -	}
> -	if (bno >= startoff + blockcount) {
> -		if (++idx == nextents) {
> -			ep = NULL;
> -		} else {
> -			ep = xfs_iext_get_ext(ifp, idx);
> -		}
> -	}
> -	*idxp = idx;
> -	return ep;
> -}
> -
> -/*
> - * Return a pointer to the indirection array entry containing the
> - * extent record for filesystem block bno. Store the index of the
> - * target irec in *erp_idxp.
> - */
> -xfs_ext_irec_t *			/* pointer to found extent record */
> -xfs_iext_bno_to_irec(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_fileoff_t	bno,		/* block number to search for */
> -	int		*erp_idxp)	/* irec index of target ext list */
> -{
> -	xfs_ext_irec_t	*erp = NULL;	/* indirection array pointer */
> -	xfs_ext_irec_t	*erp_next;	/* next indirection array entry */
> -	int		erp_idx;	/* indirection array index */
> -	int		nlists;		/* number of extent irec's (lists) */
> -	int		high;		/* binary search upper limit */
> -	int		low;		/* binary search lower limit */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	erp_idx = 0;
> -	low = 0;
> -	high = nlists - 1;
> -	while (low <= high) {
> -		erp_idx = (low + high) >> 1;
> -		erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -		erp_next = erp_idx < nlists - 1 ? erp + 1 : NULL;
> -		if (bno < xfs_bmbt_get_startoff(erp->er_extbuf)) {
> -			high = erp_idx - 1;
> -		} else if (erp_next && bno >=
> -			   xfs_bmbt_get_startoff(erp_next->er_extbuf)) {
> -			low = erp_idx + 1;
> -		} else {
> -			break;
> -		}
> -	}
> -	*erp_idxp = erp_idx;
> -	return erp;
> -}
> -
> -/*
> - * Return a pointer to the indirection array entry containing the
> - * extent record at file extent index *idxp. Store the index of the
> - * target irec in *erp_idxp and store the page index of the target
> - * extent record in *idxp.
> - */
> -xfs_ext_irec_t *
> -xfs_iext_idx_to_irec(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	xfs_extnum_t	*idxp,		/* extent index (file -> page) */
> -	int		*erp_idxp,	/* pointer to target irec */
> -	int		realloc)	/* new bytes were just added */
> -{
> -	xfs_ext_irec_t	*prev;		/* pointer to previous irec */
> -	xfs_ext_irec_t	*erp = NULL;	/* pointer to current irec */
> -	int		erp_idx;	/* indirection array index */
> -	int		nlists;		/* number of irec's (ex lists) */
> -	int		high;		/* binary search upper limit */
> -	int		low;		/* binary search lower limit */
> -	xfs_extnum_t	page_idx = *idxp; /* extent index in target list */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	ASSERT(page_idx >= 0);
> -	ASSERT(page_idx <= xfs_iext_count(ifp));
> -	ASSERT(page_idx < xfs_iext_count(ifp) || realloc);
> -
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	erp_idx = 0;
> -	low = 0;
> -	high = nlists - 1;
> -
> -	/* Binary search extent irec's */
> -	while (low <= high) {
> -		erp_idx = (low + high) >> 1;
> -		erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -		prev = erp_idx > 0 ? erp - 1 : NULL;
> -		if (page_idx < erp->er_extoff || (page_idx == erp->er_extoff &&
> -		     realloc && prev && prev->er_extcount < XFS_LINEAR_EXTS)) {
> -			high = erp_idx - 1;
> -		} else if (page_idx > erp->er_extoff + erp->er_extcount ||
> -			   (page_idx == erp->er_extoff + erp->er_extcount &&
> -			    !realloc)) {
> -			low = erp_idx + 1;
> -		} else if (page_idx == erp->er_extoff + erp->er_extcount &&
> -			   erp->er_extcount == XFS_LINEAR_EXTS) {
> -			ASSERT(realloc);
> -			page_idx = 0;
> -			erp_idx++;
> -			erp = erp_idx < nlists ? erp + 1 : NULL;
> -			break;
> -		} else {
> -			page_idx -= erp->er_extoff;
> -			break;
> -		}
> -	}
> -	*idxp = page_idx;
> -	*erp_idxp = erp_idx;
> -	return erp;
> -}
> -
> -/*
> - * Allocate and initialize an indirection array once the space needed
> - * for incore extents increases above XFS_IEXT_BUFSZ.
> - */
> -void
> -xfs_iext_irec_init(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -
> -	ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
> -	nextents = xfs_iext_count(ifp);
> -	ASSERT(nextents <= XFS_LINEAR_EXTS);
> -
> -	erp = kmem_alloc(sizeof(xfs_ext_irec_t), KM_NOFS);
> -
> -	if (nextents == 0) {
> -		ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
> -	} else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
> -		xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
> -	}
> -	erp->er_extbuf = ifp->if_u1.if_extents;
> -	erp->er_extcount = nextents;
> -	erp->er_extoff = 0;
> -
> -	ifp->if_flags |= XFS_IFEXTIREC;
> -	ifp->if_real_bytes = XFS_IEXT_BUFSZ;
> -	ifp->if_bytes = nextents * sizeof(xfs_bmbt_rec_t);
> -	ifp->if_u1.if_ext_irec = erp;
> -
> -	return;
> -}
> -
> -/*
> - * Allocate and initialize a new entry in the indirection array.
> - */
> -xfs_ext_irec_t *
> -xfs_iext_irec_new(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		erp_idx)	/* index for new irec */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	int		i;		/* loop counter */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -
> -	/* Resize indirection array */
> -	xfs_iext_realloc_indirect(ifp, ++nlists *
> -				  sizeof(xfs_ext_irec_t));
> -	/*
> -	 * Move records down in the array so the
> -	 * new page can use erp_idx.
> -	 */
> -	erp = ifp->if_u1.if_ext_irec;
> -	for (i = nlists - 1; i > erp_idx; i--) {
> -		memmove(&erp[i], &erp[i-1], sizeof(xfs_ext_irec_t));
> -	}
> -	ASSERT(i == erp_idx);
> -
> -	/* Initialize new extent record */
> -	erp = ifp->if_u1.if_ext_irec;
> -	erp[erp_idx].er_extbuf = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
> -	ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
> -	memset(erp[erp_idx].er_extbuf, 0, XFS_IEXT_BUFSZ);
> -	erp[erp_idx].er_extcount = 0;
> -	erp[erp_idx].er_extoff = erp_idx > 0 ?
> -		erp[erp_idx-1].er_extoff + erp[erp_idx-1].er_extcount : 0;
> -	return (&erp[erp_idx]);
> -}
> -
> -/*
> - * Remove a record from the indirection array.
> - */
> -void
> -xfs_iext_irec_remove(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		erp_idx)	/* irec index to remove */
> -{
> -	xfs_ext_irec_t	*erp;		/* indirection array pointer */
> -	int		i;		/* loop counter */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -	if (erp->er_extbuf) {
> -		xfs_iext_irec_update_extoffs(ifp, erp_idx + 1,
> -			-erp->er_extcount);
> -		kmem_free(erp->er_extbuf);
> -	}
> -	/* Compact extent records */
> -	erp = ifp->if_u1.if_ext_irec;
> -	for (i = erp_idx; i < nlists - 1; i++) {
> -		memmove(&erp[i], &erp[i+1], sizeof(xfs_ext_irec_t));
> -	}
> -	/*
> -	 * Manually free the last extent record from the indirection
> -	 * array.  A call to xfs_iext_realloc_indirect() with a size
> -	 * of zero would result in a call to xfs_iext_destroy() which
> -	 * would in turn call this function again, creating a nasty
> -	 * infinite loop.
> -	 */
> -	if (--nlists) {
> -		xfs_iext_realloc_indirect(ifp,
> -			nlists * sizeof(xfs_ext_irec_t));
> -	} else {
> -		kmem_free(ifp->if_u1.if_ext_irec);
> -	}
> -	ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
> -}
> -
> -/*
> - * This is called to clean up large amounts of unused memory allocated
> - * by the indirection array.  Before compacting anything though, verify
> - * that the indirection array is still needed and switch back to the
> - * linear extent list (or even the inline buffer) if possible.  The
> - * compaction policy is as follows:
> - *
> - *    Full Compaction: Extents fit into a single page (or inline buffer)
> - * Partial Compaction: Extents occupy less than 50% of allocated space
> - *      No Compaction: Extents occupy at least 50% of allocated space
> - */
> -void
> -xfs_iext_irec_compact(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_extnum_t	nextents;	/* number of extents in file */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	nextents = xfs_iext_count(ifp);
> -
> -	if (nextents == 0) {
> -		xfs_iext_destroy(ifp);
> -	} else if (nextents <= XFS_LINEAR_EXTS) {
> -		xfs_iext_indirect_to_direct(ifp);
> -	} else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
> -		xfs_iext_irec_compact_pages(ifp);
> -	}
> -}
> -
> -/*
> - * Combine extents from neighboring extent pages.
> - */
> -void
> -xfs_iext_irec_compact_pages(
> -	xfs_ifork_t	*ifp)		/* inode fork pointer */
> -{
> -	xfs_ext_irec_t	*erp, *erp_next;/* pointers to irec entries */
> -	int		erp_idx = 0;	/* indirection array index */
> -	int		nlists;		/* number of irec's (ex lists) */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	while (erp_idx < nlists - 1) {
> -		erp = &ifp->if_u1.if_ext_irec[erp_idx];
> -		erp_next = erp + 1;
> -		if (erp_next->er_extcount <=
> -		    (XFS_LINEAR_EXTS - erp->er_extcount)) {
> -			memcpy(&erp->er_extbuf[erp->er_extcount],
> -				erp_next->er_extbuf, erp_next->er_extcount *
> -				sizeof(xfs_bmbt_rec_t));
> -			erp->er_extcount += erp_next->er_extcount;
> -			/*
> -			 * Free page before removing extent record
> -			 * so er_extoffs don't get modified in
> -			 * xfs_iext_irec_remove.
> -			 */
> -			kmem_free(erp_next->er_extbuf);
> -			erp_next->er_extbuf = NULL;
> -			xfs_iext_irec_remove(ifp, erp_idx + 1);
> -			nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -		} else {
> -			erp_idx++;
> -		}
> -	}
> -}
> -
> -/*
> - * This is called to update the er_extoff field in the indirection
> - * array when extents have been added or removed from one of the
> - * extent lists. erp_idx contains the irec index to begin updating
> - * at and ext_diff contains the number of extents that were added
> - * or removed.
> - */
> -void
> -xfs_iext_irec_update_extoffs(
> -	xfs_ifork_t	*ifp,		/* inode fork pointer */
> -	int		erp_idx,	/* irec index to update */
> -	int		ext_diff)	/* number of new extents */
> -{
> -	int		i;		/* loop counter */
> -	int		nlists;		/* number of irec's (ex lists */
> -
> -	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
> -	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
> -	for (i = erp_idx; i < nlists; i++) {
> -		ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
> -	}
> -}
> -
>  /*
>   * Initialize an inode's copy-on-write fork.
>   */
> @@ -1756,87 +831,3 @@ xfs_ifork_init_cow(
>  	ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
>  	ip->i_cnextents = 0;
>  }
> -
> -/*
> - * Lookup the extent covering bno.
> - *
> - * If there is an extent covering bno return the extent index, and store the
> - * expanded extent structure in *gotp, and the extent cursor in *cur.
> - * If there is no extent covering bno, but there is an extent after it (e.g.
> - * it lies in a hole) return that extent in *gotp and its cursor in *cur
> - * instead.
> - * If bno is beyond the last extent return false, and return an invalid
> - * cursor value.
> - */
> -bool
> -xfs_iext_lookup_extent(
> -	struct xfs_inode	*ip,
> -	struct xfs_ifork	*ifp,
> -	xfs_fileoff_t		bno,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	struct xfs_bmbt_rec_host *ep;
> -
> -	XFS_STATS_INC(ip->i_mount, xs_look_exlist);
> -
> -	ep = xfs_iext_bno_to_ext(ifp, bno, &cur->idx);
> -	if (!ep)
> -		return false;
> -	xfs_bmbt_get_all(ep, gotp);
> -	return true;
> -}
> -
> -/*
> - * Returns the last extent before end, and if this extent doesn't cover
> - * end, update end to the end of the extent.
> - */
> -bool
> -xfs_iext_lookup_extent_before(
> -	struct xfs_inode	*ip,
> -	struct xfs_ifork	*ifp,
> -	xfs_fileoff_t		*end,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
> -	    gotp->br_startoff <= *end - 1)
> -		return true;
> -	if (!xfs_iext_prev_extent(ifp, cur, gotp))
> -		return false;
> -	*end = gotp->br_startoff + gotp->br_blockcount;
> -	return true;
> -}
> -
> -/*
> - * Return true if there is an extent at cursor cur and return the expanded
> - * extent structure at cur in gotp in that case.  Else return false.
> - */
> -bool
> -xfs_iext_get_extent(
> -	struct xfs_ifork	*ifp,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	if (cur->idx < 0 || cur->idx >= xfs_iext_count(ifp))
> -		return false;
> -	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
> -	return true;
> -}
> -
> -void
> -xfs_iext_update_extent(
> -	struct xfs_inode	*ip,
> -	int			state,
> -	struct xfs_iext_cursor	*cur,
> -	struct xfs_bmbt_irec	*gotp)
> -{
> -	struct xfs_ifork	*ifp = xfs_iext_state_to_fork(ip, state);
> -
> -	ASSERT(cur->idx >= 0);
> -	ASSERT(cur->idx < xfs_iext_count(ifp));
> -
> -	trace_xfs_bmap_pre_update(ip, cur, state, _RET_IP_);
> -	xfs_bmbt_set_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
> -	trace_xfs_bmap_post_update(ip, cur, state, _RET_IP_);
> -}
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index 508f13784334..015872caaab4 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
> @@ -21,45 +21,18 @@
>  struct xfs_inode_log_item;
>  struct xfs_dinode;
>  
> -/*
> - * The following xfs_ext_irec_t struct introduces a second (top) level
> - * to the in-core extent allocation scheme. These structs are allocated
> - * in a contiguous block, creating an indirection array where each entry
> - * (irec) contains a pointer to a buffer of in-core extent records which
> - * it manages. Each extent buffer is 4k in size, since 4k is the system
> - * page size on Linux i386 and systems with larger page sizes don't seem
> - * to gain much, if anything, by using their native page size as the
> - * extent buffer size. Also, using 4k extent buffers everywhere provides
> - * a consistent interface for CXFS across different platforms.
> - *
> - * There is currently no limit on the number of irec's (extent lists)
> - * allowed, so heavily fragmented files may require an indirection array
> - * which spans multiple system pages of memory. The number of extents
> - * which would require this amount of contiguous memory is very large
> - * and should not cause problems in the foreseeable future. However,
> - * if the memory needed for the contiguous array ever becomes a problem,
> - * it is possible that a third level of indirection may be required.
> - */
> -typedef struct xfs_ext_irec {
> -	xfs_bmbt_rec_host_t *er_extbuf;	/* block of extent records */
> -	xfs_extnum_t	er_extoff;	/* extent offset in file */
> -	xfs_extnum_t	er_extcount;	/* number of extents in page/block */
> -} xfs_ext_irec_t;
> -
>  /*
>   * File incore extent information, present for each of data & attr forks.
>   */
> -#define	XFS_IEXT_BUFSZ		4096
> -#define	XFS_LINEAR_EXTS		(XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t))
>  typedef struct xfs_ifork {
>  	int			if_bytes;	/* bytes in if_u1 */
>  	int			if_real_bytes;	/* bytes allocated in if_u1 */
>  	struct xfs_btree_block	*if_broot;	/* file's incore btree root */
>  	short			if_broot_bytes;	/* bytes allocated for root */
>  	unsigned char		if_flags;	/* per-fork flags */
> +	int			if_height;	/* height of the extent tree */
>  	union {
> -		xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */
> -		xfs_ext_irec_t	*if_ext_irec;	/* irec map file exts */
> +		void		*if_root;	/* extent tree root */
>  		char		*if_data;	/* inline file data */
>  	} if_u1;
>  } xfs_ifork_t;
> @@ -70,7 +43,6 @@ typedef struct xfs_ifork {
>  #define	XFS_IFINLINE	0x01	/* Inline data is read in */
>  #define	XFS_IFEXTENTS	0x02	/* All extent pointers are read in */
>  #define	XFS_IFBROOT	0x04	/* i_broot points to the bmap b-tree root */
> -#define	XFS_IFEXTIREC	0x08	/* Indirection array of extent blocks */
>  
>  /*
>   * Fork handling.
> @@ -140,35 +112,12 @@ int		xfs_iextents_copy(struct xfs_inode *, struct xfs_bmbt_rec *,
>  				  int);
>  void		xfs_init_local_fork(struct xfs_inode *, int, const void *, int);
>  
> -struct xfs_bmbt_rec_host *
> -		xfs_iext_get_ext(struct xfs_ifork *, xfs_extnum_t);
> -xfs_extnum_t	xfs_iext_count(struct xfs_ifork *);
> +xfs_extnum_t	xfs_iext_count(struct xfs_ifork *ifp);
>  void		xfs_iext_insert(struct xfs_inode *, struct xfs_iext_cursor *cur,
>  			xfs_extnum_t, struct xfs_bmbt_irec *, int);
> -void		xfs_iext_add(struct xfs_ifork *, xfs_extnum_t, int);
> -void		xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
> -					    xfs_extnum_t, int);
>  void		xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
>  			int, int);
> -void		xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
> -void		xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
> -void		xfs_iext_realloc_direct(struct xfs_ifork *, int);
>  void		xfs_iext_destroy(struct xfs_ifork *);
> -struct xfs_bmbt_rec_host *
> -		xfs_iext_bno_to_ext(struct xfs_ifork *, xfs_fileoff_t, int *);
> -struct xfs_ext_irec *
> -		xfs_iext_bno_to_irec(struct xfs_ifork *, xfs_fileoff_t, int *);
> -struct xfs_ext_irec *
> -		xfs_iext_idx_to_irec(struct xfs_ifork *, xfs_extnum_t *, int *,
> -				     int);
> -void		xfs_iext_irec_init(struct xfs_ifork *);
> -struct xfs_ext_irec *
> -		xfs_iext_irec_new(struct xfs_ifork *, int);
> -void		xfs_iext_irec_remove(struct xfs_ifork *, int);
> -void		xfs_iext_irec_compact(struct xfs_ifork *);
> -void		xfs_iext_irec_compact_pages(struct xfs_ifork *);
> -void		xfs_iext_irec_compact_full(struct xfs_ifork *);
> -void		xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
>  
>  bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
>  			struct xfs_ifork *ifp, xfs_fileoff_t bno,
> @@ -185,29 +134,10 @@ void		xfs_iext_update_extent(struct xfs_inode *ip, int state,
>  			struct xfs_iext_cursor *cur,
>  			struct xfs_bmbt_irec *gotp);
>  
> -static inline void xfs_iext_first(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx = 0;
> -}
> -
> -static inline void xfs_iext_last(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx = xfs_iext_count(ifp) - 1;
> -}
> -
> -static inline void xfs_iext_next(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx++;
> -}
> -
> -static inline void xfs_iext_prev(struct xfs_ifork *ifp,
> -		struct xfs_iext_cursor *cur)
> -{
> -	cur->idx--;
> -}
> +void		xfs_iext_first(struct xfs_ifork *, struct xfs_iext_cursor *);
> +void		xfs_iext_last(struct xfs_ifork *, struct xfs_iext_cursor *);
> +void		xfs_iext_next(struct xfs_ifork *, struct xfs_iext_cursor *);
> +void		xfs_iext_prev(struct xfs_ifork *, struct xfs_iext_cursor *);
>  
>  static inline bool xfs_iext_next_extent(struct xfs_ifork *ifp,
>  		struct xfs_iext_cursor *cur, struct xfs_bmbt_irec *gotp)
> diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
> index 5da6382bdaf1..983878019097 100644
> --- a/fs/xfs/libxfs/xfs_types.h
> +++ b/fs/xfs/libxfs/xfs_types.h
> @@ -143,7 +143,8 @@ typedef uint32_t	xfs_dqid_t;
>  #define	XFS_WORDMASK	((1 << XFS_WORDLOG) - 1)
>  
>  struct xfs_iext_cursor {
> -	xfs_extnum_t		idx;
> +	struct xfs_iext_leaf	*leaf;
> +	int			pos;
>  };
>  
>  #endif	/* __XFS_TYPES_H__ */
> diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
> index 778b709dbd0c..72d753d898d3 100644
> --- a/fs/xfs/scrub/bmap.c
> +++ b/fs/xfs/scrub/bmap.c
> @@ -168,7 +168,6 @@ xfs_scrub_bmapbt_rec(
>  	struct xfs_scrub_btree		*bs,
>  	union xfs_btree_rec		*rec)
>  {
> -	struct xfs_bmbt_rec_host	ihost;
>  	struct xfs_bmbt_irec		irec;
>  	struct xfs_scrub_bmap_info	*info = bs->private;
>  	struct xfs_inode		*ip = bs->cur->bc_private.b.ip;
> @@ -193,9 +192,7 @@ xfs_scrub_bmapbt_rec(
>  	}
>  
>  	/* Set up the in-core record and scrub it. */
> -	ihost.l0 = be64_to_cpu(rec->bmbt.l0);
> -	ihost.l1 = be64_to_cpu(rec->bmbt.l1);
> -	xfs_bmbt_get_all(&ihost, &irec);
> +	xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
>  	return xfs_scrub_bmap_extent(ip, bs->cur, info, &irec);
>  }
>  
> diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
> index a929ca72fa8e..5b5128ed0f18 100644
> --- a/fs/xfs/xfs_inode.c
> +++ b/fs/xfs/xfs_inode.c
> @@ -933,7 +933,7 @@ xfs_ialloc(
>  		ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
>  		ip->i_df.if_flags = XFS_IFEXTENTS;
>  		ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
> -		ip->i_df.if_u1.if_extents = NULL;
> +		ip->i_df.if_u1.if_root = NULL;
>  		break;
>  	default:
>  		ASSERT(0);
> diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
> index eb6f4f7c9520..6ee5c3bf19ad 100644
> --- a/fs/xfs/xfs_inode_item.c
> +++ b/fs/xfs/xfs_inode_item.c
> @@ -162,7 +162,6 @@ xfs_inode_item_format_data_fork(
>  		    ip->i_df.if_bytes > 0) {
>  			struct xfs_bmbt_rec *p;
>  
> -			ASSERT(ip->i_df.if_u1.if_extents != NULL);
>  			ASSERT(xfs_iext_count(&ip->i_df) > 0);
>  
>  			p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IEXT);
> @@ -252,7 +251,6 @@ xfs_inode_item_format_attr_fork(
>  
>  			ASSERT(xfs_iext_count(ip->i_afp) ==
>  				ip->i_d.di_anextents);
> -			ASSERT(ip->i_afp->if_u1.if_extents != NULL);
>  
>  			p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_EXT);
>  			data_bytes = xfs_iextents_copy(ip, p, XFS_ATTR_FORK);
> diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
> index 667bfce802cd..515ba042d75c 100644
> --- a/fs/xfs/xfs_trace.h
> +++ b/fs/xfs/xfs_trace.h
> @@ -218,45 +218,6 @@ TRACE_EVENT(xfs_attr_list_node_descend,
>  		   __entry->bt_before)
>  );
>  
> -TRACE_EVENT(xfs_iext_insert,
> -	TP_PROTO(struct xfs_inode *ip, xfs_extnum_t idx,
> -		 struct xfs_bmbt_irec *r, int state, unsigned long caller_ip),
> -	TP_ARGS(ip, idx, r, state, caller_ip),
> -	TP_STRUCT__entry(
> -		__field(dev_t, dev)
> -		__field(xfs_ino_t, ino)
> -		__field(xfs_extnum_t, idx)
> -		__field(xfs_fileoff_t, startoff)
> -		__field(xfs_fsblock_t, startblock)
> -		__field(xfs_filblks_t, blockcount)
> -		__field(xfs_exntst_t, state)
> -		__field(int, bmap_state)
> -		__field(unsigned long, caller_ip)
> -	),
> -	TP_fast_assign(
> -		__entry->dev = VFS_I(ip)->i_sb->s_dev;
> -		__entry->ino = ip->i_ino;
> -		__entry->idx = idx;
> -		__entry->startoff = r->br_startoff;
> -		__entry->startblock = r->br_startblock;
> -		__entry->blockcount = r->br_blockcount;
> -		__entry->state = r->br_state;
> -		__entry->bmap_state = state;
> -		__entry->caller_ip = caller_ip;
> -	),
> -	TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
> -		  "offset %lld block %lld count %lld flag %d caller %ps",
> -		  MAJOR(__entry->dev), MINOR(__entry->dev),
> -		  __entry->ino,
> -		  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
> -		  (long)__entry->idx,
> -		  __entry->startoff,
> -		  (int64_t)__entry->startblock,
> -		  __entry->blockcount,
> -		  __entry->state,
> -		  (char *)__entry->caller_ip)
> -);
> -
>  DECLARE_EVENT_CLASS(xfs_bmap_class,
>  	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state,
>  		 unsigned long caller_ip),
> @@ -264,7 +225,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  	TP_STRUCT__entry(
>  		__field(dev_t, dev)
>  		__field(xfs_ino_t, ino)
> -		__field(xfs_extnum_t, idx)
> +		__field(void *, leaf);
> +		__field(int, pos);
>  		__field(xfs_fileoff_t, startoff)
>  		__field(xfs_fsblock_t, startblock)
>  		__field(xfs_filblks_t, blockcount)
> @@ -280,7 +242,8 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  		xfs_iext_get_extent(ifp, cur, &r);
>  		__entry->dev = VFS_I(ip)->i_sb->s_dev;
>  		__entry->ino = ip->i_ino;
> -		__entry->idx = cur->idx;
> +		__entry->leaf = cur->leaf;
> +		__entry->pos = cur->pos;
>  		__entry->startoff = r.br_startoff;
>  		__entry->startblock = r.br_startblock;
>  		__entry->blockcount = r.br_blockcount;
> @@ -288,12 +251,13 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
>  		__entry->bmap_state = state;
>  		__entry->caller_ip = caller_ip;
>  	),
> -	TP_printk("dev %d:%d ino 0x%llx state %s idx %ld "
> +	TP_printk("dev %d:%d ino 0x%llx state %s cur 0x%p/%d "
>  		  "offset %lld block %lld count %lld flag %d caller %ps",
>  		  MAJOR(__entry->dev), MINOR(__entry->dev),
>  		  __entry->ino,
>  		  __print_flags(__entry->bmap_state, "|", XFS_BMAP_EXT_FLAGS),
> -		  (long)__entry->idx,
> +		  __entry->leaf,
> +		  __entry->pos,
>  		  __entry->startoff,
>  		  (int64_t)__entry->startblock,
>  		  __entry->blockcount,
> @@ -306,6 +270,7 @@ DEFINE_EVENT(xfs_bmap_class, name, \
>  	TP_PROTO(struct xfs_inode *ip, struct xfs_iext_cursor *cur, int state, \
>  		 unsigned long caller_ip), \
>  	TP_ARGS(ip, cur, state, caller_ip))
> +DEFINE_BMAP_EVENT(xfs_iext_insert);
>  DEFINE_BMAP_EVENT(xfs_iext_remove);
>  DEFINE_BMAP_EVENT(xfs_bmap_pre_update);
>  DEFINE_BMAP_EVENT(xfs_bmap_post_update);
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 16/18] xfs: use a b+tree for the in-core extent list
  2017-11-01 18:47   ` Darrick J. Wong
@ 2017-11-02  0:16     ` Darrick J. Wong
  2017-11-02  6:03       ` Christoph Hellwig
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-02  0:16 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

FWIW the attached crap patch fixes all the fstests failures I saw.

--D

---
 fs/xfs/libxfs/xfs_iext_tree.c |   21 ++++++++++++---------
 fs/xfs/xfs_trace.h            |    1 +
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_iext_tree.c b/fs/xfs/libxfs/xfs_iext_tree.c
index c18d344..2aa5651 100644
--- a/fs/xfs/libxfs/xfs_iext_tree.c
+++ b/fs/xfs/libxfs/xfs_iext_tree.c
@@ -28,17 +28,17 @@
  * In-core extent record layout:
  *
  * +-------+----------------------------+
- * | 00:51 | all 52 bits of startoff    |
- * | 52:63 | low 12 bits of startblock  |
+ * | 00:53 | all 54 bits of startoff    |
+ * | 54:63 | low 10 bits of startblock  |
  * +-------+----------------------------+
  * | 00:20 | all 21 bits of length      |
  * |    21 | unwritten extent bit       |
  * | 22:63 | high 42 bits of startblock |
  * +-------+----------------------------+
  */
-#define XFS_IEXT_STARTOFF_MASK		xfs_mask64lo(52)
-#define XFS_IEXT_LENGTH_MASK		xfs_mask64lo(21)
-#define XFS_IEXT_STARTBLOCK_MASK	xfs_mask64lo(54)
+#define XFS_IEXT_STARTOFF_MASK		xfs_mask64lo(BMBT_STARTOFF_BITLEN)
+#define XFS_IEXT_LENGTH_MASK		xfs_mask64lo(BMBT_BLOCKCOUNT_BITLEN)
+#define XFS_IEXT_STARTBLOCK_MASK	xfs_mask64lo(BMBT_STARTBLOCK_BITLEN)
 
 struct xfs_iext_rec {
 	uint64_t			lo;
@@ -72,8 +74,8 @@ xfs_iext_set(
 	rec->lo = irec->br_startoff & XFS_IEXT_STARTOFF_MASK;
 	rec->hi = irec->br_blockcount & XFS_IEXT_LENGTH_MASK;
 
-	rec->lo |= (irec->br_startblock << 52);
-	rec->hi |= ((irec->br_startblock & ~xfs_mask64lo(12)) << (22 - 12));
+	rec->lo |= (irec->br_startblock << 54);
+	rec->hi |= ((irec->br_startblock & ~xfs_mask64lo(10)) << (22 - 10));
 
 	if (irec->br_state == XFS_EXT_UNWRITTEN)
 		rec->hi |= (1 << 21);
@@ -87,8 +89,8 @@ xfs_iext_get(
 	irec->br_startoff = rec->lo & XFS_IEXT_STARTOFF_MASK;
 	irec->br_blockcount = rec->hi & XFS_IEXT_LENGTH_MASK;
 
-	irec->br_startblock = rec->lo >> 52;
-	irec->br_startblock |= (rec->hi & xfs_mask64hi(42)) >> (22 - 12);
+	irec->br_startblock = rec->lo >> 54;
+	irec->br_startblock |= (rec->hi & xfs_mask64hi(42)) >> (22 - 10);
 
 	if (rec->hi & (1 << 21))
 		irec->br_state = XFS_EXT_UNWRITTEN;

^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [PATCH 16/18] xfs: use a b+tree for the in-core extent list
  2017-11-02  0:16     ` Darrick J. Wong
@ 2017-11-02  6:03       ` Christoph Hellwig
  0 siblings, 0 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02  6:03 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Wed, Nov 01, 2017 at 05:16:27PM -0700, Darrick J. Wong wrote:
> FWIW the attached crap patch fixes all the fstests failures I saw.

Thanks, I'll take a look.  Been a little busy finishing off another
large non-xfs patchset and preparing a conference talk all at the same
time..

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-01 23:00         ` Darrick J. Wong
@ 2017-11-02 11:57           ` Brian Foster
  2017-11-02 16:05             ` Darrick J. Wong
  2017-11-02 23:45             ` Dave Chinner
  0 siblings, 2 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 11:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Wed, Nov 01, 2017 at 04:00:24PM -0700, Darrick J. Wong wrote:
> On Wed, Nov 01, 2017 at 09:58:55AM -0400, Brian Foster wrote:
> > On Tue, Oct 31, 2017 at 02:15:20PM -0700, Darrick J. Wong wrote:
> > > On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> > > > On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > > > > This prepares for getting rid of the current in-memory extent format.
> > > > > 
> > > > 
> > > > Couldn't we port this function over to use whatever the new in-memory
> > > > extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> > > > rather than the associated bit in the on-disk format..?
> > > 
> > > It's certainly possible, but in general verifiers are supposed to check
> > > on-disk metadata before they end up in-core, and this would seem to get
> > > us closer to that, right?
> > > 
> > 
> > Yeah, but are any of these calls actually made in a buffer verifier
> > path?
> 
> No.  They probably ought to be -- we can add them to the bmbt verifier and
> the inode fork verifier (in the magical future when those exist :P), but
> for now I think xfs_iread_extents is the closest we come to an "obvious"
> place where we load disk data into/out of its in-core representation.
> 

Ok.. I agree that verifiers are intended to check on-disk format, but I
still don't see how that design aspect of buffer verifiers really has
much bearing on this function given how it is currently used. This
particular block has already passed the associated buffer verifier.
Also, do we have any verifiers that consider fork state?

If the goal is to check the on-disk record, perhaps at least that part
of this check (associated with the extent flag bit(s)) should be moved
to the verifier? Even with doing that, it seems there still may be a
need for a higher level sanity check based on the fork, and I don't see
any reason why that necessarily needs to use the on-disk value as
opposed to the in-core value. *shrug* Not that big of a deal though..

Brian

> --D
> 
> > Brian
> > 
> > > --D
> > > 
> > > > Brian
> > > > 
> > > > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > > > ---
> > > > >  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
> > > > >  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
> > > > >  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
> > > > >  3 files changed, 9 insertions(+), 10 deletions(-)
> > > > > 
> > > > > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > > > > index f45f05c45e15..b2b6832b9e6b 100644
> > > > > --- a/fs/xfs/libxfs/xfs_bmap.c
> > > > > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > > > > @@ -1259,14 +1259,14 @@ xfs_iread_extents(
> > > > >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> > > > >  		for (j = 0; j < num_recs; j++, i++, frp++) {
> > > > >  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> > > > > -			trp->l0 = be64_to_cpu(frp->l0);
> > > > > -			trp->l1 = be64_to_cpu(frp->l1);
> > > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> > > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
> > > > >  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> > > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > > >  				error = -EFSCORRUPTED;
> > > > >  				goto out_brelse;
> > > > >  			}
> > > > > +			trp->l0 = be64_to_cpu(frp->l0);
> > > > > +			trp->l1 = be64_to_cpu(frp->l1);
> > > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > > >  		}
> > > > >  		xfs_trans_brelse(tp, bp);
> > > > > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > index 6f891eeb88f6..2fbfe2a24b15 100644
> > > > > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> > > > >   * Check that the extent does not contain an invalid unwritten extent flag.
> > > > >   */
> > > > >  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > > > > -		struct xfs_bmbt_rec_host *ep)
> > > > > +		struct xfs_bmbt_rec *ep)
> > > > >  {
> > > > > -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > > +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > >  		return true;
> > > > >  	if (whichfork == XFS_DATA_FORK &&
> > > > >  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > > > > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > index bb63f38b97cc..abe601b48c9c 100644
> > > > > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > @@ -371,13 +371,13 @@ xfs_iformat_extents(
> > > > >  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> > > > >  		for (i = 0; i < nex; i++, dp++) {
> > > > >  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > > -			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > > -			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> > > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
> > > > >  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> > > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > > >  				return -EFSCORRUPTED;
> > > > >  			}
> > > > > +			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > > +			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > > >  		}
> > > > >  	}
> > > > > @@ -764,8 +764,6 @@ xfs_iextents_copy(
> > > > >  	for (i = 0; i < nrecs; i++) {
> > > > >  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > >  
> > > > > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> > > > > -
> > > > >  		start_block = xfs_bmbt_get_startblock(ep);
> > > > >  		if (isnullstartblock(start_block)) {
> > > > >  			/*
> > > > > @@ -779,6 +777,7 @@ xfs_iextents_copy(
> > > > >  		/* Translate to on disk format */
> > > > >  		put_unaligned_be64(ep->l0, &dp->l0);
> > > > >  		put_unaligned_be64(ep->l1, &dp->l1);
> > > > > +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> > > > >  
> > > > >  		dp++;
> > > > >  		copied++;
> > > > > -- 
> > > > > 2.14.2
> > > > > 
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all
  2017-10-31 14:22 ` [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all Christoph Hellwig
  2017-10-31 21:34   ` Darrick J. Wong
@ 2017-11-02 13:54   ` Brian Foster
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 13:54 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:21PM +0200, Christoph Hellwig wrote:
> To make life a little simpler make xfs_bmbt_set_all unaligned access
> aware so that we can use it directly on the destination buffer.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap_btree.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
> index 086e6fc8e4fc..89260972a0f6 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.c
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.c
> @@ -199,14 +199,14 @@ xfs_bmbt_disk_set_all(
>  	ASSERT(!(s->br_blockcount & xfs_mask64hi(64-BMBT_BLOCKCOUNT_BITLEN)));
>  	ASSERT(!(s->br_startblock & xfs_mask64hi(64-BMBT_STARTBLOCK_BITLEN)));
>  
> -	r->l0 = cpu_to_be64(
> +	put_unaligned_be64(
>  		((xfs_bmbt_rec_base_t)extent_flag << 63) |
>  		 ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
> -		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43));
> -	r->l1 = cpu_to_be64(
> +		 ((xfs_bmbt_rec_base_t)s->br_startblock >> 43), &r->l0);
> +	put_unaligned_be64(
>  		((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
>  		 ((xfs_bmbt_rec_base_t)s->br_blockcount &
> -		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21)));
> +		  (xfs_bmbt_rec_base_t)xfs_mask64lo(21)), &r->l1);
>  }
>  
>  /*
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy
  2017-10-31 14:22 ` [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy Christoph Hellwig
  2017-10-31 21:41   ` Darrick J. Wong
@ 2017-11-02 13:54   ` Brian Foster
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 13:54 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:22PM +0200, Christoph Hellwig wrote:
> This actually makes the function very slightly less efficient for now as we
> detour through the expanded irect format between the in-core extent format
> and the on-disk one instead of just endian swapping them.  But with the
> incore extent btree the in-core one will use a different format and the
> representation will be entirely hidden.  It also happens to make the
> function a whole more readable.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_inode_fork.c | 53 +++++++++++-------------------------------
>  1 file changed, 13 insertions(+), 40 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index abe601b48c9c..7dd77b497fc2 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -725,9 +725,6 @@ xfs_iext_count(struct xfs_ifork *ifp)
>  /*
>   * Convert in-core extents to on-disk form
>   *
> - * For either the data or attr fork in extent format, we need to endian convert
> - * the in-core extent as we place them into the on-disk inode.
> - *
>   * In the case of the data fork, the in-core and on-disk fork sizes can be
>   * different due to delayed allocation extents. We only copy on-disk extents
>   * here, so callers must always use the physical fork size to determine the
> @@ -736,55 +733,31 @@ xfs_iext_count(struct xfs_ifork *ifp)
>   */
>  int
>  xfs_iextents_copy(
> -	xfs_inode_t		*ip,
> -	xfs_bmbt_rec_t		*dp,
> +	struct xfs_inode	*ip,
> +	struct xfs_bmbt_rec	*dp,
>  	int			whichfork)
>  {
>  	int			state = xfs_bmap_fork_to_state(whichfork);
> -	int			copied;
> -	int			i;
> -	xfs_ifork_t		*ifp;
> -	int			nrecs;
> -	xfs_fsblock_t		start_block;
> +	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
> +	struct xfs_bmbt_irec	rec;
> +	int			copied = 0, i = 0;
>  
> -	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED));
> +	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
>  	ASSERT(ifp->if_bytes > 0);
>  
> -	nrecs = xfs_iext_count(ifp);
> -	ASSERT(nrecs > 0);
> -
> -	/*
> -	 * There are some delayed allocation extents in the
> -	 * inode, so copy the extents one at a time and skip
> -	 * the delayed ones.  There must be at least one
> -	 * non-delayed extent.
> -	 */
> -	copied = 0;
> -	for (i = 0; i < nrecs; i++) {
> -		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> -
> -		start_block = xfs_bmbt_get_startblock(ep);
> -		if (isnullstartblock(start_block)) {
> -			/*
> -			 * It's a delayed allocation extent, so skip it.
> -			 */
> +	while (xfs_iext_get_extent(ifp, i++, &rec)) {
> +		if (isnullstartblock(rec.br_startblock))
>  			continue;
> -		}
> -
> +		xfs_bmbt_disk_set_all(dp, &rec);
>  		trace_xfs_write_extent(ip, i, state, _RET_IP_);
> -
> -		/* Translate to on disk format */
> -		put_unaligned_be64(ep->l0, &dp->l0);
> -		put_unaligned_be64(ep->l1, &dp->l1);
>  		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> -
> +		copied += sizeof(struct xfs_bmbt_rec);
>  		dp++;
> -		copied++;
>  	}
> -	ASSERT(copied != 0);
>  
> -	return (copied * (uint)sizeof(xfs_bmbt_rec_t));
> +	ASSERT(copied > 0);
> +	ASSERT(copied <= ifp->if_bytes);
> +	return copied;
>  }
>  
>  /*
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree
  2017-10-31 14:22 ` [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree Christoph Hellwig
  2017-10-31 21:41   ` Darrick J. Wong
@ 2017-11-02 13:54   ` Brian Foster
  1 sibling, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 13:54 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:23PM +0200, Christoph Hellwig wrote:
> This actually makes the function very slightly less efficient for now as we
> detour through the expanded irect format between the in-core extent format
> and the on-disk one instead of just endian swapping them.  But with the
> incore extent btree the in-core one will use a different format and the
> representation will be entirely hidden.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---

Reviewed-by: Brian Foster <bfoster@redhat.com>

>  fs/xfs/libxfs/xfs_bmap.c | 20 ++++++++------------
>  1 file changed, 8 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 14428d72cf33..56482bf6280d 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -666,14 +666,13 @@ xfs_bmap_extents_to_btree(
>  	xfs_bmbt_rec_t		*arp;		/* child record pointer */
>  	struct xfs_btree_block	*block;		/* btree root block */
>  	xfs_btree_cur_t		*cur;		/* bmap btree cursor */
> -	xfs_bmbt_rec_host_t	*ep;		/* extent record pointer */
>  	int			error;		/* error return value */
> -	xfs_extnum_t		i, cnt;		/* extent record index */
>  	xfs_ifork_t		*ifp;		/* inode fork pointer */
>  	xfs_bmbt_key_t		*kp;		/* root block key pointer */
>  	xfs_mount_t		*mp;		/* mount structure */
> -	xfs_extnum_t		nextents;	/* number of file extents */
>  	xfs_bmbt_ptr_t		*pp;		/* root block address pointer */
> +	struct xfs_bmbt_irec	rec;
> +	xfs_extnum_t		i = 0, cnt = 0;
>  
>  	mp = ip->i_mount;
>  	ASSERT(whichfork != XFS_COW_FORK);
> @@ -752,15 +751,12 @@ xfs_bmap_extents_to_btree(
>  				XFS_BTNUM_BMAP, 0, 0, ip->i_ino,
>  				XFS_BTREE_LONG_PTRS);
>  
> -	arp = XFS_BMBT_REC_ADDR(mp, ablock, 1);
> -	nextents =  xfs_iext_count(ifp);
> -	for (cnt = i = 0; i < nextents; i++) {
> -		ep = xfs_iext_get_ext(ifp, i);
> -		if (!isnullstartblock(xfs_bmbt_get_startblock(ep))) {
> -			arp->l0 = cpu_to_be64(ep->l0);
> -			arp->l1 = cpu_to_be64(ep->l1);
> -			arp++; cnt++;
> -		}
> +	while (xfs_iext_get_extent(ifp, i++, &rec)) {
> +		if (isnullstartblock(rec.br_startblock))
> +			continue;
> +		arp = XFS_BMBT_REC_ADDR(mp, ablock, 1 + cnt);
> +		xfs_bmbt_disk_set_all(arp, &rec);
> +		cnt++;
>  	}
>  	ASSERT(cnt == XFS_IFORK_NEXTENTS(ip, whichfork));
>  	xfs_btree_set_numrecs(ablock, cnt);
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-02 11:57           ` Brian Foster
@ 2017-11-02 16:05             ` Darrick J. Wong
  2017-11-02 16:54               ` Brian Foster
  2017-11-02 23:45             ` Dave Chinner
  1 sibling, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-02 16:05 UTC (permalink / raw)
  To: Brian Foster; +Cc: Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 07:57:11AM -0400, Brian Foster wrote:
> On Wed, Nov 01, 2017 at 04:00:24PM -0700, Darrick J. Wong wrote:
> > On Wed, Nov 01, 2017 at 09:58:55AM -0400, Brian Foster wrote:
> > > On Tue, Oct 31, 2017 at 02:15:20PM -0700, Darrick J. Wong wrote:
> > > > On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> > > > > On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > > > > > This prepares for getting rid of the current in-memory extent format.
> > > > > > 
> > > > > 
> > > > > Couldn't we port this function over to use whatever the new in-memory
> > > > > extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> > > > > rather than the associated bit in the on-disk format..?
> > > > 
> > > > It's certainly possible, but in general verifiers are supposed to check
> > > > on-disk metadata before they end up in-core, and this would seem to get
> > > > us closer to that, right?
> > > > 
> > > 
> > > Yeah, but are any of these calls actually made in a buffer verifier
> > > path?
> > 
> > No.  They probably ought to be -- we can add them to the bmbt verifier and
> > the inode fork verifier (in the magical future when those exist :P), but
> > for now I think xfs_iread_extents is the closest we come to an "obvious"
> > place where we load disk data into/out of its in-core representation.
> > 
> 
> Ok.. I agree that verifiers are intended to check on-disk format, but I
> still don't see how that design aspect of buffer verifiers really has
> much bearing on this function given how it is currently used. This
> particular block has already passed the associated buffer verifier.
> Also, do we have any verifiers that consider fork state?

Nope.  Thinking about this a little more, the only check we care about
wrt the bmbt records is that the unwritten flag can't be set on an attr
fork.  We don't have enough context to know that in the buffer verifier,
but we do know that when we're loading up the incore extent map, so we
can at least catch that problem there.

(Similarly, the bmbt btree code has a similar layering violation in that
we can only check bb_owner from xfs_btree_cursor context, but since we
also have no way to push such context through to the buffer layer we're
stuck with that for now.)

> If the goal is to check the on-disk record, perhaps at least that part
> of this check (associated with the extent flag bit(s)) should be moved
> to the verifier? Even with doing that, it seems there still may be a
> need for a higher level sanity check based on the fork, and I don't see
> any reason why that necessarily needs to use the on-disk value as
> opposed to the in-core value. *shrug* Not that big of a deal though..

<shrug> Same feeling here.  TBH I wonder about how costly those
unaligned be64 accesses are on things like sparc such that we should
minimize the number of calls and just check the (probably aligned)
incore version instead...

--D

> Brian
> 
> > --D
> > 
> > > Brian
> > > 
> > > > --D
> > > > 
> > > > > Brian
> > > > > 
> > > > > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > > > > ---
> > > > > >  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
> > > > > >  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
> > > > > >  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
> > > > > >  3 files changed, 9 insertions(+), 10 deletions(-)
> > > > > > 
> > > > > > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > > > > > index f45f05c45e15..b2b6832b9e6b 100644
> > > > > > --- a/fs/xfs/libxfs/xfs_bmap.c
> > > > > > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > > > > > @@ -1259,14 +1259,14 @@ xfs_iread_extents(
> > > > > >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> > > > > >  		for (j = 0; j < num_recs; j++, i++, frp++) {
> > > > > >  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> > > > > > -			trp->l0 = be64_to_cpu(frp->l0);
> > > > > > -			trp->l1 = be64_to_cpu(frp->l1);
> > > > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> > > > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
> > > > > >  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> > > > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > > > >  				error = -EFSCORRUPTED;
> > > > > >  				goto out_brelse;
> > > > > >  			}
> > > > > > +			trp->l0 = be64_to_cpu(frp->l0);
> > > > > > +			trp->l1 = be64_to_cpu(frp->l1);
> > > > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > > > >  		}
> > > > > >  		xfs_trans_brelse(tp, bp);
> > > > > > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > > index 6f891eeb88f6..2fbfe2a24b15 100644
> > > > > > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > > @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> > > > > >   * Check that the extent does not contain an invalid unwritten extent flag.
> > > > > >   */
> > > > > >  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > > > > > -		struct xfs_bmbt_rec_host *ep)
> > > > > > +		struct xfs_bmbt_rec *ep)
> > > > > >  {
> > > > > > -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > > > +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > > >  		return true;
> > > > > >  	if (whichfork == XFS_DATA_FORK &&
> > > > > >  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > > > > > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > > index bb63f38b97cc..abe601b48c9c 100644
> > > > > > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > > @@ -371,13 +371,13 @@ xfs_iformat_extents(
> > > > > >  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> > > > > >  		for (i = 0; i < nex; i++, dp++) {
> > > > > >  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > > > -			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > > > -			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> > > > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
> > > > > >  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> > > > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > > > >  				return -EFSCORRUPTED;
> > > > > >  			}
> > > > > > +			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > > > +			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > > > >  		}
> > > > > >  	}
> > > > > > @@ -764,8 +764,6 @@ xfs_iextents_copy(
> > > > > >  	for (i = 0; i < nrecs; i++) {
> > > > > >  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > > >  
> > > > > > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> > > > > > -
> > > > > >  		start_block = xfs_bmbt_get_startblock(ep);
> > > > > >  		if (isnullstartblock(start_block)) {
> > > > > >  			/*
> > > > > > @@ -779,6 +777,7 @@ xfs_iextents_copy(
> > > > > >  		/* Translate to on disk format */
> > > > > >  		put_unaligned_be64(ep->l0, &dp->l0);
> > > > > >  		put_unaligned_be64(ep->l1, &dp->l1);
> > > > > > +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> > > > > >  
> > > > > >  		dp++;
> > > > > >  		copied++;
> > > > > > -- 
> > > > > > 2.14.2
> > > > > > 
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > the body of a message to majordomo@vger.kernel.org
> > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-02 16:05             ` Darrick J. Wong
@ 2017-11-02 16:54               ` Brian Foster
  2017-11-02 18:42                 ` Christoph Hellwig
  0 siblings, 1 reply; 73+ messages in thread
From: Brian Foster @ 2017-11-02 16:54 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 09:05:05AM -0700, Darrick J. Wong wrote:
> On Thu, Nov 02, 2017 at 07:57:11AM -0400, Brian Foster wrote:
> > On Wed, Nov 01, 2017 at 04:00:24PM -0700, Darrick J. Wong wrote:
> > > On Wed, Nov 01, 2017 at 09:58:55AM -0400, Brian Foster wrote:
> > > > On Tue, Oct 31, 2017 at 02:15:20PM -0700, Darrick J. Wong wrote:
> > > > > On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> > > > > > On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > > > > > > This prepares for getting rid of the current in-memory extent format.
> > > > > > > 
> > > > > > 
> > > > > > Couldn't we port this function over to use whatever the new in-memory
> > > > > > extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> > > > > > rather than the associated bit in the on-disk format..?
> > > > > 
> > > > > It's certainly possible, but in general verifiers are supposed to check
> > > > > on-disk metadata before they end up in-core, and this would seem to get
> > > > > us closer to that, right?
> > > > > 
> > > > 
> > > > Yeah, but are any of these calls actually made in a buffer verifier
> > > > path?
> > > 
> > > No.  They probably ought to be -- we can add them to the bmbt verifier and
> > > the inode fork verifier (in the magical future when those exist :P), but
> > > for now I think xfs_iread_extents is the closest we come to an "obvious"
> > > place where we load disk data into/out of its in-core representation.
> > > 
> > 
> > Ok.. I agree that verifiers are intended to check on-disk format, but I
> > still don't see how that design aspect of buffer verifiers really has
> > much bearing on this function given how it is currently used. This
> > particular block has already passed the associated buffer verifier.
> > Also, do we have any verifiers that consider fork state?
> 
> Nope.  Thinking about this a little more, the only check we care about
> wrt the bmbt records is that the unwritten flag can't be set on an attr
> fork.  We don't have enough context to know that in the buffer verifier,
> but we do know that when we're loading up the incore extent map, so we
> can at least catch that problem there.
> 
> (Similarly, the bmbt btree code has a similar layering violation in that
> we can only check bb_owner from xfs_btree_cursor context, but since we
> also have no way to push such context through to the buffer layer we're
> stuck with that for now.)
> 
> > If the goal is to check the on-disk record, perhaps at least that part
> > of this check (associated with the extent flag bit(s)) should be moved
> > to the verifier? Even with doing that, it seems there still may be a
> > need for a higher level sanity check based on the fork, and I don't see
> > any reason why that necessarily needs to use the on-disk value as
> > opposed to the in-core value. *shrug* Not that big of a deal though..
> 
> <shrug> Same feeling here.  TBH I wonder about how costly those
> unaligned be64 accesses are on things like sparc such that we should
> minimize the number of calls and just check the (probably aligned)
> incore version instead...
> 

I'm not totally sure.. I guess it's potentially a function call where an
immediate memory copy would suffice..? Anyways, that's pretty much what
caused the function to stand out to me as starting to look a little too
busy/ugly for its own good. We now have a validation helper that has to
handle unaligned accesses because its caller may or may not refer to
unaligned memory. It's not even totally clear to me when we can expect
the memory to be unaligned.. when we're referring to an on-disk inode
fork?

So rather than start to propagate that logic, much more clean to me is
to do the unaligned accesses only where necessary and fix up the error
checks to examine the read values. I don't see any logical difference
between checking the bit vs. the extent state since the unwritten bit
basically maps directly to XFS_EXT_UNWRITTEN/XFS_EXT_NORM in the incore
record.

Brian

> --D
> 
> > Brian
> > 
> > > --D
> > > 
> > > > Brian
> > > > 
> > > > > --D
> > > > > 
> > > > > > Brian
> > > > > > 
> > > > > > > Signed-off-by: Christoph Hellwig <hch@lst.de>
> > > > > > > ---
> > > > > > >  fs/xfs/libxfs/xfs_bmap.c       | 6 +++---
> > > > > > >  fs/xfs/libxfs/xfs_bmap_btree.h | 4 ++--
> > > > > > >  fs/xfs/libxfs/xfs_inode_fork.c | 9 ++++-----
> > > > > > >  3 files changed, 9 insertions(+), 10 deletions(-)
> > > > > > > 
> > > > > > > diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> > > > > > > index f45f05c45e15..b2b6832b9e6b 100644
> > > > > > > --- a/fs/xfs/libxfs/xfs_bmap.c
> > > > > > > +++ b/fs/xfs/libxfs/xfs_bmap.c
> > > > > > > @@ -1259,14 +1259,14 @@ xfs_iread_extents(
> > > > > > >  		frp = XFS_BMBT_REC_ADDR(mp, block, 1);
> > > > > > >  		for (j = 0; j < num_recs; j++, i++, frp++) {
> > > > > > >  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
> > > > > > > -			trp->l0 = be64_to_cpu(frp->l0);
> > > > > > > -			trp->l1 = be64_to_cpu(frp->l1);
> > > > > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
> > > > > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, frp)) {
> > > > > > >  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> > > > > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > > > > >  				error = -EFSCORRUPTED;
> > > > > > >  				goto out_brelse;
> > > > > > >  			}
> > > > > > > +			trp->l0 = be64_to_cpu(frp->l0);
> > > > > > > +			trp->l1 = be64_to_cpu(frp->l1);
> > > > > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > > > > >  		}
> > > > > > >  		xfs_trans_brelse(tp, bp);
> > > > > > > diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > > > index 6f891eeb88f6..2fbfe2a24b15 100644
> > > > > > > --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > > > +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> > > > > > > @@ -127,9 +127,9 @@ extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
> > > > > > >   * Check that the extent does not contain an invalid unwritten extent flag.
> > > > > > >   */
> > > > > > >  static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> > > > > > > -		struct xfs_bmbt_rec_host *ep)
> > > > > > > +		struct xfs_bmbt_rec *ep)
> > > > > > >  {
> > > > > > > -	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > > > > +	if (get_unaligned_be64(&ep->l0) >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> > > > > > >  		return true;
> > > > > > >  	if (whichfork == XFS_DATA_FORK &&
> > > > > > >  	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> > > > > > > diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > > > index bb63f38b97cc..abe601b48c9c 100644
> > > > > > > --- a/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > > > +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> > > > > > > @@ -371,13 +371,13 @@ xfs_iformat_extents(
> > > > > > >  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> > > > > > >  		for (i = 0; i < nex; i++, dp++) {
> > > > > > >  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > > > > -			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > > > > -			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > > > > -			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> > > > > > > +			if (!xfs_bmbt_validate_extent(mp, whichfork, dp)) {
> > > > > > >  				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> > > > > > >  						 XFS_ERRLEVEL_LOW, mp);
> > > > > > >  				return -EFSCORRUPTED;
> > > > > > >  			}
> > > > > > > +			ep->l0 = get_unaligned_be64(&dp->l0);
> > > > > > > +			ep->l1 = get_unaligned_be64(&dp->l1);
> > > > > > >  			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> > > > > > >  		}
> > > > > > >  	}
> > > > > > > @@ -764,8 +764,6 @@ xfs_iextents_copy(
> > > > > > >  	for (i = 0; i < nrecs; i++) {
> > > > > > >  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> > > > > > >  
> > > > > > > -		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
> > > > > > > -
> > > > > > >  		start_block = xfs_bmbt_get_startblock(ep);
> > > > > > >  		if (isnullstartblock(start_block)) {
> > > > > > >  			/*
> > > > > > > @@ -779,6 +777,7 @@ xfs_iextents_copy(
> > > > > > >  		/* Translate to on disk format */
> > > > > > >  		put_unaligned_be64(ep->l0, &dp->l0);
> > > > > > >  		put_unaligned_be64(ep->l1, &dp->l1);
> > > > > > > +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, dp));
> > > > > > >  
> > > > > > >  		dp++;
> > > > > > >  		copied++;
> > > > > > > -- 
> > > > > > > 2.14.2
> > > > > > > 
> > > > > > > --
> > > > > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > > > > --
> > > > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > > > the body of a message to majordomo@vger.kernel.org
> > > > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > > > --
> > > > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > > > the body of a message to majordomo@vger.kernel.org
> > > > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-10-31 14:22 ` [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction Christoph Hellwig
  2017-10-31 22:02   ` Darrick J. Wong
@ 2017-11-02 17:14   ` Brian Foster
  2017-11-02 18:51     ` Christoph Hellwig
  2017-11-03  7:26     ` Christoph Hellwig
  1 sibling, 2 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 17:14 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Tue, Oct 31, 2017 at 04:22:24PM +0200, Christoph Hellwig wrote:
> Add a new xfs_iext_cursor structure to hide the direct extent map
> index manipulations. In addition to the existing lookup/get/insert/
> remove and update routines new primitives to get the first and last
> extent cursor, as well as moving up and down by one extent are
> provided.  Also new are convenience to increment/decrement the
> cursor and retreive the new extent, as well as to peek into the
> previous/next extent without updating the cursor and last but not
> least a macro to iterate over all extents in a fork.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c       | 432 ++++++++++++++++++++---------------------
>  fs/xfs/libxfs/xfs_bmap.h       |  12 +-
>  fs/xfs/libxfs/xfs_inode_fork.c |  75 +++----
>  fs/xfs/libxfs/xfs_inode_fork.h |  87 ++++++++-
>  fs/xfs/libxfs/xfs_types.h      |   3 +
>  fs/xfs/scrub/bmap.c            |   6 +-
>  fs/xfs/scrub/dir.c             |  14 +-
>  fs/xfs/xfs_bmap_util.c         |  12 +-
>  fs/xfs/xfs_dir2_readdir.c      |   8 +-
>  fs/xfs/xfs_dquot.c             |   4 +-
>  fs/xfs/xfs_iomap.c             |  13 +-
>  fs/xfs/xfs_reflink.c           |  56 +++---
>  fs/xfs/xfs_trace.h             |  12 +-
>  13 files changed, 401 insertions(+), 333 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 56482bf6280d..453dc1ae76ab 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
...
> @@ -1263,7 +1268,8 @@ xfs_iread_extents(
>  			}
>  			trp->l0 = be64_to_cpu(frp->l0);
>  			trp->l1 = be64_to_cpu(frp->l1);
> -			trace_xfs_read_extent(ip, i, state, _THIS_IP_);
> +			trace_xfs_read_extent(ip, &ext, state, _THIS_IP_);
> +			xfs_iext_next(ifp, &ext);

Can we just open code ext->idx here rather than maintain two counters,
or will that go away later?

BTW, I agree with Darrick's comment regarding 'ext' vs. 'cur' naming.
The former causes me to confuse the cursor field with the record field
in the cursor processing code.

>  		}
>  		xfs_trans_brelse(tp, bp);
>  		bno = nextbno;
...
> @@ -1553,8 +1558,6 @@ xfs_bmap_add_extent_delay_real(
>  	nextents = (whichfork == XFS_COW_FORK ? &bma->ip->i_cnextents :
>  						&bma->ip->i_d.di_nextents);
>  
> -	ASSERT(bma->idx >= 0);
> -	ASSERT(bma->idx <= xfs_iext_count(ifp));

I think it might be useful to encapsulate this check (which is also part
of xfs_iext_get_extent()) into a cursor validation helper so we can
preserve these asserts (here and in the other bmap functions). E.g.,
something like:

	ASSERT(xfs_iext_valid(&bma->ext));

>  	ASSERT(!isnullstartblock(new->br_startblock));
>  	ASSERT(!bma->cur ||
>  	       (bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
...
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 7dd77b497fc2..c9e10d4818b7 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
...
> @@ -1948,31 +1955,31 @@ xfs_iext_lookup_extent_before(
>  	struct xfs_inode	*ip,
>  	struct xfs_ifork	*ifp,
>  	xfs_fileoff_t		*end,
> -	xfs_extnum_t		*idxp,
> +	struct xfs_iext_cursor	*cur,
>  	struct xfs_bmbt_irec	*gotp)
>  {
> -	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, idxp, gotp) &&
> +	if (xfs_iext_lookup_extent(ip, ifp, *end - 1, cur, gotp) &&
>  	    gotp->br_startoff <= *end - 1)
>  		return true;
> -	if (!xfs_iext_get_extent(ifp, --*idxp, gotp))
> +	if (!xfs_iext_prev_extent(ifp, cur, gotp))
>  		return false;
>  	*end = gotp->br_startoff + gotp->br_blockcount;
>  	return true;
>  }
>  
>  /*
> - * Return true if there is an extent at index idx, and return the expanded
> - * extent structure at idx in that case.  Else return false.
> + * Return true if there is an extent at cursor cur and return the expanded
> + * extent structure at cur in gotp in that case.  Else return false.

"Return true if the cursor points at an extent and return the extent
structure in gotp. Else return false."

>   */
>  bool
>  xfs_iext_get_extent(
>  	struct xfs_ifork	*ifp,
> -	xfs_extnum_t		idx,
> +	struct xfs_iext_cursor	*cur,
>  	struct xfs_bmbt_irec	*gotp)
>  {
> -	if (idx < 0 || idx >= xfs_iext_count(ifp))
> +	if (cur->idx < 0 || cur->idx >= xfs_iext_count(ifp))
>  		return false;
> -	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), gotp);
> +	xfs_bmbt_get_all(xfs_iext_get_ext(ifp, cur->idx), gotp);
>  	return true;
>  }
>  
...
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h
> index 113fd42ec36d..dc347dd9dc78 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.h
> +++ b/fs/xfs/libxfs/xfs_inode_fork.h
...
> @@ -182,15 +183,85 @@ void		xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
>  
>  bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
>  			struct xfs_ifork *ifp, xfs_fileoff_t bno,
> -			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
> +			struct xfs_iext_cursor *cur,
> +			 struct xfs_bmbt_irec *gotp);

Indentation looks off here.

>  bool		xfs_iext_lookup_extent_before(struct xfs_inode *ip,
>  			struct xfs_ifork *ifp, xfs_fileoff_t *end,
> -			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
> -
> -bool		xfs_iext_get_extent(struct xfs_ifork *ifp, xfs_extnum_t idx,
> +			struct xfs_iext_cursor *cur,
> +			struct xfs_bmbt_irec *gotp);
> +bool		xfs_iext_get_extent(struct xfs_ifork *ifp,
> +			struct xfs_iext_cursor *cur,
>  			struct xfs_bmbt_irec *gotp);
>  void		xfs_iext_update_extent(struct xfs_inode *ip, int state,
> -			xfs_extnum_t idx, struct xfs_bmbt_irec *gotp);
> +			struct xfs_iext_cursor *cur,
> +			struct xfs_bmbt_irec *gotp);
> +
...
> diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h
> index f04dbfb2f50d..5da6382bdaf1 100644
> --- a/fs/xfs/libxfs/xfs_types.h
> +++ b/fs/xfs/libxfs/xfs_types.h
> @@ -142,5 +142,8 @@ typedef uint32_t	xfs_dqid_t;
>  #define	XFS_NBWORD	(1 << XFS_NBWORDLOG)
>  #define	XFS_WORDMASK	((1 << XFS_WORDLOG) - 1)
>  
> +struct xfs_iext_cursor {
> +	xfs_extnum_t		idx;
> +};
>  

FWIW, this patch has me wondering about a couple things that may not be
apparent until I get through more of the series:

1.) Why do we place these new cursors directly on the stack as opposed
to dynamic allocation?
2.) Why not encode the fork/inode in the cursor rather than passing them
around throughout the helper functions?

Brian

...
> -- 
> 2.14.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-02 16:54               ` Brian Foster
@ 2017-11-02 18:42                 ` Christoph Hellwig
  2017-11-02 19:35                   ` Brian Foster
  0 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 18:42 UTC (permalink / raw)
  To: Brian Foster; +Cc: Darrick J. Wong, Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 12:54:19PM -0400, Brian Foster wrote:
> > <shrug> Same feeling here.  TBH I wonder about how costly those
> > unaligned be64 accesses are on things like sparc such that we should
> > minimize the number of calls and just check the (probably aligned)
> > incore version instead...
> > 
> 
> I'm not totally sure.. I guess it's potentially a function call where an
> immediate memory copy would suffice..? Anyways, that's pretty much what
> caused the function to stand out to me as starting to look a little too
> busy/ugly for its own good. We now have a validation helper that has to
> handle unaligned accesses because its caller may or may not refer to
> unaligned memory. It's not even totally clear to me when we can expect
> the memory to be unaligned.. when we're referring to an on-disk inode
> fork?

We are iterating over each possibly unaligned on-disk extent, so we
already are taking the hit.  In all modern CPUs unaligned access
don't cause a function call.  They either require a slightly different
load instruction or multiple small (e.g. byte) loads.  I'd really like
to see anyone who can demonstrate a difference vs reading the inode or
bmap btree from disk.

> So rather than start to propagate that logic, much more clean to me is
> to do the unaligned accesses only where necessary and fix up the error
> checks to examine the read values. I don't see any logical difference
> between checking the bit vs. the extent state since the unwritten bit
> basically maps directly to XFS_EXT_UNWRITTEN/XFS_EXT_NORM in the incore
> record.

With the new incore extent list I wanted to keep the incore extent
format private to the xfs_iext_tree.c file, and so far succeeded.

With the final version of this tree I could switch to checking
the xfs_bmbt_irec structure, which makes a whole lot sense, but I
can't think of an easy way to stage this.  Let me thing if I can come
up with something, else I'll leave this patch as-is and will change it
again in the end.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-10-31 22:02   ` Darrick J. Wong
@ 2017-11-02 18:49     ` Christoph Hellwig
  2017-11-02 19:01       ` Darrick J. Wong
  0 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 18:49 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Tue, Oct 31, 2017 at 03:02:34PM -0700, Darrick J. Wong wrote:
> > +	struct xfs_iext_cursor ext;
> 
> I was expecting an extent map cursor to have 'cur' in the name --
> 'ext' misleads me into thinking that 'ext' is an actual extent.
> 
> struct xfs_iext_cursor	*icur;
> 
> Also, xfs_iext_insert names its cursor parameter *cur, so why ext here?

I started out with cur, but in most bmap routines we already have the
btree cursor named cur, so I had to look for something else.  I could
probably change to icur, but that will be a heck of an annoying rebase..

> For now I'm going to assume that xfs_iext_{first,last,next,prev} will
> actually do something with the ifp parameter in the near future.

Yes, good assumption.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-11-02 17:14   ` Brian Foster
@ 2017-11-02 18:51     ` Christoph Hellwig
  2017-11-02 19:36       ` Brian Foster
  2017-11-03  7:26     ` Christoph Hellwig
  1 sibling, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 18:51 UTC (permalink / raw)
  To: Brian Foster; +Cc: Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 01:14:37PM -0400, Brian Foster wrote:
> Can we just open code ext->idx here rather than maintain two counters,
> or will that go away later?

This area will change quite a bit.  Please take a look at the end result.

> > -	ASSERT(bma->idx >= 0);
> > -	ASSERT(bma->idx <= xfs_iext_count(ifp));
> 
> I think it might be useful to encapsulate this check (which is also part
> of xfs_iext_get_extent()) into a cursor validation helper so we can
> preserve these asserts (here and in the other bmap functions). E.g.,
> something like:
> 
> 	ASSERT(xfs_iext_valid(&bma->ext));

I'll take a look at that.

> 1.) Why do we place these new cursors directly on the stack as opposed
> to dynamic allocation?

Why would be do a dynamic allocation?

> 2.) Why not encode the fork/inode in the cursor rather than passing them
> around throughout the helper functions?

We could do that, but I'm not sure it's really worth the effort.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow
  2017-10-31 22:20   ` Darrick J. Wong
@ 2017-11-02 18:56     ` Christoph Hellwig
  0 siblings, 0 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 18:56 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Tue, Oct 31, 2017 at 03:20:45PM -0700, Darrick J. Wong wrote:
> >  #define XFS_BMAPI_DELALLOC	0x400
> >  
> > +/* Only convert unwritten extents, don't allocate new hotels */
> 
> New ... blocks?

blocks.

> > +#define XFS_BMAPI_CONVERT_ONLY	0x800
> 
> I wonder if this and XFS_BMAPI_DELALLOC could be solved with a single
> flag that means "don't fill in any holes; only touch pre-existing
> extents" ?

Maybe..  My real plan here is to get rid of xfs_bmapi_write in its
current form.  When we convert delalloc to real we never want to allocate
blocks for holes, and in general the loop will look fairly different.

Except for a DAX special case when we convert unwritten extents we never
really want to fill holes.  There is just way to much magic in
xfs_bmapi_write and it would benefit a lot from being split into a three
different functions.

But I'd like to finish one project first before getting into another
big one..

> >  #define XFS_BMAPI_FLAGS \
> >  	{ XFS_BMAPI_ENTIRE,	"ENTIRE" }, \
> >  	{ XFS_BMAPI_METADATA,	"METADATA" }, \
> 
> If you do end up adding a new BMAPI flag, this needs updating so the
> tracepoints keep working.

Fixed.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork
  2017-10-31 22:35   ` Darrick J. Wong
@ 2017-11-02 18:57     ` Christoph Hellwig
  2017-11-02 19:26       ` Darrick J. Wong
  0 siblings, 1 reply; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 18:57 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Tue, Oct 31, 2017 at 03:35:08PM -0700, Darrick J. Wong wrote:
> Do you see any secondary effects, such as increased slab fragmentation
> because of the extra kmem_allocs?  In general I think this should be ok,
> I just worry slightly that whatever reason we had for having
> if_inline_data is still around and will blow up in weird ways if we get
> rid of it.

Why would we get much slab fragmentation?  The small slabs (16 or 32
byte for those current users) have a huge turnover, so they generally
aren't a majr problem.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-11-02 18:49     ` Christoph Hellwig
@ 2017-11-02 19:01       ` Darrick J. Wong
  2017-11-02 19:11         ` Christoph Hellwig
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-02 19:01 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Thu, Nov 02, 2017 at 07:49:42PM +0100, Christoph Hellwig wrote:
> On Tue, Oct 31, 2017 at 03:02:34PM -0700, Darrick J. Wong wrote:
> > > +	struct xfs_iext_cursor ext;
> > 
> > I was expecting an extent map cursor to have 'cur' in the name --
> > 'ext' misleads me into thinking that 'ext' is an actual extent.
> > 
> > struct xfs_iext_cursor	*icur;
> > 
> > Also, xfs_iext_insert names its cursor parameter *cur, so why ext here?
> 
> I started out with cur, but in most bmap routines we already have the
> btree cursor named cur, so I had to look for something else.  I could
> probably change to icur, but that will be a heck of an annoying rebase..

How many more patches do you have after the ones you've already sent?

I /do/ have the ability to do evil things like sed -e 's/ext,/icur,/g'
to the patches and re-stuff them in the git branch.  I probably wouldn't
bother with the ones already named 'cur', it's just the 'ext' ones that
increase my cognitive impedance.

--D

> > For now I'm going to assume that xfs_iext_{first,last,next,prev} will
> > actually do something with the ifp parameter in the near future.
> 
> Yes, good assumption.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 16/18] xfs: use a b+tree for the in-core extent list
  2017-11-02  0:14   ` Darrick J. Wong
@ 2017-11-02 19:09     ` Christoph Hellwig
  0 siblings, 0 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 19:09 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

> > + * Note that we currently always allocate 64-bits worth for pointers in the
> > + * inner nodes or the link pointers in the leaf nodes even on 32-bit
> 
> We do?  sizeof(void *) == 4 on 32-bit; on a i686 machine this evaluates to....

We did :)  Things changed a few times, so I guess I can relax this.

.. and I need to do another test run on i386..

> Does RECS_PER_LEAF reserve two uint64_t for the prev and next pointers
> in struct xfs_iext_leaf?  It'd be a little more clear if the definition
> was:
> 
> RECS_PER_LEAF = (NODE_SIZE - (2 * sizeof(void *))) / sizeof(xfs_iext_rec)

Or rather:

	RECS_PER_LEAF = (NODE_SIZE - (2 * sizeof(struct xfs_iext_leaf *))) \
			sizeof(xfs_iext_rec);

> 
> The L1 cache sizes range from 4 bytes (h8300) to 256 bytes (s390), with
> x64 at 64 bytes and arm64/ppc64 at 128.
> 
> This means that this is broken on h8300 because it's not possible to
> create a leaf containing even one record.  Not that even a 32-byte leaf
> is acceptable in terms of overhead.  Perhaps NODE_SIZE should have a
> minimum?

Hah.  I missed the odd h8300 case.  Maybe we should just make it constant
256 - that's what'll get test most anyway.

> 2^32 max extent records
> RECS_PER_LEAF = 15
> KEYS_PER_NODE = 16
> 
> level 0 = 286,331,154 leaf blocks
> level 1 = 17,895,698 nodes
> level 2 = 1,118,482 nodes
> level 3 = 69,906 nodes
> level 4 = 4,370 nodes
> level 5 = 274 nodes
> level 6 = 18 nodes
> level 7 = 2 nodes
> level 8 = 1 node
> == 305,419,904 blocks, or 78GB of RAM?  The leaves eat 68G, the nodes eat 10G.

Note that leafs and nodes will usually not be fully used up.

So double the above for the worst case.

> > +inline xfs_extnum_t xfs_iext_count(struct xfs_ifork *ifp)
> 
> static inline?

It is used outside this file.

> > +	for (i = nr_entries; i > pos; i--) {
> > +		node->keys[i] = node->keys[i - 1];
> > +		node->ptrs[i] = node->ptrs[i - 1];
> > +	}
> 
> /me wonders if memmove is more appropriate here, but I'm guessing the
> loop came out of lib/btree.c?

Not using memmove has the big advantage of being type safe.  And with
modern compilers opencoding it or not should not matter, although
now that you mention it I'll take a look at the assembly output.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-11-02 19:01       ` Darrick J. Wong
@ 2017-11-02 19:11         ` Christoph Hellwig
  0 siblings, 0 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-02 19:11 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 12:01:15PM -0700, Darrick J. Wong wrote:
> How many more patches do you have after the ones you've already sent?

None for now, but they are fairly iterative.

> I /do/ have the ability to do evil things like sed -e 's/ext,/icur,/g'
> to the patches and re-stuff them in the git branch.  I probably wouldn't
> bother with the ones already named 'cur', it's just the 'ext' ones that
> increase my cognitive impedance.

Ok, I'll look into it for V2.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork
  2017-11-02 18:57     ` Christoph Hellwig
@ 2017-11-02 19:26       ` Darrick J. Wong
  2017-11-02 21:43         ` Dave Chinner
  0 siblings, 1 reply; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-02 19:26 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Thu, Nov 02, 2017 at 07:57:25PM +0100, Christoph Hellwig wrote:
> On Tue, Oct 31, 2017 at 03:35:08PM -0700, Darrick J. Wong wrote:
> > Do you see any secondary effects, such as increased slab fragmentation
> > because of the extra kmem_allocs?  In general I think this should be ok,
> > I just worry slightly that whatever reason we had for having
> > if_inline_data is still around and will blow up in weird ways if we get
> > rid of it.
> 
> Why would we get much slab fragmentation?  The small slabs (16 or 32
> byte for those current users) have a huge turnover, so they generally
> aren't a majr problem.

I don't think they will be a serious problem either; this is just me
wondering why we had if_inline_data in the first place (now that we're
removing it).

--D

> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-02 18:42                 ` Christoph Hellwig
@ 2017-11-02 19:35                   ` Brian Foster
  0 siblings, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 19:35 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Darrick J. Wong, linux-xfs

On Thu, Nov 02, 2017 at 07:42:21PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 02, 2017 at 12:54:19PM -0400, Brian Foster wrote:
> > > <shrug> Same feeling here.  TBH I wonder about how costly those
> > > unaligned be64 accesses are on things like sparc such that we should
> > > minimize the number of calls and just check the (probably aligned)
> > > incore version instead...
> > > 
> > 
> > I'm not totally sure.. I guess it's potentially a function call where an
> > immediate memory copy would suffice..? Anyways, that's pretty much what
> > caused the function to stand out to me as starting to look a little too
> > busy/ugly for its own good. We now have a validation helper that has to
> > handle unaligned accesses because its caller may or may not refer to
> > unaligned memory. It's not even totally clear to me when we can expect
> > the memory to be unaligned.. when we're referring to an on-disk inode
> > fork?
> 
> We are iterating over each possibly unaligned on-disk extent, so we
> already are taking the hit.  In all modern CPUs unaligned access
> don't cause a function call.  They either require a slightly different
> load instruction or multiple small (e.g. byte) loads.  I'd really like
> to see anyone who can demonstrate a difference vs reading the inode or
> bmap btree from disk.
> 

Ok.

> > So rather than start to propagate that logic, much more clean to me is
> > to do the unaligned accesses only where necessary and fix up the error
> > checks to examine the read values. I don't see any logical difference
> > between checking the bit vs. the extent state since the unwritten bit
> > basically maps directly to XFS_EXT_UNWRITTEN/XFS_EXT_NORM in the incore
> > record.
> 
> With the new incore extent list I wanted to keep the incore extent
> format private to the xfs_iext_tree.c file, and so far succeeded.
> 
> With the final version of this tree I could switch to checking
> the xfs_bmbt_irec structure, which makes a whole lot sense, but I
> can't think of an easy way to stage this.  Let me thing if I can come
> up with something, else I'll leave this patch as-is and will change it
> again in the end.

Right.. checking xfs_bmbt_irec->br_state is precisely what I'm
advocating. If it's easier just to change it after the fact, that's fine
by me.

Brian

> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-11-02 18:51     ` Christoph Hellwig
@ 2017-11-02 19:36       ` Brian Foster
  0 siblings, 0 replies; 73+ messages in thread
From: Brian Foster @ 2017-11-02 19:36 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: linux-xfs

On Thu, Nov 02, 2017 at 07:51:38PM +0100, Christoph Hellwig wrote:
> On Thu, Nov 02, 2017 at 01:14:37PM -0400, Brian Foster wrote:
> > Can we just open code ext->idx here rather than maintain two counters,
> > or will that go away later?
> 
> This area will change quite a bit.  Please take a look at the end result.
> 

Ok.. after a quick look at the final xfs_iread_extents(), it appears
that concern is no longer relevant.

> > > -	ASSERT(bma->idx >= 0);
> > > -	ASSERT(bma->idx <= xfs_iext_count(ifp));
> > 
> > I think it might be useful to encapsulate this check (which is also part
> > of xfs_iext_get_extent()) into a cursor validation helper so we can
> > preserve these asserts (here and in the other bmap functions). E.g.,
> > something like:
> > 
> > 	ASSERT(xfs_iext_valid(&bma->ext));
> 
> I'll take a look at that.
> 
> > 1.) Why do we place these new cursors directly on the stack as opposed
> > to dynamic allocation?
> 
> Why would be do a dynamic allocation?
> 

I just noticed it as a divergence from our other cursor implementations.
This is of course a different implementation, however. I guess it more
depends on what ends up in the cursor. It looks like the cursor ends up
with an integer index and leaf pointer, the latter of which appears to
be memory owned by the inode..? If that's the case, then perhaps there
is really no point to dynamic allocation of the cursor itself.

> > 2.) Why not encode the fork/inode in the cursor rather than passing them
> > around throughout the helper functions?
> 
> We could do that, but I'm not sure it's really worth the effort.

I'm not against cleaning it up after the fact. The broader question was
whether there was any reason we'd need to pass the fork to every call.
If not, I think combining it with the cursor may provide a cleaner
interface.

Brian

> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork
  2017-11-02 19:26       ` Darrick J. Wong
@ 2017-11-02 21:43         ` Dave Chinner
  2017-11-02 22:08           ` Darrick J. Wong
  0 siblings, 1 reply; 73+ messages in thread
From: Dave Chinner @ 2017-11-02 21:43 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 12:26:34PM -0700, Darrick J. Wong wrote:
> On Thu, Nov 02, 2017 at 07:57:25PM +0100, Christoph Hellwig wrote:
> > On Tue, Oct 31, 2017 at 03:35:08PM -0700, Darrick J. Wong wrote:
> > > Do you see any secondary effects, such as increased slab fragmentation
> > > because of the extra kmem_allocs?  In general I think this should be ok,
> > > I just worry slightly that whatever reason we had for having
> > > if_inline_data is still around and will blow up in weird ways if we get
> > > rid of it.
> > 
> > Why would we get much slab fragmentation?  The small slabs (16 or 32
> > byte for those current users) have a huge turnover, so they generally
> > aren't a majr problem.
> 
> I don't think they will be a serious problem either; this is just me
> wondering why we had if_inline_data in the first place (now that we're
> removing it).

Think back to ~1993 when XFS was first being implemented. State of
the art was 100-150MHz CPUs, and so the cost of an allocation for
every inode with a single data extent used a substantial fraction of
the available CPU. And given that most files are a single extent,
this was a worthwhile optimisation to minimise CPU overhead of the
initial data read on a file.

Nowdays, memory allocation costs less in terms of instructions than
it did on Irix in 1993, and the CPUs are also a couple of orders of
magnitudes faster. IOWs, the optimisation won't make as much
difference to performance now as it did 20 years ago...

It's the same reason we have the data fork in the inode, but the
attr fork is dynamically allocated - every inode has it's data fork
referenced, but attr fork references are comparitively rare and
generally not performance sensitive and so allocating the attr fork
was a good trade-off between CPU overhead for those that needed
attrs vs lower memory usage for the larger majority of users...

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork
  2017-11-02 21:43         ` Dave Chinner
@ 2017-11-02 22:08           ` Darrick J. Wong
  0 siblings, 0 replies; 73+ messages in thread
From: Darrick J. Wong @ 2017-11-02 22:08 UTC (permalink / raw)
  To: Dave Chinner; +Cc: Christoph Hellwig, linux-xfs

On Fri, Nov 03, 2017 at 08:43:21AM +1100, Dave Chinner wrote:
> On Thu, Nov 02, 2017 at 12:26:34PM -0700, Darrick J. Wong wrote:
> > On Thu, Nov 02, 2017 at 07:57:25PM +0100, Christoph Hellwig wrote:
> > > On Tue, Oct 31, 2017 at 03:35:08PM -0700, Darrick J. Wong wrote:
> > > > Do you see any secondary effects, such as increased slab fragmentation
> > > > because of the extra kmem_allocs?  In general I think this should be ok,
> > > > I just worry slightly that whatever reason we had for having
> > > > if_inline_data is still around and will blow up in weird ways if we get
> > > > rid of it.
> > > 
> > > Why would we get much slab fragmentation?  The small slabs (16 or 32
> > > byte for those current users) have a huge turnover, so they generally
> > > aren't a majr problem.
> > 
> > I don't think they will be a serious problem either; this is just me
> > wondering why we had if_inline_data in the first place (now that we're
> > removing it).
> 
> Think back to ~1993 when XFS was first being implemented. State of
> the art was 100-150MHz CPUs, and so the cost of an allocation for
> every inode with a single data extent used a substantial fraction of
> the available CPU. And given that most files are a single extent,
> this was a worthwhile optimisation to minimise CPU overhead of the
> initial data read on a file.

Heh, that's what I expected was the reason.  Carry on, then. :)

> Nowdays, memory allocation costs less in terms of instructions than
> it did on Irix in 1993, and the CPUs are also a couple of orders of
> magnitudes faster. IOWs, the optimisation won't make as much
> difference to performance now as it did 20 years ago...
> 
> It's the same reason we have the data fork in the inode, but the
> attr fork is dynamically allocated - every inode has it's data fork
> referenced, but attr fork references are comparitively rare and
> generally not performance sensitive and so allocating the attr fork
> was a good trade-off between CPU overhead for those that needed
> attrs vs lower memory usage for the larger majority of users...

<nod>

--D

> Cheers,
> 
> Dave.
> -- 
> Dave Chinner
> david@fromorbit.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent
  2017-11-02 11:57           ` Brian Foster
  2017-11-02 16:05             ` Darrick J. Wong
@ 2017-11-02 23:45             ` Dave Chinner
  1 sibling, 0 replies; 73+ messages in thread
From: Dave Chinner @ 2017-11-02 23:45 UTC (permalink / raw)
  To: Brian Foster; +Cc: Darrick J. Wong, Christoph Hellwig, linux-xfs

On Thu, Nov 02, 2017 at 07:57:11AM -0400, Brian Foster wrote:
> On Wed, Nov 01, 2017 at 04:00:24PM -0700, Darrick J. Wong wrote:
> > On Wed, Nov 01, 2017 at 09:58:55AM -0400, Brian Foster wrote:
> > > On Tue, Oct 31, 2017 at 02:15:20PM -0700, Darrick J. Wong wrote:
> > > > On Tue, Oct 31, 2017 at 01:53:12PM -0400, Brian Foster wrote:
> > > > > On Tue, Oct 31, 2017 at 04:22:13PM +0200, Christoph Hellwig wrote:
> > > > > > This prepares for getting rid of the current in-memory extent format.
> > > > > > 
> > > > > 
> > > > > Couldn't we port this function over to use whatever the new in-memory
> > > > > extent format is? IOW, just have the helper check for XFS_EXT_UNWRITTEN
> > > > > rather than the associated bit in the on-disk format..?
> > > > 
> > > > It's certainly possible, but in general verifiers are supposed to check
> > > > on-disk metadata before they end up in-core, and this would seem to get
> > > > us closer to that, right?
> > > > 
> > > 
> > > Yeah, but are any of these calls actually made in a buffer verifier
> > > path?
> > 
> > No.  They probably ought to be -- we can add them to the bmbt verifier and
> > the inode fork verifier (in the magical future when those exist :P), but
> > for now I think xfs_iread_extents is the closest we come to an "obvious"
> > place where we load disk data into/out of its in-core representation.
> > 
> 
> Ok.. I agree that verifiers are intended to check on-disk format, but I
> still don't see how that design aspect of buffer verifiers really has
> much bearing on this function given how it is currently used. This
> particular block has already passed the associated buffer verifier.
> Also, do we have any verifiers that consider fork state?
> 
> If the goal is to check the on-disk record, perhaps at least that part
> of this check (associated with the extent flag bit(s)) should be moved
> to the verifier? Even with doing that, it seems there still may be a
> need for a higher level sanity check based on the fork, and I don't see
> any reason why that necessarily needs to use the on-disk value as
> opposed to the in-core value. *shrug* Not that big of a deal though..

IMO, we shouldn't be moving the buffer contents verification to the
verifier.  IO completion context is a CPU and latency sensitive area
of the code. The verifier is there to verify the /structure/ of the
metadata is valid (e.g. btree block headers), not the individual
metadata records within the buffer contain valid values
(records/ptrs).

The CRC that the verifier checks tells us the metadata records
within the block are the same as what was last written, and that's
about as much of the buffer data as the verifier should be checking.
We do content based verification when we get the checked buffer into
the subsystem that parses the content.

IOWs, xfs_iread_extents() is, IMO, the right place to be verifying
that BMBT records being read from a buffer with "verified contents".

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction
  2017-11-02 17:14   ` Brian Foster
  2017-11-02 18:51     ` Christoph Hellwig
@ 2017-11-03  7:26     ` Christoph Hellwig
  1 sibling, 0 replies; 73+ messages in thread
From: Christoph Hellwig @ 2017-11-03  7:26 UTC (permalink / raw)
  To: Brian Foster; +Cc: Christoph Hellwig, linux-xfs

> > @@ -1553,8 +1558,6 @@ xfs_bmap_add_extent_delay_real(
> >  	nextents = (whichfork == XFS_COW_FORK ? &bma->ip->i_cnextents :
> >  						&bma->ip->i_d.di_nextents);

> > -	ASSERT(bma->idx >= 0);
> > -	ASSERT(bma->idx <= xfs_iext_count(ifp));
> 
> I think it might be useful to encapsulate this check (which is also part
> of xfs_iext_get_extent()) into a cursor validation helper so we can
> preserve these asserts (here and in the other bmap functions). E.g.,
> something like:
> 
> 	ASSERT(xfs_iext_valid(&bma->ext));

The bug problem here is that we don not check if it is valid, it just
is a plausability check.  bma->idx == xfs_iext_count(ifp) is not a
a valid index, but one beyond valid, a fact that the whole code
relies on.

But we do check for validity and plausability in the actual btree code,
so I don't think the additional checks here add much value.

> > - * Return true if there is an extent at index idx, and return the expanded
> > - * extent structure at idx in that case.  Else return false.
> > + * Return true if there is an extent at cursor cur and return the expanded
> > + * extent structure at cur in gotp in that case.  Else return false.
> 
> "Return true if the cursor points at an extent and return the extent
> structure in gotp. Else return false."

Fixed.

> >  bool		xfs_iext_lookup_extent(struct xfs_inode *ip,
> >  			struct xfs_ifork *ifp, xfs_fileoff_t bno,
> > -			xfs_extnum_t *idxp, struct xfs_bmbt_irec *gotp);
> > +			struct xfs_iext_cursor *cur,
> > +			 struct xfs_bmbt_irec *gotp);
> 
> Indentation looks off here.

Fixed.


^ permalink raw reply	[flat|nested] 73+ messages in thread

end of thread, other threads:[~2017-11-03  7:26 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-31 14:22 b+tree for the incore extent list Christoph Hellwig
2017-10-31 14:22 ` [PATCH 01/18] xfs: pass an on-disk extent to xfs_bmbt_validate_extent Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:15     ` Darrick J. Wong
2017-11-01 13:58       ` Brian Foster
2017-11-01 23:00         ` Darrick J. Wong
2017-11-02 11:57           ` Brian Foster
2017-11-02 16:05             ` Darrick J. Wong
2017-11-02 16:54               ` Brian Foster
2017-11-02 18:42                 ` Christoph Hellwig
2017-11-02 19:35                   ` Brian Foster
2017-11-02 23:45             ` Dave Chinner
2017-10-31 14:22 ` [PATCH 02/18] xfs: don't create overlapping extents in xfs_bmap_add_extent_delay_real Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:34   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 03/18] xfs: treat idx as a cursor " Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:35   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 04/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_delay Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:35   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 05/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_hole_real Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:35   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 06/18] xfs: treat idx as a cursor in xfs_bmap_add_extent_unwritten_real Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:36   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 07/18] xfs: treat idx as a cursor in xfs_bmap_del_extent_* Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:37   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 08/18] xfs: treat idx as a cursor in xfs_bmap_collapse_extents Christoph Hellwig
2017-10-31 17:53   ` Brian Foster
2017-10-31 21:37   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 09/18] xfs: allow unaligned extent records in xfs_bmbt_disk_set_all Christoph Hellwig
2017-10-31 21:34   ` Darrick J. Wong
2017-11-02 13:54   ` Brian Foster
2017-10-31 14:22 ` [PATCH 10/18] xfs: iterate over extents in xfs_iextents_copy Christoph Hellwig
2017-10-31 21:41   ` Darrick J. Wong
2017-11-02 13:54   ` Brian Foster
2017-10-31 14:22 ` [PATCH 11/18] xfs: iterate over extents in xfs_bmap_extents_to_btree Christoph Hellwig
2017-10-31 21:41   ` Darrick J. Wong
2017-11-02 13:54   ` Brian Foster
2017-10-31 14:22 ` [PATCH 12/18] xfs: introduce the xfs_iext_cursor abstraction Christoph Hellwig
2017-10-31 22:02   ` Darrick J. Wong
2017-11-02 18:49     ` Christoph Hellwig
2017-11-02 19:01       ` Darrick J. Wong
2017-11-02 19:11         ` Christoph Hellwig
2017-11-02 17:14   ` Brian Foster
2017-11-02 18:51     ` Christoph Hellwig
2017-11-02 19:36       ` Brian Foster
2017-11-03  7:26     ` Christoph Hellwig
2017-10-31 14:22 ` [PATCH 13/18] xfs: iterate backwards in xfs_reflink_cancel_cow_blocks Christoph Hellwig
2017-10-31 22:10   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 14/18] xfs: simplify xfs_reflink_convert_cow Christoph Hellwig
2017-10-31 22:20   ` Darrick J. Wong
2017-11-02 18:56     ` Christoph Hellwig
2017-10-31 14:22 ` [PATCH 15/18] xfs: remove support for inlining data/extents into the inode fork Christoph Hellwig
2017-10-31 22:35   ` Darrick J. Wong
2017-11-02 18:57     ` Christoph Hellwig
2017-11-02 19:26       ` Darrick J. Wong
2017-11-02 21:43         ` Dave Chinner
2017-11-02 22:08           ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 16/18] xfs: use a b+tree for the in-core extent list Christoph Hellwig
2017-11-01 18:47   ` Darrick J. Wong
2017-11-02  0:16     ` Darrick J. Wong
2017-11-02  6:03       ` Christoph Hellwig
2017-11-02  0:14   ` Darrick J. Wong
2017-11-02 19:09     ` Christoph Hellwig
2017-10-31 14:22 ` [PATCH 17/18] xfs: remove the nr_extents argument to xfs_iext_insert Christoph Hellwig
2017-10-31 22:35   ` Darrick J. Wong
2017-10-31 14:22 ` [PATCH 18/18] xfs: remove the nr_extents argument to xfs_iext_remove Christoph Hellwig
2017-10-31 22:37   ` Darrick J. Wong
2017-11-01  3:08 ` b+tree for the incore extent list Darrick J. Wong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.