Linux XFS filesystem development
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: Christoph Hellwig <hch@lst.de>
Cc: Carlos Maiolino <cem@kernel.org>,
	Wilfred Mallawa <wilfred.mallawa@wdc.com>,
	Damien Le Moal <dlemoal@kernel.org>,
	Hans Holmberg <hans.holmberg@wdc.com>,
	Andrey Albershteyn <aalbersh@kernel.org>,
	linux-xfs@vger.kernel.org
Subject: Re: [PATCH 2/5] xfs: fix racy open zone caching
Date: Tue, 11 Aug 2026 10:12:01 -0700	[thread overview]
Message-ID: <20260811171201.GD3556460@frogsfrogsfrogs> (raw)
In-Reply-To: <20260811164912.124416-3-hch@lst.de>

On Tue, Aug 11, 2026 at 10:48:38AM -0600, Christoph Hellwig wrote:
> When testing on very fast storage devices, I've observed writers using
> io_uring creating many open zones with just a few kiB written to it,
> which then don't get used.  I tracked this down to multiple io_uring
> helper threads finding a full zone in i_private, and then going on to
> select a one, with the final one winning the race and leaving it in
> i_private.
> 
> Fix this by dropping full zones from i_private as soon we find them,
> checking cached for a cached zoned when a single writes needs a new zone,
> and by keeping an existing cached zone in xfs_set_cached_zone when it
> still has space available, dropping the newly found/allocated one
> instead.  This uses i_flags_lock as a low-level spinlock for short
> hold times to avoid interactions with the ilock, which is used for
> completions.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Much clearer now, thanks!
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>

--D

> ---
>  fs/xfs/xfs_zone_alloc.c | 66 +++++++++++++++++++++++++++++++++--------
>  1 file changed, 53 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
> index 7d13fa7ab30a..bdbb60cc5d5b 100644
> --- a/fs/xfs/xfs_zone_alloc.c
> +++ b/fs/xfs/xfs_zone_alloc.c
> @@ -793,17 +793,35 @@ xfs_get_cached_zone(
>  
>  	rcu_read_lock();
>  	oz = VFS_I(ip)->i_private;
> -	if (oz) {
> -		/*
> -		 * GC only steals open zones at mount time, so no GC zones
> -		 * should end up in the cache.
> -		 */
> -		ASSERT(!oz->oz_is_gc);
> -		if (!atomic_inc_not_zero(&oz->oz_ref))
> +	if (!oz)
> +		goto out_unlock;
> +
> +	/*
> +	 * GC only steals open zones at mount time, so no GC zones should end up
> +	 * in the cache.
> +	 */
> +	ASSERT(!oz->oz_is_gc);
> +
> +	/*
> +	 * Drop the old cached open zone if it is full.
> +	 */
> +	if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
> +		spin_lock(&ip->i_flags_lock);
> +		oz = VFS_I(ip)->i_private;
> +		if (oz && oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
> +			VFS_I(ip)->i_private = NULL;
> +			spin_unlock(&ip->i_flags_lock);
> +			xfs_open_zone_put(oz);
>  			oz = NULL;
> +			goto out_unlock;
> +		}
> +		spin_unlock(&ip->i_flags_lock);
>  	}
> -	rcu_read_unlock();
>  
> +	if (!atomic_inc_not_zero(&oz->oz_ref))
> +		oz = NULL;
> +out_unlock:
> +	rcu_read_unlock();
>  	return oz;
>  }
>  
> @@ -818,18 +836,41 @@ xfs_get_cached_zone(
>   * that were every written to, but significantly simplifies the cached zone
>   * lookup.  Because the open_zone is clearly marked as full when all data
>   * in the underlying RTG was written, the caching is always safe.
> + *
> + * Called with a reference on @oz held.  And returns two references on the
> + * returned zone: one for the caller and one for pinning the zone in
> + * inode->i_private.
>   */
> -static void
> +static struct xfs_open_zone *
>  xfs_set_cached_zone(
>  	struct xfs_inode	*ip,
>  	struct xfs_open_zone	*oz)
>  {
>  	struct xfs_open_zone	*old_oz;
>  
> +	/*
> +	 * If the open zone cached in the inode still has free space, use that
> +	 * instead of the new open zone just selected.  This can happen when
> +	 * multiple threads race to perform zone selection for an inode.
> +	 * io_uring worker threads seem to be good way to trigger this.
> +	 *
> +	 * We need to grab an extra reference to this open zone as the caller
> +	 * owns a reference in addition to the i_private pointer.
> +	 */
> +	spin_lock(&ip->i_flags_lock);
> +	old_oz = VFS_I(ip)->i_private;
> +	if (old_oz && old_oz->oz_allocated < rtg_blocks(old_oz->oz_rtg) &&
> +	    atomic_inc_not_zero(&old_oz->oz_ref)) {
> +		spin_unlock(&ip->i_flags_lock);
> +		xfs_open_zone_put(oz);
> +		return old_oz;
> +	}
> +	VFS_I(ip)->i_private = oz;
>  	atomic_inc(&oz->oz_ref);
> -	old_oz = xchg(&VFS_I(ip)->i_private, oz);
> +	spin_unlock(&ip->i_flags_lock);
>  	if (old_oz)
>  		xfs_open_zone_put(old_oz);
> +	return oz;
>  }
>  
>  static void
> @@ -873,14 +914,13 @@ xfs_zone_alloc_and_submit(
>  	 * the inode is still associated with a zone and use that if so.
>  	 */
>  	if (!*oz)
> +select_zone:
>  		*oz = xfs_get_cached_zone(ip);
> -
>  	if (!*oz) {
> -select_zone:
>  		*oz = xfs_select_zone(mp, write_hint, pack_tight);
>  		if (!*oz)
>  			goto out_error;
> -		xfs_set_cached_zone(ip, *oz);
> +		*oz = xfs_set_cached_zone(ip, *oz);
>  	}
>  
>  	alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),
> -- 
> 2.53.0
> 
> 

  parent reply	other threads:[~2026-08-11 17:12 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-11 16:48 zoned xfs updates v2 Christoph Hellwig
2026-08-11 16:48 ` [PATCH 1/5] xfs: handle NULL open_zone for merged ioends in xfs_ioend_put_open_zones Christoph Hellwig
2026-08-11 17:03   ` Damien Le Moal
2026-08-11 16:48 ` [PATCH 2/5] xfs: fix racy open zone caching Christoph Hellwig
2026-08-11 17:05   ` Damien Le Moal
2026-08-11 17:12   ` Darrick J. Wong [this message]
2026-08-11 16:48 ` [PATCH 3/5] xfs: fix zoned write iomap flags assignments Christoph Hellwig
2026-08-11 17:05   ` Damien Le Moal
2026-08-11 16:48 ` [PATCH 4/5] xfs: factor out a xfs_iomap_set_anon_write helper Christoph Hellwig
2026-08-11 17:06   ` Damien Le Moal
2026-08-11 16:48 ` [PATCH 5/5] xfs: split ioend handling into a separate source file Christoph Hellwig
2026-08-11 17:09   ` Damien Le Moal
  -- strict thread matches above, loose matches on Subject: below --
2026-08-10 15:37 zoned xfs updates Christoph Hellwig
2026-08-10 15:37 ` [PATCH 2/5] xfs: fix racy open zone caching Christoph Hellwig
2026-08-10 18:21   ` Darrick J. Wong
2026-08-11 15:03     ` Christoph Hellwig

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260811171201.GD3556460@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=aalbersh@kernel.org \
    --cc=cem@kernel.org \
    --cc=dlemoal@kernel.org \
    --cc=hans.holmberg@wdc.com \
    --cc=hch@lst.de \
    --cc=linux-xfs@vger.kernel.org \
    --cc=wilfred.mallawa@wdc.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox