Skip to contentSkip to conversation

XFS: dynamic busy extent tracking

RFC PATCH In review Dave Chinner 3 participants 14 messages Oct 7, 2008 – Oct 10, 2008 XFS filesystem

lore
Share & tools
lore
#0 lore raw

cover XFS: dynamic busy extent tracking

The busy extent tracking in XFS is currently very static and has some performance issues. We can only track 128 busy extents per AG, and when we overflow this we fall back to synchronous transactions. Also, every time we re-use a busy extent we cause a synchronous log force, which stops all allocation and freeing in that AG while the log force is in progress.

This limits the amount of transaction aggregation we can potentially do because of the fact that we do log forces when a busy extent is found which would effectively close off an aggregating transaction group. It is relatively easy to trigger overflows or reuse of busy extents and cause thіs to occur.

Further, when it comes to issuing block discard messages to tell the lower layer that we're finished with a block, we need to track all freed extents, not just those we can fit in a static array. Hence adding block discard functionality to XFS also requires us to rework the way we track busy extents.

This patch series is a work in progress. It's probably better to look at the finished product rather than the inividual patches because the later patches undo a lot of the earlier factoring and shuffling. I'll need to completely redo the series before final subbbmission. However, I'm posting this now because it passes XFSQA and it seems to be a good time to post it for comments before I start cleaning it up....

#1 lore raw

PATCH 1/7 XFS: rename xfs_get_perag

xfs_get_perag is really getting the perag that an inode belongs to based on it's inode number. Rename it appropriately so we can use xfs_perag_get() to get the perag from a provided ag number. Convert a number of sites over to using this interface.

---
 fs/xfs/linux-2.6/xfs_sync.c |   21 ++++++++++++---------
 fs/xfs/xfs_iget.c           |   14 +++++++-------
 fs/xfs/xfs_inode.c          |    6 +++---
 fs/xfs/xfs_mount.h          |   15 ++++++++++++---
 4 files changed, 34 insertions(+), 22 deletions(-)
fs/xfs/linux-2.6/xfs_sync.c +12 -9
index ee1648b..08b2acf 100644
@@ -57,7 +57,7 @@ xfs_sync_inodes_ag(
 	int		ag,
 	int		flags)
 {
-	xfs_perag_t	*pag = &mp->m_perag[ag];
+	xfs_perag_t	*pag = xfs_perag_get(mp, ag);
 	int		nr_found;
 	uint32_t	first_index = 0;
 	int		error = 0;
@@ -197,10 +197,11 @@ xfs_sync_inodes_ag(
 		 * bail out if the filesystem is corrupted.
 		 */
 		if (error == EFSCORRUPTED)
-			return XFS_ERROR(error);
+			break;
 
 	} while (nr_found);
 
+	xfs_perag_put(pag);
 	return last_error;
 }
 
@@ -598,7 +599,7 @@ xfs_reclaim_inode(
 	int		locked,
 	int		sync_mode)
 {
-	xfs_perag_t	*pag = xfs_get_perag(ip->i_mount, ip->i_ino);
+	xfs_perag_t	*pag = xfs_perag_get_from_ino(ip->i_mount, ip->i_ino);
 
 	/* The hash lock here protects a thread in xfs_iget_core from
 	 * racing with us on linking the inode back with a vnode.
@@ -615,12 +616,13 @@ xfs_reclaim_inode(
 			xfs_ifunlock(ip);
 			xfs_iunlock(ip, XFS_ILOCK_EXCL);
 		}
+		xfs_perag_put(pag);
 		return 1;
 	}
 	__xfs_iflags_set(ip, XFS_IRECLAIM);
 	spin_unlock(&ip->i_flags_lock);
 	write_unlock(&pag->pag_ici_lock);
-	xfs_put_perag(ip->i_mount, pag);
+	xfs_perag_put(pag);
 
 	/*
 	 * If the inode is still dirty, then flush it out.  If the inode
@@ -663,7 +665,7 @@ xfs_inode_set_reclaim_tag(
 	xfs_inode_t	*ip)
 {
 	xfs_mount_t	*mp = ip->i_mount;
-	xfs_perag_t	*pag = xfs_get_perag(mp, ip->i_ino);
+	xfs_perag_t	*pag = xfs_perag_get_from_ino(mp, ip->i_ino);
 
 	read_lock(&pag->pag_ici_lock);
 	spin_lock(&ip->i_flags_lock);
@@ -672,7 +674,7 @@ xfs_inode_set_reclaim_tag(
 	__xfs_iflags_set(ip, XFS_IRECLAIMABLE);
 	spin_unlock(&ip->i_flags_lock);
 	read_unlock(&pag->pag_ici_lock);
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 }
 
 void
@@ -690,14 +692,14 @@ xfs_inode_clear_reclaim_tag(
 	xfs_inode_t	*ip)
 {
 	xfs_mount_t	*mp = ip->i_mount;
-	xfs_perag_t	*pag = xfs_get_perag(mp, ip->i_ino);
+	xfs_perag_t	*pag = xfs_perag_get_from_ino(mp, ip->i_ino);
 
 	read_lock(&pag->pag_ici_lock);
 	spin_lock(&ip->i_flags_lock);
 	__xfs_inode_clear_reclaim_tag(mp, pag, ip);
 	spin_unlock(&ip->i_flags_lock);
 	read_unlock(&pag->pag_ici_lock);
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 }
 
 
@@ -709,7 +711,7 @@ xfs_reclaim_inodes_ag(
 	int		mode)
 {
 	xfs_inode_t	*ip = NULL;
-	xfs_perag_t	*pag = &mp->m_perag[ag];
+	xfs_perag_t	*pag = xfs_perag_get(mp, ag);
 	int		nr_found;
 	uint32_t	first_index;
 	int		skipped;
@@ -779,6 +781,7 @@ restart:
 		delay(1);
 		goto restart;
 	}
+	xfs_perag_put(pag);
 	return;
 
 }
fs/xfs/xfs_iget.c +7 -7
index a1f209b..b34b732 100644
@@ -245,7 +245,7 @@ xfs_iget(
 		return EINVAL;
 
 	/* get the perag structure and ensure that it's inode capable */
-	pag = xfs_get_perag(mp, ino);
+	pag = xfs_perag_get_from_ino(mp, ino);
 	if (!pag->pagi_inodeok)
 		return EINVAL;
 	ASSERT(pag->pag_ici_init);
@@ -269,7 +269,7 @@ again:
 		if (error)
 			goto out_error_or_again;
 	}
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 
 	xfs_iflags_set(ip, XFS_IMODIFIED);
 	*ipp = ip;
@@ -289,7 +289,7 @@ out_error_or_again:
 		delay(1);
 		goto again;
 	}
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 	return error;
 }
 
@@ -307,11 +307,11 @@ xfs_inode_incore(xfs_mount_t	*mp,
 	xfs_inode_t	*ip;
 	xfs_perag_t	*pag;
 
-	pag = xfs_get_perag(mp, ino);
+	pag = xfs_perag_get_from_ino(mp, ino);
 	read_lock(&pag->pag_ici_lock);
 	ip = radix_tree_lookup(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, ino));
 	read_unlock(&pag->pag_ici_lock);
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 
 	/* the returned inode must match the transaction */
 	if (ip && (ip->i_transp != tp))
@@ -410,12 +410,12 @@ xfs_iextract(
 	xfs_inode_t	*ip)
 {
 	xfs_mount_t	*mp = ip->i_mount;
-	xfs_perag_t	*pag = xfs_get_perag(mp, ip->i_ino);
+	xfs_perag_t	*pag = xfs_perag_get_from_ino(mp, ip->i_ino);
 
 	write_lock(&pag->pag_ici_lock);
 	radix_tree_delete(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, ip->i_ino));
 	write_unlock(&pag->pag_ici_lock);
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 
 	mp->m_ireclaims++;
 }
fs/xfs/xfs_inode.c +3 -3
index eba2ff0..17dbf24 100644
@@ -2111,7 +2111,7 @@ xfs_ifree_cluster(
 	xfs_inode_t		*ip, **ip_found;
 	xfs_inode_log_item_t	*iip;
 	xfs_log_item_t		*lip;
-	xfs_perag_t		*pag = xfs_get_perag(mp, inum);
+	xfs_perag_t		*pag = xfs_perag_get_from_ino(mp, inum);
 
 	if (mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) {
 		blks_per_cluster = 1;
@@ -2253,7 +2253,7 @@ xfs_ifree_cluster(
 	}
 
 	kmem_free(ip_found);
-	xfs_put_perag(mp, pag);
+	xfs_perag_put(pag);
 }
 
 /*
@@ -2973,7 +2973,7 @@ xfs_iflush_cluster(
 	xfs_buf_t	*bp)
 {
 	xfs_mount_t		*mp = ip->i_mount;
-	xfs_perag_t		*pag = xfs_get_perag(mp, ip->i_ino);
+	xfs_perag_t		*pag = xfs_perag_get_from_ino(mp, ip->i_ino);
 	unsigned long		first_index, mask;
 	unsigned long		inodes_per_cluster;
 	int			ilist_size;
fs/xfs/xfs_mount.h +12 -3
index f927ada..55cc89f 100644
@@ -455,18 +455,27 @@ xfs_daddr_to_agbno(struct xfs_mount *mp, xfs_daddr_t d)
  * perag get/put wrappers for eventual ref counting
  */
 static inline xfs_perag_t *
-xfs_get_perag(struct xfs_mount *mp, xfs_ino_t ino)
+xfs_perag_get(struct xfs_mount *mp, xfs_agnumber_t agno)
 {
-	return &mp->m_perag[XFS_INO_TO_AGNO(mp, ino)];
+	return &mp->m_perag[agno];
 }
 
 static inline void
-xfs_put_perag(struct xfs_mount *mp, xfs_perag_t *pag)
+xfs_perag_put(xfs_perag_t *pag)
 {
 	/* nothing to see here, move along */
 }
 
 /*
+ * Get the perag associated with the given inode number
+ */
+static inline xfs_perag_t *
+xfs_perag_get_from_ino(struct xfs_mount *mp, xfs_ino_t ino)
+{
+	return xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ino));
+}
+
+/*
  * Per-cpu superblock locking functions
  */
 #ifdef HAVE_PERCPU_SB
Signature · 1 line
1.5.6.5
#2 lore raw

Re: [PATCH 1/7] XFS: rename xfs_get_perag

Quoted 4 lines · Dave Chinner

xfs_get_perag is really getting the perag that an inode belongs to based on it's inode number. Rename it appropriately so we can use xfs_perag_get() to get the perag from a provided ag number. Convert a number of sites over to using this interface.

Looks good.

#3 lore raw

PATCH 2/7 XFS: replace fixed size busy extent array with an rbtree

In reply to Dave Chinner

When we free a metadata extent, we record it in the per-AG busy extent array so that it is not re-used before the freeing transaction hits the disk. This array is fixed size, so when it overflows we make further allocation transactions synchronous because we cannot track more freed extents until those transactions hit the disk and are completed. Under heavy mixed allocation and freeing workloads with large log buffers, we can overflow this array quite easily.

This is important as we want to be able to issue block discard commands when we free extents, and that has to happen after the free transaction has been committed to disk. This is made complex by the way the overflows are handled - we don't record the extent to be freed in the busy list, so we've got no callback to issue the discard request from. Hence we need a different tracking mechanism to be easily able to issue discard blocks.

Further, the array is sparsely populated, which means that inserts need to search for a free slot, and array searches often have to search many more slots that are actually used to check all the busy extents. Quite inefficient, really.

To fix all these problems, make the busy extent tracking dynamic and faster to search by converting it to a per-AG rbtree and indexing it by block number. We always search by block number, so this means that the search and insert scaling becomes O(log n) instead of O(array size). This will also allow us to efficiently track far more busy extents than the current method allows.

---
 fs/xfs/xfs_ag.h         |   20 ++--
 fs/xfs/xfs_alloc.c      |  235 +++++++++++++++++++++++++++--------------------
 fs/xfs/xfs_alloc.h      |    5 +-
 fs/xfs/xfs_mount.c      |    8 +--
 fs/xfs/xfs_trans.c      |    6 +-
 fs/xfs/xfs_trans.h      |   10 +-
 fs/xfs/xfs_trans_item.c |   19 +---
 fs/xfs/xfs_trans_priv.h |    3 -
 8 files changed, 161 insertions(+), 145 deletions(-)
fs/xfs/xfs_ag.h +11 -9
index 2bfd863..e7aaa1c 100644
@@ -156,14 +156,16 @@ typedef struct xfs_agfl {
 } xfs_agfl_t;
 
 /*
- * Busy block/extent entry.  Used in perag to mark blocks that have been freed
- * but whose transactions aren't committed to disk yet.
+ * Busy block/extent entry.  Indexed by a rbtree in perag to mark blocks that
+ * have been freed but whose transactions aren't committed to disk yet.
  */
-typedef struct xfs_perag_busy {
-	xfs_agblock_t	busy_start;
-	xfs_extlen_t	busy_length;
-	struct xfs_trans *busy_tp;	/* transaction that did the free */
-} xfs_perag_busy_t;
+struct xfs_busy_extent {
+	struct rb_node	rb_node;
+	xfs_agnumber_t	agno;
+	xfs_agblock_t	bno;
+	xfs_extlen_t	length;
+	struct xfs_trans *tp;	/* transaction that did the free */
+};
 
 /*
  * Per-ag incore structure, copies of information in agf and agi,
@@ -192,9 +194,9 @@ typedef struct xfs_perag
 	xfs_agino_t	pagi_freecount;	/* number of free inodes */
 	xfs_agino_t	pagi_count;	/* number of allocated inodes */
 	int		pagb_count;	/* pagb slots in use */
-	xfs_perag_busy_t *pagb_list;	/* unstable blocks */
 #ifdef __KERNEL__
-	spinlock_t	pagb_lock;	/* lock for pagb_list */
+	spinlock_t	pagb_lock;	/* lock for pagb_tree */
+	struct rb_root	pagb_tree;	/* ordered tree of busy extents */
 
 	atomic_t        pagf_fstrms;    /* # of filestreams active in this AG */
 
fs/xfs/xfs_alloc.c +134 -101

288 lines not on this page · raw message

fs/xfs/xfs_alloc.h +2 -3
index 5881727..21040af 100644
@@ -22,6 +22,7 @@ struct xfs_buf;
 struct xfs_mount;
 struct xfs_perag;
 struct xfs_trans;
+struct xfs_busy_extent;
 
 /*
  * Freespace allocation types.  Argument to xfs_alloc_[v]extent.
@@ -128,9 +129,7 @@ xfs_alloc_mark_busy(xfs_trans_t *tp,
 		xfs_extlen_t len);
 
 void
-xfs_alloc_clear_busy(xfs_trans_t *tp,
-		xfs_agnumber_t ag,
-		int idx);
+xfs_alloc_clear_busy(xfs_trans_t *tp, struct xfs_busy_extent *busyp);
 
 #endif	/* __KERNEL__ */
 
fs/xfs/xfs_mount.c +1 -7
index 794a4e2..2239685 100644
@@ -131,14 +131,8 @@ STATIC void
 xfs_free_perag(
 	xfs_mount_t	*mp)
 {
-	if (mp->m_perag) {
-		int	agno;
-
-		for (agno = 0; agno < mp->m_maxagi; agno++)
-			if (mp->m_perag[agno].pagb_list)
-				kmem_free(mp->m_perag[agno].pagb_list);
+	if (mp->m_perag)
 		kmem_free(mp->m_perag);
-	}
 }
 
 /*
fs/xfs/xfs_trans.c +2 -4
index ad137ef..e88b9bf 100644
@@ -1338,10 +1338,8 @@ xfs_trans_committed(
 	lbcp = &tp->t_busy;
 	while (lbcp != NULL) {
 		for (i = 0, lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) {
-			if (!XFS_LBC_ISFREE(lbcp, i)) {
-				xfs_alloc_clear_busy(tp, lbsp->lbc_ag,
-						     lbsp->lbc_idx);
-			}
+			if (!XFS_LBC_ISFREE(lbcp, i))
+				xfs_alloc_clear_busy(tp, lbsp->lbc_busyp);
 		}
 		lbcp = lbcp->lbc_next;
 	}
fs/xfs/xfs_trans.h +5 -5
index d6fe4a8..6a5e8d5 100644
@@ -762,6 +762,7 @@ struct xfs_log_item_desc;
 struct xfs_mount;
 struct xfs_trans;
 struct xfs_dquot_acct;
+struct xfs_busy_extent;
 
 typedef struct xfs_log_item {
 	struct list_head		li_ail;		/* AIL pointers */
@@ -824,8 +825,7 @@ typedef struct xfs_item_ops {
  */
 
 typedef struct xfs_log_busy_slot {
-	xfs_agnumber_t		lbc_ag;
-	ushort			lbc_idx;	/* index in perag.busy[] */
+	struct xfs_busy_extent	*lbc_busyp;
 } xfs_log_busy_slot_t;
 
 #define XFS_LBC_NUM_SLOTS	31
@@ -971,9 +971,9 @@ int		_xfs_trans_commit(xfs_trans_t *,
 void		xfs_trans_cancel(xfs_trans_t *, int);
 int		xfs_trans_ail_init(struct xfs_mount *);
 void		xfs_trans_ail_destroy(struct xfs_mount *);
-xfs_log_busy_slot_t *xfs_trans_add_busy(xfs_trans_t *tp,
-					xfs_agnumber_t ag,
-					xfs_extlen_t idx);
+
+xfs_log_busy_slot_t *xfs_trans_add_busy(struct xfs_trans *tp,
+					struct xfs_busy_extent *busyp);
 
 extern kmem_zone_t	*xfs_trans_zone;
 
fs/xfs/xfs_trans_item.c +6 -13
index e110bf5..3baa0af 100644
@@ -449,7 +449,9 @@ xfs_trans_unlock_chunk(
  * descriptor with its ???? field.
  */
 xfs_log_busy_slot_t *
-xfs_trans_add_busy(xfs_trans_t *tp, xfs_agnumber_t ag, xfs_extlen_t idx)
+xfs_trans_add_busy(
+	xfs_trans_t		*tp,
+	struct xfs_busy_extent	*busyp)
 {
 	xfs_log_busy_chunk_t	*lbcp;
 	xfs_log_busy_slot_t	*lbsp;
@@ -479,16 +481,8 @@ xfs_trans_add_busy(xfs_trans_t *tp, xfs_agnumber_t ag, xfs_extlen_t idx)
 		tp->t_busy.lbc_next = lbcp;
 		tp->t_busy_free = XFS_LIC_NUM_SLOTS - 1;
 
-		/*
-		 * Initialize the descriptor and the generic portion
-		 * of the log item.
-		 *
-		 * Point the new slot at this item and return it.
-		 * Also point the log item at its currently active
-		 * descriptor and set the item's mount pointer.
-		 */
-		lbsp->lbc_ag = ag;
-		lbsp->lbc_idx = idx;
+		/* Initialize the descriptor and return it */
+		lbsp->lbc_busyp = busyp;
 		return lbsp;
 	}
 
@@ -521,8 +515,7 @@ xfs_trans_add_busy(xfs_trans_t *tp, xfs_agnumber_t ag, xfs_extlen_t idx)
 	}
 	lbsp = XFS_LBC_SLOT(lbcp, i);
 	tp->t_busy_free--;
-	lbsp->lbc_ag = ag;
-	lbsp->lbc_idx = idx;
+	lbsp->lbc_busyp = busyp;
 	return lbsp;
 }
 
fs/xfs/xfs_trans_priv.h +0 -3
index 73e2ad3..fd50556 100644
@@ -39,9 +39,6 @@ void				xfs_trans_free_items(struct xfs_trans *, int);
 void				xfs_trans_unlock_items(struct xfs_trans *,
 							xfs_lsn_t);
 void				xfs_trans_free_busy(xfs_trans_t *tp);
-xfs_log_busy_slot_t		*xfs_trans_add_busy(xfs_trans_t *tp,
-						    xfs_agnumber_t ag,
-						    xfs_extlen_t idx);
 
 /*
  * AIL traversal cursor.
Signature · 1 line
1.5.6.5
#4 lore raw

Re: [PATCH 2/7] XFS: replace fixed size busy extent array with an rbtree

fs/xfs/xfs_mount.c STATIC void · line 141

Quoted 13 lines
fs/xfs/xfs_mount.c +1 -7
@@ -131,14 +131,8 @@ STATIC void
 xfs_free_perag(
 	xfs_mount_t	*mp)
 {
-	if (mp->m_perag) {
-		int	agno;
-
-		for (agno = 0; agno < mp->m_maxagi; agno++)
-			if (mp->m_perag[agno].pagb_list)
-				kmem_free(mp->m_perag[agno].pagb_list);
+	if (mp->m_perag)
 		kmem_free(mp->m_perag);
-	}

kmem_free(NULL) is fine, so no need for the if. And with that there's no need for this one-line wrapper and we can just do the free in the caller.

fs/xfs/xfs_trans.h typedef struct xfs_item_ops { · line 829

Quoted 5 lines
fs/xfs/xfs_trans.h +1 -2
 typedef struct xfs_log_busy_slot {
-	xfs_agnumber_t		lbc_ag;
-	ushort			lbc_idx;	/* index in perag.busy[] */
+	struct xfs_busy_extent	*lbc_busyp;
 } xfs_log_busy_slot_t;

Just use xfs_busy_extent directly - there's only about a handful places using xfs_log_busy_slot anyway.

fs/xfs/xfs_trans_item.c xfs_trans_unlock_chunk( · line 454

Quoted 5 lines

xfs_log_busy_slot_t *

fs/xfs/xfs_trans_item.c +3 -1
-xfs_trans_add_busy(xfs_trans_t *tp, xfs_agnumber_t ag, xfs_extlen_t idx)
+xfs_trans_add_busy(
+	xfs_trans_t		*tp,
+	struct xfs_busy_extent	*busyp)

And this one can lose it's return value. It's always the second argmument and ignored by all callers anyway.

#5 lore raw

Re: [PATCH 2/7] XFS: replace fixed size busy extent array with an rbtree

Quoted 9 lines · Christoph Hellwig
Quoted 9 lines
fs/xfs/xfs_mount.c +0 -5
@@ -131,14 +131,8 @@ STATIC void
 xfs_free_perag(
 	xfs_mount_t	*mp)
 {
-	if (mp->m_perag) {
-		int	agno;
-
-		for (agno = 0; agno < mp->m_maxagi; agno++)
-			if (mp->m_perag[agno].pagb_list)
Quoted 8 lines

fs/xfs/xfs_mount.c STATIC void · line 141

Quoted 4 lines
fs/xfs/xfs_mount.c +1 -2
-				kmem_free(mp->m_perag[agno].pagb_list);
+	if (mp->m_perag)
 		kmem_free(mp->m_perag);
-	}

kmem_free(NULL) is fine, so no need for the if. And with that there's no need for this one-line wrapper and we can just do the free in the caller.

Ok, I'll do that.

Quoted 9 lines

fs/xfs/xfs_trans.h typedef struct xfs_item_ops { · line 829

Quoted 5 lines
fs/xfs/xfs_trans.h +1 -2
 typedef struct xfs_log_busy_slot {
-	xfs_agnumber_t		lbc_ag;
-	ushort			lbc_idx;	/* index in perag.busy[] */
+	struct xfs_busy_extent	*lbc_busyp;
 } xfs_log_busy_slot_t;

Just use xfs_busy_extent directly - there's only about a handful places using xfs_log_busy_slot anyway.

Quoted 8 lines

fs/xfs/xfs_trans_item.c xfs_trans_unlock_chunk( · line 454

Quoted 5 lines

xfs_log_busy_slot_t *

fs/xfs/xfs_trans_item.c +3 -1
-xfs_trans_add_busy(xfs_trans_t *tp, xfs_agnumber_t ag, xfs_extlen_t idx)
+xfs_trans_add_busy(
+	xfs_trans_t		*tp,
+	struct xfs_busy_extent	*busyp)

And this one can lose it's return value. It's always the second argmument and ignored by all callers anyway.

I'm not concerned about this as the busy slot stuff in the struct xfs_trans gets removed in the last patch of the series. I'm half-tempted to integrate this one with the initial rbtree patch as all that intermediate stuffing around just goes away.

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#6 lore raw

PATCH 3/7 XFS: Don't immediately reallocate busy extents

In reply to Dave Chinner

Every time we reallocate a busy extent, we cause a synchronous log force to occur to ensure the freeing transaction is on disk before we continue and use the newly allocated extent. This is extremely sub-optimal as it means we are holding the AGF locked over a blocking log force that could take several hundred milliseconds to complete. This prevents us from making progress and other allocations and freeing from ocurring in that AG for that period of time.

Instead of searching the busy extent list after deciding on the extent to allocate, check each candidate extent during the allocation decisions as to whether they are in the busy list. If they are in the busy list, we don't consider that extent a good choice to allocate. This will have relatively low overhead because the rbtree is indexed by block number so searches should find a busy extent very quickly.

---
 fs/xfs/xfs_alloc.c |  779 +++++++++++++++++++++++++---------------------------
 1 files changed, 380 insertions(+), 399 deletions(-)
fs/xfs/xfs_alloc.c +380 -399
index 98908ba..77dc18e 100644
@@ -45,12 +45,6 @@
 #define	XFSA_FIXUP_BNO_OK	1
 #define	XFSA_FIXUP_CNT_OK	2
 
-STATIC void
-xfs_alloc_search_busy(xfs_trans_t *tp,
-		    xfs_agnumber_t agno,
-		    xfs_agblock_t bno,
-		    xfs_extlen_t len);
-
 #if defined(XFS_ALLOC_TRACE)
 ktrace_t *xfs_alloc_trace_buf;
 
@@ -85,6 +79,178 @@ STATIC int xfs_alloc_ag_vextent_size(xfs_alloc_arg_t *);
 STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *,
 	xfs_btree_cur_t *, xfs_agblock_t *, xfs_extlen_t *, int *);
 
+
+
+/*
+ * AG Busy list management
+ * The busy list contains block ranges that have been freed but whose
+ * transactions have not yet hit disk.  If any block listed in a busy
+ * list is reused, the transaction that freed it must be forced to disk
+ * before continuing to use the block.
+ *
+ * xfs_alloc_mark_busy - add to the per-ag busy list
+ * xfs_alloc_clear_busy - remove an item from the per-ag busy list
+ */
+static void
+xfs_alloc_busy_insert(
+	struct xfs_perag	*pag,
+	xfs_agblock_t		bno,
+	struct rb_node		*node)
+{
+	struct rb_node		**rbp = &pag->pagb_tree.rb_node;
+	struct rb_node		*parent = NULL;
+	struct xfs_busy_extent	*busyp;
+
+	while (*rbp) {
+		parent = *rbp;
+		busyp = rb_entry(parent, struct xfs_busy_extent, rb_node);
+
+		if (bno < busyp->bno)
+			rbp = &(*rbp)->rb_left;
+		else if (bno > busyp->bno)
+			rbp = &(*rbp)->rb_right;
+		else
+			BUG();
+	}
+
+	rb_link_node(node, parent, rbp);
+	rb_insert_color(node, &pag->pagb_tree);
+}
+
+void
+xfs_alloc_mark_busy(
+	struct xfs_trans	*tp,
+	xfs_agnumber_t		agno,
+	xfs_agblock_t		bno,
+	xfs_extlen_t		len)
+{
+	struct xfs_busy_extent	*busyp;
+	struct xfs_perag	*pag;
+
+	busyp = kmem_zalloc(sizeof(struct xfs_busy_extent), KM_MAYFAIL);
+	if (!busyp) {
+		/*
+		 * No Memory!  Since it is now not possible to track the free
+		 * block, make this a synchronous transaction to insure that
+		 * the block is not reused before this transaction commits.
+		 */
+		TRACE_BUSY("xfs_alloc_mark_busy", "ENOMEM", agno, bno, len, tp);
+		xfs_trans_set_sync(tp);
+		return;
+	}
+
+	busyp->agno = agno;
+	busyp->bno = bno;
+	busyp->length = len;
+	busyp->tp = tp;
+
+	pag = xfs_perag_get(tp->t_mountp, agno);
+	spin_lock(&pag->pagb_lock);
+	xfs_alloc_busy_insert(pag, bno, &busyp->rb_node);
+	xfs_trans_add_busy(tp, busyp);
+	TRACE_BUSY("xfs_alloc_mark_busy", "got", agno, bno, len, tp);
+	spin_unlock(&pag->pagb_lock);
+	xfs_perag_put(pag);
+}
+
+/*
+ * Search for a busy extent within the range of the extent we are about to
+ * allocate.  You need to be holding the busy extent tree lock when calling
+ * __xfs_alloc_busy_search().
+ */
+static struct xfs_busy_extent *
+__xfs_alloc_busy_search(
+	struct xfs_trans	*tp,
+	xfs_agnumber_t		agno,
+	xfs_agblock_t		bno,
+	xfs_extlen_t		len)
+{
+	struct xfs_perag	*pag;
+	struct rb_node		*rbp;
+	xfs_agblock_t		uend, bend;
+
+	uend = bno + len - 1;
+	pag = xfs_perag_get(tp->t_mountp, agno);
+	rbp = pag->pagb_tree.rb_node;
+	while (rbp) {
+		struct xfs_busy_extent	*busyp;
+
+		busyp = rb_entry(rbp, struct xfs_busy_extent, rb_node);
+		bend = busyp->bno + busyp->length - 1;
+		if (uend < busyp->bno)
+			rbp = rbp->rb_left;
+		else if (bno > bend)
+			rbp = rbp->rb_right;
+		else {
+			/* (start1,length1) within (start2, length2) */
+			TRACE_BUSYSEARCH("xfs_alloc_search_busy",
+					 "found1", agno, bno, len, tp);
+			xfs_perag_put(pag);
+			return busyp;
+		}
+	}
+	xfs_perag_put(pag);
+	return NULL;
+}
+
+void
+xfs_alloc_clear_busy(
+	struct xfs_trans	*tp,
+	struct xfs_busy_extent	*busyp)
+{
+	struct xfs_perag	*pag;
+
+	pag = xfs_perag_get(tp->t_mountp, busyp->agno);
+	spin_lock(&pag->pagb_lock);
+
+	/* check that the busyp is still in the rbtree */
+	ASSERT(__xfs_alloc_busy_search(tp, busyp->agno, busyp->bno,
+						busyp->length) == busyp);
+
+	TRACE_UNBUSY("xfs_alloc_clear_busy", "found", busyp->agno, busyp->bno,
+							busyp->length, tp);
+	rb_erase(&busyp->rb_node, &pag->pagb_tree);
+	spin_unlock(&pag->pagb_lock);
+	xfs_perag_put(pag);
+
+	kmem_free(busyp);
+}
+
+/*
+ * If we find the extent in the busy list, force the log out to get the
+ * extent out of the busy list so the caller can use it straight away.
+ */
+STATIC void
+xfs_alloc_search_busy(
+	struct xfs_trans	*tp,
+	xfs_agnumber_t		agno,
+	xfs_agblock_t		bno,
+	xfs_extlen_t		len)
+{
+	struct xfs_perag	*pag;
+	struct xfs_busy_extent	*busyp;
+	xfs_lsn_t		lsn;
+
+	pag = xfs_perag_get(tp->t_mountp, agno);
+	spin_lock(&pag->pagb_lock);
+	busyp = __xfs_alloc_busy_search(tp, agno, bno, len);
+	if (!busyp) {
+		TRACE_BUSYSEARCH("xfs_alloc_search_busy", "not-found", agno, bno, len, tp);
+		spin_unlock(&pag->pagb_lock);
+		xfs_perag_put(pag);
+		return;
+	}
+	/*
+	 * A block was found, force the log through the LSN of the
+	 * transaction that freed the block
+	 */
+	TRACE_BUSYSEARCH("xfs_alloc_search_busy", "found", agno, bno, len, tp);
+	lsn = busyp->tp->t_commit_lsn;
+	spin_unlock(&pag->pagb_lock);
+	xfs_log_force(tp->t_mountp, lsn, XFS_LOG_FORCE|XFS_LOG_SYNC);
+	xfs_perag_put(pag);
+}
+
 /*
  * Internal functions.
  */
@@ -705,6 +871,12 @@ xfs_alloc_ag_vextent(
  * Extent's length (returned in *len) will be between minlen and maxlen,
  * and of the form k * prod + mod unless there's nothing that large.
  * Return the starting a.g. block (bno), or NULLAGBLOCK if we can't do it.
+ *
+ * Avoid allocating extents that span a busy extent range to avoid causing a
+ * synchronous log force to be required during the transaction and hence avoid
+ * holding the AGF locked for an excessive period of time. If we are doing low
+ * space allocation, speed is a secondary factor so in that case we will use
+ * busy extents and take the hit....
  */
 STATIC int			/* error */
 xfs_alloc_ag_vextent_exact(
@@ -735,46 +907,36 @@ xfs_alloc_ag_vextent_exact(
 	 */
 	if ((error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i)))
 		goto error0;
-	if (!i) {
-		/*
-		 * Didn't find it, return null.
-		 */
-		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
-		args->agbno = NULLAGBLOCK;
-		return 0;
-	}
-	/*
-	 * Grab the freespace record.
-	 */
-	if ((error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i)))
+	if (!i)
+		goto not_found;
+
+	/* Grab a non-busy freespace record. */
+	error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i);
+	if (error)
 		goto error0;
+	if (__xfs_alloc_busy_search(args->tp, args->agno, fbno, flen))
+		goto not_found;
 	XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
 	ASSERT(fbno <= args->agbno);
+
+	/* Give up if the freespace isn't long enough for the minimum request. */
 	minend = args->agbno + args->minlen;
 	maxend = args->agbno + args->maxlen;
 	fend = fbno + flen;
-	/*
-	 * Give up if the freespace isn't long enough for the minimum request.
-	 */
-	if (fend < minend) {
-		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
-		args->agbno = NULLAGBLOCK;
-		return 0;
-	}
+	if (fend < minend)
+		goto not_found;
 	/*
 	 * End of extent will be smaller of the freespace end and the
 	 * maximal requested end.
 	 */
 	end = XFS_AGBLOCK_MIN(fend, maxend);
-	/*
-	 * Fix the length according to mod and prod if given.
-	 */
+
+	/* Fix the length according to mod and prod if given. */
 	args->len = end - args->agbno;
 	xfs_alloc_fix_len(args);
-	if (!xfs_alloc_fix_minleft(args)) {
-		xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
-		return 0;
-	}
+	if (!xfs_alloc_fix_minleft(args))
+		goto not_found;
+
 	rlen = args->len;
 	ASSERT(args->agbno + rlen <= fend);
 	end = args->agbno + rlen;
@@ -797,6 +959,13 @@ xfs_alloc_ag_vextent_exact(
 	args->wasfromfl = 0;
 	return 0;
 
+not_found:
+	/* Didn't find it, return null. */
+	xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+	args->agbno = NULLAGBLOCK;
+	TRACE_ALLOC("not found", args);
+	return 0;
+
 error0:
 	xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR);
 	TRACE_ALLOC("error", args);
@@ -804,10 +973,104 @@ error0:
 }
 
 /*
+ * Search the btree in a given direction via the search cursor
+ * and compare the records found against the good extent we've
+ * already found.
+ */
+STATIC int
+xfs_alloc_find_best_extent(
+	xfs_alloc_arg_t	*args,
+	xfs_btree_cur_t	**gcur,		/* good cursor */
+	xfs_btree_cur_t	**scur,		/* searching cursor */
+	xfs_agblock_t	gdiff,		/* difference for search comparison */
+	xfs_agblock_t	*sbno,		/* extent found by search */
+	xfs_extlen_t	*slen,
+	xfs_extlen_t	*slena,		/* aligned length */
+	int		dir)	/* 0 = search right, 1 = search left */
+{
+	xfs_agblock_t	bno;
+	xfs_agblock_t	new;
+	xfs_agblock_t	sdiff;
+	int		i;
+	int		error;
+
+	/* The good extent is perfect, no need to  search. */
+	if (!gdiff)
+		goto out_use_good;
+
+	/*
+	 * Look until we find a better one, run out of
+	 * space, or run off the end.
+	 */
+	do {
+		error = xfs_alloc_get_rec(*scur, sbno, slen, &i);
+		if (error)
+			goto error0;
+		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+		xfs_alloc_compute_aligned(*sbno, *slen, args->alignment,
+					args->minlen, &bno, slena);
+		if (__xfs_alloc_busy_search(args->tp, args->agno, bno, *slena)) {
+			/* just freed - skip this one */
+			goto next_record;
+		}
+
+		/* The good extent is closer than this one */
+		if (!dir) {
+			if (bno >= args->agbno + gdiff)
+				goto out_use_good;
+		} else {
+			if (bno <= args->agbno - gdiff)
+				goto out_use_good;
+		}
+
+		/* Same distance, compare length and pick the best. */
+		if (*slena >= args->minlen) {
+			args->len = XFS_EXTLEN_MIN(*slena, args->maxlen);
+			xfs_alloc_fix_len(args);
+			sdiff = xfs_alloc_compute_diff(args->agbno, args->len,
+					args->alignment, *sbno, *slen, &new);
+
+			/* choose closer size and invalidate other cursor */
+			if (sdiff < gdiff)
+				goto out_use_search;
+			goto out_use_good;
+		}
+next_record:
+		if (!dir)
+			error = xfs_btree_increment(*scur, 0, &i);
+		else
+			error = xfs_btree_decrement(*scur, 0, &i);
+		if (error)
+			goto error0;
+	} while (i);
+
+out_use_good:
+	xfs_btree_del_cursor(*scur, XFS_BTREE_NOERROR);
+	*scur = NULL;
+	return 0;
+
+out_use_search:
+	xfs_btree_del_cursor(*gcur, XFS_BTREE_NOERROR);
+	*gcur = NULL;
+	return 0;
+
+error0:
+	/* caller invalidates cursors */
+	return error;
+
+}
+
+/*
  * Allocate a variable extent near bno in the allocation group agno.
  * Extent's length (returned in len) will be between minlen and maxlen,
  * and of the form k * prod + mod unless there's nothing that large.
  * Return the starting a.g. block, or NULLAGBLOCK if we can't do it.
+ *
+ * Avoid allocating extents that span a busy extent range to avoid causing a
+ * synchronous log force to be required during the transaction and hence avoid
+ * holding the AGF locked for an excessive period of time. If we are doing low
+ * space allocation, speed is a secondary factor so in that case we will use
+ * busy extents and take the hit....
  */
 STATIC int				/* error */
 xfs_alloc_ag_vextent_near(
@@ -926,6 +1189,8 @@ xfs_alloc_ag_vextent_near(
 					args->minlen, &ltbnoa, &ltlena);
 			if (ltlena < args->minlen)
 				continue;
+			if (__xfs_alloc_busy_search(args->tp, args->agno, ltbnoa, ltlena))
+				continue;
 			args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
 			xfs_alloc_fix_len(args);
 			ASSERT(args->len >= args->minlen);
@@ -1045,8 +1310,15 @@ xfs_alloc_ag_vextent_near(
 			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
 			xfs_alloc_compute_aligned(ltbno, ltlen, args->alignment,
 					args->minlen, &ltbnoa, &ltlena);
-			if (ltlena >= args->minlen)
+			if (ltlena >= args->minlen &&
+			    !__xfs_alloc_busy_search(args->tp, args->agno, ltbnoa, ltlena))
 				break;
+			/*
+			 * clear the length so we don't accidentally use this
+			 * extent after we decrement the cursor then find a
+			 * match from the other cursor.
+			 */
+			ltlena = 0;
 			if ((error = xfs_btree_decrement(bno_cur_lt, 0, &i)))
 				goto error0;
 			if (!i) {
@@ -1061,8 +1333,10 @@ xfs_alloc_ag_vextent_near(
 			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
 			xfs_alloc_compute_aligned(gtbno, gtlen, args->alignment,
 					args->minlen, &gtbnoa, &gtlena);
-			if (gtlena >= args->minlen)
+			if (gtlena >= args->minlen &&
+			    !__xfs_alloc_busy_search(args->tp, args->agno, gtbnoa, gtlena))
 				break;
+			gtlena = 0;
 			if ((error = xfs_btree_increment(bno_cur_gt, 0, &i)))
 				goto error0;
 			if (!i) {
@@ -1072,201 +1346,39 @@ xfs_alloc_ag_vextent_near(
 			}
 		}
 	} while (bno_cur_lt || bno_cur_gt);
-	/*
-	 * Got both cursors still active, need to find better entry.
-	 */
+
+	/* Got both cursors still active, need to find better entry. */
 	if (bno_cur_lt && bno_cur_gt) {
-		/*
-		 * Left side is long enough, look for a right side entry.
-		 */
 		if (ltlena >= args->minlen) {
-			/*
-			 * Fix up the length.
-			 */
+			/* Left side is good, look for a right side entry. */
 			args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
 			xfs_alloc_fix_len(args);
 			rlen = args->len;
 			ltdiff = xfs_alloc_compute_diff(args->agbno, rlen,
 				args->alignment, ltbno, ltlen, &ltnew);
-			/*
-			 * Not perfect.
-			 */
-			if (ltdiff) {
-				/*
-				 * Look until we find a better one, run out of
-				 * space, or run off the end.
-				 */
-				while (bno_cur_lt && bno_cur_gt) {
-					if ((error = xfs_alloc_get_rec(
-							bno_cur_gt, &gtbno,
-							&gtlen, &i)))
-						goto error0;
-					XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
-					xfs_alloc_compute_aligned(gtbno, gtlen,
-						args->alignment, args->minlen,
-						&gtbnoa, &gtlena);
-					/*
-					 * The left one is clearly better.
-					 */
-					if (gtbnoa >= args->agbno + ltdiff) {
-						xfs_btree_del_cursor(
-							bno_cur_gt,
-							XFS_BTREE_NOERROR);
-						bno_cur_gt = NULL;
-						break;
-					}
-					/*
-					 * If we reach a big enough entry,
-					 * compare the two and pick the best.
-					 */
-					if (gtlena >= args->minlen) {
-						args->len =
-							XFS_EXTLEN_MIN(gtlena,
-								args->maxlen);
-						xfs_alloc_fix_len(args);
-						rlen = args->len;
-						gtdiff = xfs_alloc_compute_diff(
-							args->agbno, rlen,
-							args->alignment,
-							gtbno, gtlen, &gtnew);
-						/*
-						 * Right side is better.
-						 */
-						if (gtdiff < ltdiff) {
-							xfs_btree_del_cursor(
-								bno_cur_lt,
-								XFS_BTREE_NOERROR);
-							bno_cur_lt = NULL;
-						}
-						/*
-						 * Left side is better.
-						 */
-						else {
-							xfs_btree_del_cursor(
-								bno_cur_gt,
-								XFS_BTREE_NOERROR);
-							bno_cur_gt = NULL;
-						}
-						break;
-					}
-					/*
-					 * Fell off the right end.
-					 */
-					if ((error = xfs_btree_increment(
-							bno_cur_gt, 0, &i)))
-						goto error0;
-					if (!i) {
-						xfs_btree_del_cursor(
-							bno_cur_gt,
-							XFS_BTREE_NOERROR);
-						bno_cur_gt = NULL;
-						break;
-					}
-				}
-			}
-			/*
-			 * The left side is perfect, trash the right side.
-			 */
-			else {
-				xfs_btree_del_cursor(bno_cur_gt,
-						     XFS_BTREE_NOERROR);
-				bno_cur_gt = NULL;
-			}
-		}
-		/*
-		 * It's the right side that was found first, look left.
-		 */
-		else {
-			/*
-			 * Fix up the length.
-			 */
+
+			error = xfs_alloc_find_best_extent(args,
+						&bno_cur_lt, &bno_cur_gt,
+						ltdiff, &gtbno, &gtlen, &gtlena,
+						0 /* search right */);
+			if (error)
+				goto error0;
+		} else {
+			ASSERT(gtlena >= args->minlen);
+
+			/* Right side is good, look for a left side entry. */
 			args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);
 			xfs_alloc_fix_len(args);
 			rlen = args->len;
 			gtdiff = xfs_alloc_compute_diff(args->agbno, rlen,
 				args->alignment, gtbno, gtlen, &gtnew);
-			/*
-			 * Right side entry isn't perfect.
-			 */
-			if (gtdiff) {
-				/*
-				 * Look until we find a better one, run out of
-				 * space, or run off the end.
-				 */
-				while (bno_cur_lt && bno_cur_gt) {
-					if ((error = xfs_alloc_get_rec(
-							bno_cur_lt, &ltbno,
-							&ltlen, &i)))
-						goto error0;
-					XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
-					xfs_alloc_compute_aligned(ltbno, ltlen,
-						args->alignment, args->minlen,
-						&ltbnoa, &ltlena);
-					/*
-					 * The right one is clearly better.
-					 */
-					if (ltbnoa <= args->agbno - gtdiff) {
-						xfs_btree_del_cursor(
-							bno_cur_lt,
-							XFS_BTREE_NOERROR);
-						bno_cur_lt = NULL;
-						break;
-					}
-					/*
-					 * If we reach a big enough entry,
-					 * compare the two and pick the best.
-					 */
-					if (ltlena >= args->minlen) {
-						args->len = XFS_EXTLEN_MIN(
-							ltlena, args->maxlen);
-						xfs_alloc_fix_len(args);
-						rlen = args->len;
-						ltdiff = xfs_alloc_compute_diff(
-							args->agbno, rlen,
-							args->alignment,
-							ltbno, ltlen, &ltnew);
-						/*
-						 * Left side is better.
-						 */
-						if (ltdiff < gtdiff) {
-							xfs_btree_del_cursor(
-								bno_cur_gt,
-								XFS_BTREE_NOERROR);
-							bno_cur_gt = NULL;
-						}
-						/*
-						 * Right side is better.
-						 */
-						else {
-							xfs_btree_del_cursor(
-								bno_cur_lt,
-								XFS_BTREE_NOERROR);
-							bno_cur_lt = NULL;
-						}
-						break;
-					}
-					/*
-					 * Fell off the left end.
-					 */
-					if ((error = xfs_btree_decrement(
-							bno_cur_lt, 0, &i)))
-						goto error0;
-					if (!i) {
-						xfs_btree_del_cursor(bno_cur_lt,
-							XFS_BTREE_NOERROR);
-						bno_cur_lt = NULL;
-						break;
-					}
-				}
-			}
-			/*
-			 * The right side is perfect, trash the left side.
-			 */
-			else {
-				xfs_btree_del_cursor(bno_cur_lt,
-					XFS_BTREE_NOERROR);
-				bno_cur_lt = NULL;
-			}
+
+			error = xfs_alloc_find_best_extent(args,
+						&bno_cur_gt, &bno_cur_lt,
+						gtdiff, &ltbno, &ltlen, &ltlena,
+						1 /* search left */);
+			if (error)
+				goto error0;
 		}
 	}
 	/*
@@ -1287,7 +1399,7 @@ xfs_alloc_ag_vextent_near(
 		bno_cur_lt = bno_cur_gt;
 		bno_cur_gt = NULL;
 		ltbno = gtbno;
-		ltbnoa = gtbnoa;
+		//ltbnoa = gtbnoa;
 		ltlen = gtlen;
 		ltlena = gtlena;
 		j = 1;
@@ -1332,10 +1444,16 @@ xfs_alloc_ag_vextent_near(
 }
 
 /*
- * Allocate a variable extent anywhere in the allocation group agno.
- * Extent's length (returned in len) will be between minlen and maxlen,
- * and of the form k * prod + mod unless there's nothing that large.
- * Return the starting a.g. block, or NULLAGBLOCK if we can't do it.
+ * Allocate a variable extent anywhere in the allocation group agno.  Extent's
+ * length (returned in len) will be between minlen and maxlen, and of the form
+ * k * prod + mod unless there's nothing that large.  Return the starting a.g.
+ * block, or NULLAGBLOCK if we can't do it.
+ *
+ * Avoid allocating extents that span a busy extent range to avoid causing a
+ * synchronous log force to be required during the transaction and hence avoid
+ * holding the AGF locked for an excessive period of time. If we are doing low
+ * space allocation, speed is a secondary factor so in that case we will use
+ * busy extents and take the hit....
  */
 STATIC int				/* error */
 xfs_alloc_ag_vextent_size(
@@ -1349,10 +1467,12 @@ xfs_alloc_ag_vextent_size(
 	int		i;		/* temp status variable */
 	xfs_agblock_t	rbno;		/* returned block number */
 	xfs_extlen_t	rlen;		/* length of returned extent */
+	int		check_busy = 1;
 
 	/*
 	 * Allocate and initialize a cursor for the by-size btree.
 	 */
+restart:
 	cnt_cur = xfs_allocbt_init_cursor(args->mp, args->tp, args->agbp,
 		args->agno, XFS_BTNUM_CNT);
 	bno_cur = NULL;
@@ -1381,9 +1501,31 @@ xfs_alloc_ag_vextent_size(
 	 * There's a freespace as big as maxlen+alignment-1, get it.
 	 */
 	else {
-		if ((error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen, &i)))
-			goto error0;
-		XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+		/*
+		 * Search for a non-busy extent that is large enough.
+		 * If we are at low space, don't check, or if we fall off
+		 * the end of the btree, turn off the busy check and
+		 * restart.
+		 */
+		for (;;) {
+			error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen, &i);
+			if (error)
+				goto error0;
+			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+			if (!check_busy ||
+			    !__xfs_alloc_busy_search(args->tp, args->agno, fbno, flen))
+				break;
+			error = xfs_btree_increment(cnt_cur, 0, &i);
+			if (error)
+				goto error0;
+			if (i == 0) {
+				ASSERT(check_busy);
+				check_busy = 0;
+				xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+				TRACE_ALLOC("busy restart", args);
+				goto restart;
+			}
+		}
 	}
 	/*
 	 * In the first case above, we got the last entry in the
@@ -1407,16 +1549,25 @@ xfs_alloc_ag_vextent_size(
 		bestflen = flen;
 		bestfbno = fbno;
 		for (;;) {
-			if ((error = xfs_btree_decrement(cnt_cur, 0, &i)))
+			error = xfs_btree_decrement(cnt_cur, 0, &i);
+			if (error)
 				goto error0;
 			if (i == 0)
 				break;
-			if ((error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen,
-					&i)))
+			error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen, &i);
+			if (error)
 				goto error0;
 			XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
 			if (flen < bestrlen)
 				break;
+
+			/*
+			 * avoid busy candidate extents as they are clearly not
+			 * a better choice than other extents due to the log
+			 * force penalty of using them.
+			 */
+			if (__xfs_alloc_busy_search(args->tp, args->agno, fbno, flen))
+				continue;
 			xfs_alloc_compute_aligned(fbno, flen, args->alignment,
 				args->minlen, &rbno, &rlen);
 			rlen = XFS_EXTLEN_MIN(args->maxlen, rlen);
@@ -2566,173 +2717,3 @@ error0:
 	return error;
 }
 
-
-/*
- * AG Busy list management
- * The busy list contains block ranges that have been freed but whose
- * transactions have not yet hit disk.  If any block listed in a busy
- * list is reused, the transaction that freed it must be forced to disk
- * before continuing to use the block.
- *
- * xfs_alloc_mark_busy - add to the per-ag busy list
- * xfs_alloc_clear_busy - remove an item from the per-ag busy list
- */
-static void
-xfs_alloc_busy_insert(
-	struct xfs_perag	*pag,
-	xfs_agblock_t		bno,
-	struct rb_node		*node)
-{
-	struct rb_node		**rbp = &pag->pagb_tree.rb_node;
-	struct rb_node		*parent = NULL;
-	struct xfs_busy_extent	*busyp;
-
-	while (*rbp) {
-		parent = *rbp;
-		busyp = rb_entry(parent, struct xfs_busy_extent, rb_node);
-
-		if (bno < busyp->bno)
-			rbp = &(*rbp)->rb_left;
-		else if (bno > busyp->bno)
-			rbp = &(*rbp)->rb_right;
-		else
-			BUG();
-	}
-
-	rb_link_node(node, parent, rbp);
-	rb_insert_color(node, &pag->pagb_tree);
-}
-
-void
-xfs_alloc_mark_busy(
-	struct xfs_trans	*tp,
-	xfs_agnumber_t		agno,
-	xfs_agblock_t		bno,
-	xfs_extlen_t		len)
-{
-	struct xfs_busy_extent	*busyp;
-	struct xfs_perag	*pag;
-
-	busyp = kmem_zalloc(sizeof(struct xfs_busy_extent), KM_MAYFAIL);
-	if (!busyp) {
-		/*
-		 * No Memory!  Since it is now not possible to track the free
-		 * block, make this a synchronous transaction to insure that
-		 * the block is not reused before this transaction commits.
-		 */
-		TRACE_BUSY("xfs_alloc_mark_busy", "ENOMEM", agno, bno, len, tp);
-		xfs_trans_set_sync(tp);
-		return;
-	}
-
-	busyp->agno = agno;
-	busyp->bno = bno;
-	busyp->length = len;
-	busyp->tp = tp;
-
-	pag = xfs_perag_get(tp->t_mountp, agno);
-	spin_lock(&pag->pagb_lock);
-	xfs_alloc_busy_insert(pag, bno, &busyp->rb_node);
-	xfs_trans_add_busy(tp, busyp);
-	TRACE_BUSY("xfs_alloc_mark_busy", "got", agno, bno, len, tp);
-	spin_unlock(&pag->pagb_lock);
-	xfs_perag_put(pag);
-}
-
-/*
- * Search for a busy extent within the range of the extent we are about to
- * allocate.  You need to be holding the busy extent tree lock when calling
- * __xfs_alloc_busy_search().
- */
-static struct xfs_busy_extent *
-__xfs_alloc_busy_search(
-	struct xfs_trans	*tp,
-	xfs_agnumber_t		agno,
-	xfs_agblock_t		bno,
-	xfs_extlen_t		len)
-{
-	struct xfs_perag	*pag;
-	struct rb_node		*rbp;
-	xfs_agblock_t		uend, bend;
-
-	uend = bno + len - 1;
-	pag = xfs_perag_get(tp->t_mountp, agno);
-	rbp = pag->pagb_tree.rb_node;
-	while (rbp) {
-		struct xfs_busy_extent	*busyp;
-
-		busyp = rb_entry(rbp, struct xfs_busy_extent, rb_node);
-		bend = busyp->bno + busyp->length - 1;
-		if (uend < busyp->bno)
-			rbp = rbp->rb_left;
-		else if (bno > bend)
-			rbp = rbp->rb_right;
-		else {
-			/* (start1,length1) within (start2, length2) */
-			TRACE_BUSYSEARCH("xfs_alloc_search_busy",
-					 "found1", agno, bno, len, tp);
-			xfs_perag_put(pag);
-			return busyp;
-		}
-	}
-	xfs_perag_put(pag);
-	return NULL;
-}
-
-void
-xfs_alloc_clear_busy(
-	struct xfs_trans	*tp,
-	struct xfs_busy_extent	*busyp)
-{
-	struct xfs_perag	*pag;
-
-	pag = xfs_perag_get(tp->t_mountp, busyp->agno);
-	spin_lock(&pag->pagb_lock);
-
-	/* check that the busyp is still in the rbtree */
-	ASSERT(__xfs_alloc_busy_search(tp, busyp->agno, busyp->bno,
-						busyp->length) == busyp);
-
-	TRACE_UNBUSY("xfs_alloc_clear_busy", "found", busyp->agno, busyp->bno,
-							busyp->length, tp);
-	rb_erase(&busyp->rb_node, &pag->pagb_tree);
-	spin_unlock(&pag->pagb_lock);
-	xfs_perag_put(pag);
-
-	kmem_free(busyp);
-}
-
-/*
- * If we find the extent in the busy list, force the log out to get the
- * extent out of the busy list so the caller can use it straight away.
- */
-STATIC void
-xfs_alloc_search_busy(
-	struct xfs_trans	*tp,
-	xfs_agnumber_t		agno,
-	xfs_agblock_t		bno,
-	xfs_extlen_t		len)
-{
-	struct xfs_perag	*pag;
-	struct xfs_busy_extent	*busyp;
-	xfs_lsn_t		lsn;
-
-	pag = xfs_perag_get(tp->t_mountp, agno);
-	spin_lock(&pag->pagb_lock);
-	busyp = __xfs_alloc_busy_search(tp, agno, bno, len);
-	if (!busyp) {
-		TRACE_BUSYSEARCH("xfs_alloc_search_busy", "not-found", agno, bno, len, tp);
-		spin_unlock(&pag->pagb_lock);
-		xfs_perag_put(pag);
-		return;
-	}
-	/*
-	 * A block was found, force the log through the LSN of the
-	 * transaction that freed the block
-	 */
-	TRACE_BUSYSEARCH("xfs_alloc_search_busy", "found", agno, bno, len, tp);
-	lsn = busyp->tp->t_commit_lsn;
-	spin_unlock(&pag->pagb_lock);
-	xfs_log_force(tp->t_mountp, lsn, XFS_LOG_FORCE|XFS_LOG_SYNC);
-	xfs_perag_put(pag);
-}
Signature · 1 line
1.5.6.5
#7 lore raw

PATCH 4/7 XFS: Don't use log forces when busy extents are allocated

In reply to Dave Chinner

Even though we try to avoid busy extents, there are still situations where we can't avoid them (such as allocation from the AGFL). To minimise the overhead of such allocation behaviour, we mark the transaction doing the allocation as synchronous rather than triggering a log force to get the previous freeing transaction on disk.

The synchronous transaction provides the same guarantees as a synchronous log force because it ensures that all the transactions are on disk. i.e. it preserves the free->allocate order of the extent correctly in recovery.

The big advantage to this method comes from the fact that we don't hold the AGF locked during the writeback of the log buffers during transaction commit. Hence we can be doing further allocations while the synchronous transaction is committing, unlike the current synchronous log force case.

---
 fs/xfs/xfs_ag.h    |    1 -
 fs/xfs/xfs_alloc.c |   93 ++++++++++++++++++++--------------------------------
 2 files changed, 36 insertions(+), 58 deletions(-)
fs/xfs/xfs_ag.h +0 -1
index e7aaa1c..7048d3d 100644
@@ -164,7 +164,6 @@ struct xfs_busy_extent {
 	xfs_agnumber_t	agno;
 	xfs_agblock_t	bno;
 	xfs_extlen_t	length;
-	struct xfs_trans *tp;	/* transaction that did the free */
 };
 
 /*
fs/xfs/xfs_alloc.c +36 -57

194 lines not on this page · raw message

Signature · 1 line
1.5.6.5
#8 lore raw

PATCH 5/7 XFS: Do not classify freed allocation btree blocks as busy

In reply to Dave Chinner

During an allocation or freeing of an extent, we occasionally have an interesting thing happen during a btree update. We may merge a block as part of a record delete, then allocate it again immediately afterwards when inserting a modified extent. xfs_alloc_fixup_trees() does this sort of manipulation to the btrees, and so can merge then immediately split the tree resulting the in the same busy block being used from the free list.

Previously, this would trigger a synchronous log force of the entire log (as the current transaction had a lsn of 0 until committed), but continue to allow the extent to be used because if it is not on disk now then it must have been freed and marked busy in this transaction.

In the case of allocbt blocks, we log the fact that they get moved to the freelist and we also log them when the move off the free list back into the free space trees. When we move the blocks off the freelist back to the trees, we mark the extent busy at that point so that it doesn't get reused until the transaction is committed.

This means that as long as the block is on the AGFL and is only used for allocbt blocks, we don't have to force the log to ensure prior manipulating transactions are on disk as the process of log recovery will replay the transactions in the correct order.

However, we still need to protect against the fact that as we approach ENOSPC in an AG we can allocate data and metadata blocks direct from the AGFL. In this case, we need to treat btree blocks just like any other busy extent. Hence we still need to track btree blocks in the busy extent tree, but we need to distinguish them from normal extents so we can apply the necessary exceptions for btree block allocation.

---
 fs/xfs/xfs_ag.h          |    5 ++
 fs/xfs/xfs_alloc.c       |  181 +++++++++++++++++++++++++++++++++++-----------
 fs/xfs/xfs_alloc.h       |    5 --
 fs/xfs/xfs_alloc_btree.c |   13 ----
 4 files changed, 145 insertions(+), 59 deletions(-)
fs/xfs/xfs_ag.h +5 -0
index 7048d3d..ec87185 100644
@@ -164,6 +164,11 @@ struct xfs_busy_extent {
 	xfs_agnumber_t	agno;
 	xfs_agblock_t	bno;
 	xfs_extlen_t	length;
+	int		flags;
+};
+
+enum {
+	XFS_BUSY_EXT_FREELIST = 0x0001,	/* busy extent on freelist from abt */
 };
 
 /*
fs/xfs/xfs_alloc.c +140 -41

281 lines not on this page · raw message

fs/xfs/xfs_alloc.h +0 -5
index 21040af..69111dd 100644
@@ -122,11 +122,6 @@ extern ktrace_t *xfs_alloc_trace_buf;
 #define	XFS_ALLOC_KTRACE_BUSYSEARCH	6
 #endif
 
-void
-xfs_alloc_mark_busy(xfs_trans_t *tp,
-		xfs_agnumber_t agno,
-		xfs_agblock_t bno,
-		xfs_extlen_t len);
 
 void
 xfs_alloc_clear_busy(xfs_trans_t *tp, struct xfs_busy_extent *busyp);
fs/xfs/xfs_alloc_btree.c +0 -13
index 9e63f8c..f41bfae 100644
@@ -119,19 +119,6 @@ xfs_allocbt_free_block(
 	error = xfs_alloc_put_freelist(cur->bc_tp, agbp, NULL, bno, 1);
 	if (error)
 		return error;
-
-	/*
-	 * Since blocks move to the free list without the coordination used in
-	 * xfs_bmap_finish, we can't allow block to be available for
-	 * reallocation and non-transaction writing (user data) until we know
-	 * that the transaction that moved it to the free list is permanently
-	 * on disk. We track the blocks by declaring these blocks as "busy";
-	 * the busy list is maintained on a per-ag basis and each transaction
-	 * records which entries should be removed when the iclog commits to
-	 * disk. If a busy block is allocated, the iclog is pushed up to the
-	 * LSN that freed the block.
-	 */
-	xfs_alloc_mark_busy(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1);
 	xfs_trans_agbtree_delta(cur->bc_tp, -1);
 	return 0;
 }
Signature · 1 line
1.5.6.5
#9 lore raw

PATCH 6/7 XFS: Avoid busy extent ranges rather than the entire extent

In reply to Dave Chinner

When we check an extent for being busy, we check for an overlap and if we find one we declare that extent off-limits and move on to the next free extent. This means that we can skip entire AGs that have a single large free extent with very small overlapping busy extent.

This can actually cause significant problems - often we have to allocate within a given AG, and we have a reservation that guarantees us that the free space is there. If we only have a singel extent, and part of it is busy, we will fail the allocation and that will cause problems.

To avoid this problem, we should trim the busy range out of the extent we have found and determine if that trimmed range is still OK for allocation. In many cases, this check can be incorporated into the cwallocationextent alignment code which already does trimming of the found extent before determining if it is a valid candidate for allocation.

---
 fs/xfs/xfs_ag.h    |    1 +
 fs/xfs/xfs_alloc.c |  439 +++++++++++++++++++++++++++++-----------------------
 2 files changed, 248 insertions(+), 192 deletions(-)
fs/xfs/xfs_ag.h +1 -0
index ec87185..62bb280 100644
@@ -161,6 +161,7 @@ typedef struct xfs_agfl {
  */
 struct xfs_busy_extent {
 	struct rb_node	rb_node;
+	atomic_t	ref;
 	xfs_agnumber_t	agno;
 	xfs_agblock_t	bno;
 	xfs_extlen_t	length;
fs/xfs/xfs_alloc.c +247 -192

731 lines not on this page · raw message

Signature · 1 line
1.5.6.5
#10 lore raw

PATCH 7/7 XFS: Simplify transaction busy extent tracking

In reply to Dave Chinner

Now that we have a dynamic busy extent structure, we no longer need the complex log busy chunk infrastructure to track them in a transaction. We can simply use a linked list hanging off the transaction structure to do this now.

---
 fs/xfs/xfs_ag.h         |    3 +-
 fs/xfs/xfs_alloc.c      |    4 +-
 fs/xfs/xfs_trans.c      |   30 +++++++-------
 fs/xfs/xfs_trans.h      |   33 +---------------
 fs/xfs/xfs_trans_item.c |  102 -----------------------------------------------
 fs/xfs/xfs_trans_priv.h |    1 -
 6 files changed, 21 insertions(+), 152 deletions(-)
fs/xfs/xfs_ag.h +2 -1
index 62bb280..de105a4 100644
@@ -160,7 +160,8 @@ typedef struct xfs_agfl {
  * have been freed but whose transactions aren't committed to disk yet.
  */
 struct xfs_busy_extent {
-	struct rb_node	rb_node;
+	struct rb_node	rb_node;	/* ag by-bno indexed search tree */
+	struct list_head list;		/* transaction busy extent list */
 	atomic_t	ref;
 	xfs_agnumber_t	agno;
 	xfs_agblock_t	bno;
fs/xfs/xfs_alloc.c +3 -1
index 73536ef..ff3111f 100644
@@ -202,13 +202,14 @@ xfs_alloc_mark_busy(
 	busyp->agno = agno;
 	busyp->bno = bno;
 	busyp->length = len;
+	INIT_LIST_HEAD(&busyp->list);
 	if (freelist)
 		busyp->flags |= XFS_BUSY_EXT_FREELIST; 
 
 	pag = xfs_perag_get(tp->t_mountp, agno);
 	spin_lock(&pag->pagb_lock);
 	if (xfs_alloc_busy_insert(pag, bno, busyp))
-		xfs_trans_add_busy(tp, busyp);
+		list_add(&busyp->list, &tp->t_busy);
 	TRACE_BUSY("xfs_alloc_mark_busy", "got", agno, bno, len, tp);
 	spin_unlock(&pag->pagb_lock);
 	xfs_perag_put(pag);
@@ -226,6 +227,7 @@ xfs_alloc_clear_busy(
 	spin_lock(&pag->pagb_lock);
 	ASSERT(!RB_EMPTY_NODE(&busyp->rb_node));
 	rb_erase(&busyp->rb_node, &pag->pagb_tree);
+	list_del_init(&busyp->list);
 	spin_unlock(&pag->pagb_lock);
 
 	xfs_perag_put(pag);
fs/xfs/xfs_trans.c +15 -15
index e88b9bf..d0d0f6f 100644
@@ -253,9 +253,8 @@ _xfs_trans_alloc(
 	tp->t_type = type;
 	tp->t_mountp = mp;
 	tp->t_items_free = XFS_LIC_NUM_SLOTS;
-	tp->t_busy_free = XFS_LBC_NUM_SLOTS;
 	xfs_lic_init(&(tp->t_items));
-	XFS_LBC_INIT(&(tp->t_busy));
+	INIT_LIST_HEAD(&tp->t_busy);
 	return tp;
 }
 
@@ -282,9 +281,8 @@ xfs_trans_dup(
 	ntp->t_type = tp->t_type;
 	ntp->t_mountp = tp->t_mountp;
 	ntp->t_items_free = XFS_LIC_NUM_SLOTS;
-	ntp->t_busy_free = XFS_LBC_NUM_SLOTS;
 	xfs_lic_init(&(ntp->t_items));
-	XFS_LBC_INIT(&(ntp->t_busy));
+	INIT_LIST_HEAD(&ntp->t_busy);
 
 	ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
 	ASSERT(tp->t_ticket != NULL);
@@ -765,6 +763,19 @@ xfs_trans_unreserve_and_mod_sb(
 	}
 }
 
+/*
+ * xfs_trans_free_busy
+ * Free all of the busy extents from a transaction
+ */
+static void
+xfs_trans_free_busy(xfs_trans_t *tp)
+{
+	struct xfs_busy_extent	*busyp, *n;
+
+	list_for_each_entry_safe(busyp, n, &tp->t_busy, list) {
+		xfs_alloc_clear_busy(tp, busyp);
+	}
+}
 
 /*
  * xfs_trans_commit
@@ -1300,9 +1311,6 @@ xfs_trans_committed(
 {
 	xfs_log_item_chunk_t	*licp;
 	xfs_log_item_chunk_t	*next_licp;
-	xfs_log_busy_chunk_t	*lbcp;
-	xfs_log_busy_slot_t	*lbsp;
-	int			i;
 
 	/*
 	 * Call the transaction's completion callback if there
@@ -1335,14 +1343,6 @@ xfs_trans_committed(
 	/*
 	 * Clear all the per-AG busy list items listed in this transaction
 	 */
-	lbcp = &tp->t_busy;
-	while (lbcp != NULL) {
-		for (i = 0, lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) {
-			if (!XFS_LBC_ISFREE(lbcp, i))
-				xfs_alloc_clear_busy(tp, lbsp->lbc_busyp);
-		}
-		lbcp = lbcp->lbc_next;
-	}
 	xfs_trans_free_busy(tp);
 
 	/*
fs/xfs/xfs_trans.h +1 -32
index 6a5e8d5..b7e8cf0 100644
@@ -819,33 +819,6 @@ typedef struct xfs_item_ops {
 #define XFS_ITEM_PUSHBUF	4
 
 /*
- * This structure is used to maintain a list of block ranges that have been
- * freed in the transaction.  The ranges are listed in the perag[] busy list
- * between when they're freed and the transaction is committed to disk.
- */
-
-typedef struct xfs_log_busy_slot {
-	struct xfs_busy_extent	*lbc_busyp;
-} xfs_log_busy_slot_t;
-
-#define XFS_LBC_NUM_SLOTS	31
-typedef struct xfs_log_busy_chunk {
-	struct xfs_log_busy_chunk	*lbc_next;
-	uint				lbc_free;	/* free slots bitmask */
-	ushort				lbc_unused;	/* first unused */
-	xfs_log_busy_slot_t		lbc_busy[XFS_LBC_NUM_SLOTS];
-} xfs_log_busy_chunk_t;
-
-#define	XFS_LBC_MAX_SLOT	(XFS_LBC_NUM_SLOTS - 1)
-#define	XFS_LBC_FREEMASK	((1U << XFS_LBC_NUM_SLOTS) - 1)
-
-#define	XFS_LBC_INIT(cp)	((cp)->lbc_free = XFS_LBC_FREEMASK)
-#define	XFS_LBC_CLAIM(cp, slot)	((cp)->lbc_free &= ~(1 << (slot)))
-#define	XFS_LBC_SLOT(cp, slot)	(&((cp)->lbc_busy[(slot)]))
-#define	XFS_LBC_VACANCY(cp)	(((cp)->lbc_free) & XFS_LBC_FREEMASK)
-#define	XFS_LBC_ISFREE(cp, slot) ((cp)->lbc_free & (1 << (slot)))
-
-/*
  * This is the type of function which can be given to xfs_trans_callback()
  * to be called upon the transaction's commit to disk.
  */
@@ -896,8 +869,7 @@ typedef struct xfs_trans {
 	unsigned int		t_items_free;	/* log item descs free */
 	xfs_log_item_chunk_t	t_items;	/* first log item desc chunk */
 	xfs_trans_header_t	t_header;	/* header for in-log trans */
-	unsigned int		t_busy_free;	/* busy descs free */
-	xfs_log_busy_chunk_t	t_busy;		/* busy/async free blocks */
+	struct list_head	t_busy;		/* list of busy extents */
 	unsigned long		t_pflags;	/* saved process flags state */
 } xfs_trans_t;
 
@@ -972,9 +944,6 @@ void		xfs_trans_cancel(xfs_trans_t *, int);
 int		xfs_trans_ail_init(struct xfs_mount *);
 void		xfs_trans_ail_destroy(struct xfs_mount *);
 
-xfs_log_busy_slot_t *xfs_trans_add_busy(struct xfs_trans *tp,
-					struct xfs_busy_extent *busyp);
-
 extern kmem_zone_t	*xfs_trans_zone;
 
 #endif	/* __KERNEL__ */
fs/xfs/xfs_trans_item.c +0 -102

105 lines not on this page · raw message

fs/xfs/xfs_trans_priv.h +0 -1
index fd50556..901dc0f 100644
@@ -38,7 +38,6 @@ struct xfs_log_item_desc	*xfs_trans_next_item(struct xfs_trans *,
 void				xfs_trans_free_items(struct xfs_trans *, int);
 void				xfs_trans_unlock_items(struct xfs_trans *,
 							xfs_lsn_t);
-void				xfs_trans_free_busy(xfs_trans_t *tp);
 
 /*
  * AIL traversal cursor.
Signature · 1 line
1.5.6.5
#11 lore raw
In reply to Dave Chinner

Hi Dave,

Am Mittwoch 08 Oktober 2008 schrieb Dave Chinner:

Quoted 6 lines

The busy extent tracking in XFS is currently very static and has some performance issues. We can only track 128 busy extents per AG, and when we overflow this we fall back to synchronous transactions. Also, every time we re-use a busy extent we cause a synchronous log force, which stops all allocation and freeing in that AG while the log force is in progress.

Could this accelerate

tar -xf linux-2.6.26.tar.gz
rm -r linux-2.6.26

?

A student in the Linux Performance Tuning course I hold this week compared this with ext3, even with the improved mkfs.xfs options (but without lazy-count=1, cause mkfs.xfs from Debian Etch is too old) and even with noop as IO scheduler. AFAIR XFS took roughly 3-4 times as long as Ext3, I did not note the exact numbers. This was with 2.6.25. I can repeat the test locally with 2.6.26.5 if wanted.

Ciao,

Signature · 2 lines
Martin 'Helios' Steigerwald - http://www.Lichtvoll.de
GPG: 03B0 0D6C 0040 0710 4AFA  B82F 991B EAAC A599 84C7
#12 lore raw
Quoted 11 lines · Martin Steigerwald

Hi Dave,

Am Mittwoch 08 Oktober 2008 schrieb Dave Chinner:

Quoted 6 lines

The busy extent tracking in XFS is currently very static and has some performance issues. We can only track 128 busy extents per AG, and when we overflow this we fall back to synchronous transactions. Also, every time we re-use a busy extent we cause a synchronous log force, which stops all allocation and freeing in that AG while the log force is in progress.

Quoted 4 lines

Could this accelerate

tar -xf linux-2.6.26.tar.gz
rm -r linux-2.6.26

Not really. There is very little reuse of freed extents in this type of workload. It's when you have 10 users on the filesystem all doing that sort of thing that it will make a difference.

Quoted 8 lines

?

A student in the Linux Performance Tuning course I hold this week compared this with ext3, even with the improved mkfs.xfs options (but without lazy-count=1, cause mkfs.xfs from Debian Etch is too old) and even with noop as IO scheduler. AFAIR XFS took roughly 3-4 times as long as Ext3, I did not note the exact numbers. This was with 2.6.25. I can repeat the test locally with 2.6.26.5 if wanted.

Yes, that's par for the course. XFS journals transactions almost immediately, whereas ext3 gathers lots of changees in memory and checkpoints infrequently. Hence XFS writes a lot more to the journal and is hence slower. The dynamic extent tracking is a necessary step to moving the XFS journalling to a more checkpoint-like setup which would perform much less journal I/O and hence run substantially faster....

See the asynchronous transaction aggregation section here:

http://xfs.org/index.php/Improving_Metadata_Performance_By_Reducing_Journal_Overhead

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#13 lore raw

Am Freitag 10 Oktober 2008 schrieben Sie:

Quoted 2 lines · Martin Steigerwald
Quoted 1 line

Hi Dave,

[...]

Quoted 15 lines
Quoted 6 lines

A student in the Linux Performance Tuning course I hold this week compared this with ext3, even with the improved mkfs.xfs options (but without lazy-count=1, cause mkfs.xfs from Debian Etch is too old) and even with noop as IO scheduler. AFAIR XFS took roughly 3-4 times as long as Ext3, I did not note the exact numbers. This was with 2.6.25. I can repeat the test locally with 2.6.26.5 if wanted.

Yes, that's par for the course. XFS journals transactions almost immediately, whereas ext3 gathers lots of changees in memory and checkpoints infrequently. Hence XFS writes a lot more to the journal and is hence slower. The dynamic extent tracking is a necessary step to moving the XFS journalling to a more checkpoint-like setup which would perform much less journal I/O and hence run substantially faster....

Quoted 4 lines

See the asynchronous transaction aggregation section here:

http://xfs.org/index.php/Improving_Metadata_Performance_By_Reducing_Jou
rnal_Overhead

Thanks for the info Dave.

I still have your three mails about future improvements on XFS on my reading list. I just read a bit of the first one.

Ciao,

Signature · 2 lines
Martin 'Helios' Steigerwald - http://www.Lichtvoll.de
GPG: 03B0 0D6C 0040 0710 4AFA  B82F 991B EAAC A599 84C7