diff mbox series

[4/5] xfs: push the agfl set aside into xfs_alloc_space_available

Message ID d6841bd2b2c91912e361063d0f4ac0344298f0a3.1723688622.git.kjlx@templeofstupid.com (mailing list archive)
State Accepted, archived
Headers show
Series linux: Modifying per-ag reservation to account for dependent allocations | expand

Commit Message

Krister Johansen Aug. 15, 2024, 7:34 p.m. UTC
The blocks that have been set aside for dependent allocations and
freelist refilling at reservation induced ENOSPC are deducted from
m_ag_max_usable, which prevents them from being factored into the
longest_free_extent and maxlen calculations.  However, it's still
possible to eat into this space by making multiple small allocations.
Catch this case by withholding the space that's been set aside in
xfs_alloc_space_available's available space calculation.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 fs/xfs/libxfs/xfs_alloc.c | 15 +++++++++++++--
 fs/xfs/libxfs/xfs_alloc.h |  1 +
 fs/xfs/xfs_mount.c        |  1 +
 fs/xfs/xfs_mount.h        |  5 +++++
 4 files changed, 20 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 826f527d20f2..4dd401d407c2 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -153,7 +153,7 @@  xfs_alloc_min_freelist_calc(
  * middle of dependent allocations when they are close to hitting the
  * reservation-induced limits.
  */
-static unsigned int
+unsigned int
 xfs_allocbt_agfl_reserve(
 	struct xfs_mount	*mp)
 {
@@ -2593,6 +2593,7 @@  xfs_alloc_space_available(
 	xfs_extlen_t		reservation; /* blocks that are still reserved */
 	int			available;
 	xfs_extlen_t		agflcount;
+	xfs_extlen_t		set_aside = 0;
 
 	if (flags & XFS_ALLOC_FLAG_FREEING)
 		return true;
@@ -2605,6 +2606,16 @@  xfs_alloc_space_available(
 	if (longest < alloc_len)
 		return false;
 
+	/*
+	 * Withhold from the available space any that has been set-aside as a
+	 * reserve for refilling the AGFL close to ENOSPC.  In the case where a
+	 * dependent allocation is in progress, allow that space to be consumed
+	 * so that the dependent allocation may complete successfully. Without
+	 * this, we may ENOSPC in the middle of the allocation chain and
+	 * shutdown the filesystem.
+	 */
+	if (args->tp->t_highest_agno == NULLAGNUMBER)
+		set_aside = args->mp->m_ag_agfl_setaside;
 	/*
 	 * Do we have enough free space remaining for the allocation? Don't
 	 * account extra agfl blocks because we are about to defer free them,
@@ -2612,7 +2623,7 @@  xfs_alloc_space_available(
 	 */
 	agflcount = min_t(xfs_extlen_t, pag->pagf_flcount, min_free);
 	available = (int)(pag->pagf_freeblks + agflcount -
-			  reservation - min_free - args->minleft);
+			  reservation - min_free - args->minleft - set_aside);
 	if (available < (int)max(args->total, alloc_len))
 		return false;
 
diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h
index fae170825be0..7e92c4c455a1 100644
--- a/fs/xfs/libxfs/xfs_alloc.h
+++ b/fs/xfs/libxfs/xfs_alloc.h
@@ -70,6 +70,7 @@  typedef struct xfs_alloc_arg {
 /* freespace limit calculations */
 unsigned int xfs_alloc_set_aside(struct xfs_mount *mp);
 unsigned int xfs_alloc_ag_max_usable(struct xfs_mount *mp);
+unsigned int xfs_allocbt_agfl_reserve(struct xfs_mount	*mp);
 
 xfs_extlen_t xfs_alloc_longest_free_extent(struct xfs_perag *pag,
 		xfs_extlen_t need, xfs_extlen_t reserved);
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index ec1f7925b31f..1bc80983310a 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -1002,6 +1002,7 @@  xfs_mountfs(
 	 */
 	mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 	mp->m_ag_max_usable = xfs_alloc_ag_max_usable(mp);
+	mp->m_ag_agfl_setaside = xfs_allocbt_agfl_reserve(mp);
 
 	/*
 	 * Now we are mounted, reserve a small amount of unused space for
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 800788043ca6..4a9321424954 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -220,6 +220,11 @@  typedef struct xfs_mount {
 	 * one.
 	 */
 	uint			m_ag_resblk_count;
+	/*
+	 * Blocks set aside to refill the agfl at ENOSPC and satisfy any
+	 * dependent allocation resulting from a chain of BMBT splits.
+	 */
+	uint			m_ag_agfl_setaside;
 	struct delayed_work	m_reclaim_work;	/* background inode reclaim */
 	struct dentry		*m_debugfs;	/* debugfs parent */
 	struct xfs_kobj		m_kobj;