Re: [External] : [PATCH 2/2] xfs: consistent low ag space behavior for sparse inode chunk allocs
Mark Tinguely <[email protected]> Fri, 31 Jul 2026 12:54:26 -0500
| Newsgroups | org.kernel.vger.linux-xfs |
|---|---|
| Message-ID | <[email protected]> |
On 7/31/26 11:33 AM, Brian Foster wrote:
> Matt Fleming reports a filesystem shutdown due to inobt block
> allocation failure during sparse chunk allocation. Inode creation
> can involve multiple allocations in a transaction via the initial
> chunk allocation and inode btree growth via the subsequent inobt
> record insertion. Technically this should be safe as the chunk
> allocation sets the allocation minleft parameter to the max depth of
> the inode btree, which means the allocation selects an AG only if
> there is enough free space for record insertion after the
> allocation. The record insertion naturally occurs in the same AG as
> the allocation and the associated AGF is locked and held by the
> current transaction.
>
> The situation that reproduces this failure is a bit of a corner
> case. The allocation and inode btrees are all completely full and
> require a split on the next insertion. The AG has just enough
> available space to satisfy inode creation through sparse allocation
> (i.e. 7 blocks in this example). The block allocation occurs within
> an existing free space record, splitting the free space record into
> two and triggering the aforementioned allocbt splits.
>
> The sparse chunk allocation consumes 4 blocks for the chunk, and 4
> blocks from the AGFL for the two allocbt splits. This leaves the
> AGFL with 4 remaining blocks and 3 available blocks in the AG. The
> record insertion attempts block allocation for the inobt split, but
> even though space is available, the geometry change has changed the
> minimum allocation requirements enforced by
> xfs_alloc_fix_freelist(). The min freelist value jumps from 8 to 12
> due to the alloc btree level increases, so the available space
> calculation goes from something like this before the allocation:
>
> free + agfl - res - minfree - minleft = avail
> 2514 + 8 - 2505 - 8 - 2 = 7
>
> ... to this after it:
>
> 2510 + 4 - 2505 - 12 - 0 = -3
>
> Essentially the minleft value of 2 for the chunk alloc is not
> sufficient to overcome the additional requirements imposed on the
> subsequent inobt alloc due to the allocbt geometry changes caused by
> the first.
>
> Technically this shouldn't be a concern because a full split of any
> of the allocation btrees in one transaction means that we're not
> going to see another full split in the lifetime of that transaction,
> but the allocation path does not behave that way.
>
> Absent of a clear way to lock in the agfl state for the particular
> case of multiple allocations into a single AG, this patch works
> around the problem by increasing the minleft requirement for sparse
> chunk allocations. Specifically, we increase minleft by the number
> of blocks that the alloc length was reduced by. The reasoning behind
> this is to generally preserve the AG level no space behavior between
> full and sparse inode chunk allocations.
>
> The purpose of sparse inode chunks is to facilitate allocation in
> AGs under severe free space fragmentation and thus prevent premature
> -ENOSPC conditions across the broader fs, not necessarily squeeze
> every last block out of any single AG. Therefore, this helps ensure
> the low AG space boundary conditions for sparse inode chunks are
> relatively well tested compared to full inode chunks.
>
> Fixes: 56d1115c9bc7 ("xfs: allocate sparse inode chunks on full chunk allocation failure")
> Reported-by: Matt Fleming <[email protected]>
> Signed-off-by: Brian Foster <[email protected]>
> ---
> fs/xfs/libxfs/xfs_ialloc.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
> index 633b2d6e42c5..7e71c33fbefa 100644
> --- a/fs/xfs/libxfs/xfs_ialloc.c
> +++ b/fs/xfs/libxfs/xfs_ialloc.c
> @@ -840,6 +840,14 @@ xfs_ialloc_ag_alloc(
> args.minlen = igeo->ialloc_min_blks;
> args.maxlen = args.minlen;
>
> + /*
> + * Bump minleft by the alloc size delta to maintain consistent
> + * out of space behavior with normal sized chunks. This isn't a
> + * requirement, but helps avoid sparse chunk and inobt block
> + * allocation quirks at or close to AG depletion.
> + */
> + args.minleft += igeo->ialloc_blks - igeo->ialloc_min_blks;
> +
> /*
> * The inode record will be aligned to full chunk size. We must
> * prevent sparse allocation from AG boundaries that result in
Good catch.
Reviewed-by: Mark Tinguely <[email protected]>