Re: [PATCH v4 4/6] xfs: generic AG set based steering

Kanchan Joshi <[email protected]>
Newsgroups org.kernel.vger.linux-xfs,org.kernel.vger.linux-block,org.kernel.vger.linux-fsdevel
Message-ID <[email protected]>
On 7/21/2026 8:50 AM, Darrick J. Wong wrote:
> On Fri, Jul 17, 2026 at 06:25:36PM +0530, Kanchan Joshi wrote:
>> Improve allocator concurrency and reduce interleaving by introducing
>> fixed sized AG set.
>> Use low bits of the inode as a hash to select AG within the AG set.
>> Overall, a file will try to use the same AG (and contiguity is maintained),
>> but multiple files will be spread across all AGs in the target AG set.
>>
>> Suggested-by: Dave Chinner<[email protected]>
>> Signed-off-by: Kanchan Joshi<[email protected]>
>> Signed-off-by: Anuj Gupta<[email protected]>
>> ---
>>   fs/xfs/libxfs/xfs_bmap.c | 38 ++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 38 insertions(+)
>>
>> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
>> index d64defeda645..fd1a3aa4ad3f 100644
>> --- a/fs/xfs/libxfs/xfs_bmap.c
>> +++ b/fs/xfs/libxfs/xfs_bmap.c
>> @@ -3192,6 +3192,36 @@ xfs_bmap_select_minlen(
>>   	return args->maxlen;
>>   }
>>   
>> +#define	GENERIC_AG_SET_SZ	(2)
> What does this define?

We need more than 1 AG to do inode-based fanning of files and improve 
concurrency (AGF lock). This is the minimum required to do any 
improvement, in the default allocator.
This 'minimum fan-out' is what Dave suggested to extract out of 
write-stream and apply to two other allocators- default (which is what 
you see now), and filestream (future work).

And this can be reviewed as an independent patch as it does not require 
any other work done in the series. No application input is required, 
this is automatic optimization.

The write-stream allocator (next patch), uses a different AG-set of size 
NR_AGs/NR_WRITE_STREAMs. And that is an explicit (application input 
based) allocator.

  >> +
>> +static inline xfs_agnumber_t
>> +xfs_default_ag_set_size(
>> +	struct xfs_inode	*ip)
>> +{
>> +	struct xfs_mount	*mp = ip->i_mount;
>> +
>> +	return min_t(xfs_agnumber_t, GENERIC_AG_SET_SZ, mp->m_sb.sb_agcount);
> Because I'm not sure what it means on a single-AG filesystem.

On a single-AG filesystem, this will do nothing. The base-AG input given 
by the default allocator will not get changed.

But we get improvements on multi-AG filsystem; not sure if you saw the 
numbers in the cover letter. Corresponding to the 'generic AG-set' below:

1. On regular NVMe

a. Inter-stream concurrency
---------------------------
fio: 4k write, direct IO, 16 jobs, 1 directory, 16 files * 8GiB, iodepth 32
xfs: 16 AGs, 4 write-streams

base: 41 KIOPS
generic AG-set: 93 KIOPS (+126%)
write-stream AG-set: 227 KIOPS (+453%)
here, 16 files are assigned 4 unique write-streams (4 files/stream)

b. Intra-stream concurrency
----------------------------
fio: 4k write, direct IO, 4 jobs, 1 directory, 4 files * 8GiB, iodepth 32
xfs: 16 AGs, 4 write-streams, generic AG-set size = 2, write-stream 
AG-set size = 4

base: 59 KIOPS
generic AG-set: 94 KIOPS (+59%)
write-stream AG-set: 112 KIOPS (+89%)
here, 4 files are assigned single write-stream


>> +}
>> +
>> +static xfs_agnumber_t
>> +xfs_ag_to_ag_set(
>> +	struct xfs_bmalloca	*ap,
>> +	xfs_agnumber_t		base_agno)
>> +{
>> +	struct xfs_inode	*ip = ap->ip;
>> +	struct xfs_mount	*mp = ip->i_mount;
>> +	xfs_agnumber_t		set_size;
>> +
>> +	/* Apply fanning only for regular file data */
>> +	if (!(ap->datatype & XFS_ALLOC_USERDATA))
>> +		return base_agno;
>> +
>> +	set_size = xfs_default_ag_set_size(ip);
>> +	/* Fan out within the AG set using low bits of the inode */
>> +	return (base_agno + (XFS_INO_TO_AGINO(mp, I_INO(ip)) % set_size)) %
>> +		mp->m_sb.sb_agcount;
>> +}
>> +
>>   static int
>>   xfs_bmap_btalloc_select_lengths(
>>   	struct xfs_bmalloca	*ap,
>> @@ -3587,8 +3617,16 @@ xfs_bmap_btalloc_best_length(
>>   {
>>   	xfs_extlen_t		blen = 0;
>>   	int			error;
>> +	xfs_agnumber_t		target_ag, start_ag;
>>   
>>   	ap->blkno = XFS_INODE_TO_FSB(ap->ip);
>> +
>> +	/* fan out initial AG across the generic AG set */
>> +	start_ag = XFS_FSB_TO_AGNO(args->mp, ap->blkno);
>> +	target_ag = xfs_ag_to_ag_set(ap, start_ag);
>> +	if (target_ag != start_ag)
>> +		ap->blkno = XFS_AGB_TO_FSB(args->mp, target_ag, 0);
> /me wonders, if xfs_bmap_rtalloc looked at ap->blkno for a hint the way
> that the data device allocator does, then would it be trivial to have
> write streams on the rt device too?
Trying to understand if this kind of fanning out (this patch) or the 
logical placement (patch 5 with write-stream based AG placement) help us 
solve anything for rt device?
For regular RT device, the round-robin rotor will distribute files into 
multiple RTGs. So we don't have AGF like contention.
And using write-stream to explicitly send multiple files to a single RTG 
will do more harm than good?

And for zoned RT device, the placement is happening via write-hint.

Am I missing something that you have in mind?

All the above is for logical isolation. For physical isolation, your 
comments already help to establish that write-stream can be made to 
work. For the regular RT device sitting on top of NVMe (FDP-enabled). 
That needs few changes, but in previous patch (#3).


> I guess the tricky part would be figuring out what to do if you ever
> want to switch a file between rt and data devices -- presumably you'd
> just reset the write stream id to the default, but I guess you could
> reject such a switch if the id had been set explicitly.

Yes, we do such checks in patch 3 now also.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.