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@xxxxxxxxxxxxxxxxxx> --- 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 --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; -- 2.25.1