Linux XFS filesystem development
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <djwong@kernel.org>
To: Eric Sandeen <sandeen@redhat.com>
Cc: linux-xfs@vger.kernel.org
Subject: Re: [PATCH 1/2] xfs_repair: distinguish true ENOMEM from "not found" in cache_node_get
Date: Fri, 4 Sep 2026 09:20:38 -0700	[thread overview]
Message-ID: <20260904162038.GD1933798@frogsfrogsfrogs> (raw)
In-Reply-To: <20260903203638.1094907-2-sandeen@redhat.com>

On Thu, Sep 03, 2026 at 01:42:43PM -0500, Eric Sandeen wrote:
> When cache_node_get() needs a new node it calls cache_node_allocate(),
> which returns NULL for two very different reasons:
> 
>  - the cache is already full (c_count has reached c_maxcount), or
>  - an underlying allocation actually failed with ENOMEM.
> 
> These require different responses. A full cache should be shaken to free
> slots and, failing that, expanded. An actual ENOMEM should be returned
> to the caller to deal with.
> 
> To achieve this, make cache_node_allocate() say which case occurred via
> a new *enomem parameter, set true only for a real allocation failure.
> Cache shaking still happens before returning ENOMEM, as this may actually
> free memory and allow new allocations to succeed.
> 
> Also fix the stale return-value comment, which had the hit/new-node
> cases backwards.
> 
> Signed-off-by: Eric Sandeen <sandeen@redhat.com>
> ---
>  libxfs/cache.c | 39 ++++++++++++++++++++++++++++++++++-----
>  1 file changed, 34 insertions(+), 5 deletions(-)
> 
> diff --git a/libxfs/cache.c b/libxfs/cache.c
> index d5d9ba56..21e4c0c0 100644
> --- a/libxfs/cache.c
> +++ b/libxfs/cache.c
> @@ -284,15 +284,26 @@ cache_shake(
>  /*
>   * Allocate a new hash node (updating atomic counter in the process),
>   * unless doing so will push us over the maximum cache size.
> + *
> + * Returns NULL on failure.  A NULL return can mean two different things:
> + * either the cache is already full (c_count has reached c_maxcount), or
> + * the underlying allocation genuinely failed with ENOMEM.  These call for
> + * very different responses from the caller, so report which one happened
> + * via *enomem: it is set true only for a real allocation failure, and
> + * left false when we cannot allocate another node in the current cache
> + * size.
>   */
>  static struct cache_node *
>  cache_node_allocate(
>  	struct cache *		cache,
> -	cache_key_t		key)
> +	cache_key_t		key,
> +	bool			*enomem)

Hrmm.  Having ENOMEM as an outparam is a little weird, but I suppose we
can't do the ERR_PTR stuff that the kernel does.

OTOH I guess you could set errno here and redefine the return value as:

"This function returns a pointer to a cache object in the case of a hit.
For a miss, it returns NULL with errno set to 0.  For any other runtime
error, it returns NULL with errno set to a nonzero value."

Hrm?

>  {
>  	unsigned int		nodesfree;
>  	struct cache_node *	node;
>  
> +	*enomem = false;
> +
>  	pthread_mutex_lock(&cache->c_mutex);
>  	nodesfree = (cache->c_count < cache->c_maxcount);
>  	if (nodesfree) {
> @@ -309,6 +320,7 @@ cache_node_allocate(
>  		pthread_mutex_lock(&cache->c_mutex);
>  		cache->c_count--;
>  		pthread_mutex_unlock(&cache->c_mutex);
> +		*enomem = true;
>  		return NULL;
>  	}
>  	pthread_mutex_init(&node->cn_mutex, NULL);
> @@ -366,8 +378,9 @@ __cache_node_purge(
>   * hit, in which case this will all be over quickly and painlessly.
>   * Otherwise, we allocate a new node, taking care not to expand the
>   * cache beyond the requested maximum size (shrink it if it would).
> - * Returns one if hit in cache, otherwise zero.  A node is _always_
> - * returned, however.
> + * Returns zero if hit in cache, one if a new node was allocated.  If
> + * allocation fails with a genuine ENOMEM we return -1 with a NULL
> + * *nodep; in every other case a node is returned.
>   */
>  int
>  cache_node_get(
> @@ -384,6 +397,8 @@ cache_node_get(
>  	unsigned int		hashidx;
>  	int			priority = 0;
>  	int			purged = 0;
> +	int			ret;
> +	bool			enomem = false;
>  
>  	hashidx = cache->hash(key, cache->c_hashsize, cache->c_hashshift);
>  	hash = cache->c_hash + hashidx;
> @@ -457,7 +472,7 @@ next_object:
>  		/*
>  		 * not found, allocate a new entry
>  		 */
> -		node = cache_node_allocate(cache, key);
> +		node = cache_node_allocate(cache, key, &enomem);
>  		if (node)
>  			break;

I guess the awkward part here is that you then need extra stupid
variables and logic:

		switch (errno) {
		case ENOMEM:
			enomem = true;
			break;
		case 0:
			break;
		default:
			/* what the heck?? */
			break;
		}

I think I'd almost rather we change cache_node_allocate to return int
and the cache node as an outparam since we do that elsewhere, but
<shrug> I'll defer to Andrey.

--D

>  		priority = cache_shake(cache, priority, false);
> @@ -467,6 +482,18 @@ next_object:
>  		 * If we exceed CACHE_MAX_PRIORITY all slots are full; grow it.
>  		 */
>  		if (priority > CACHE_MAX_PRIORITY) {
> +			/*
> +			 * We've shaken every priority level and still can't
> +			 * allocate.  If the last attempt failed with a real
> +			 * ENOMEM rather than simply a full cache, then neither
> +			 * shaking nor growing the cache can help - give up and
> +			 * let the caller handle the failure.
> +			 */
> +			if (enomem) {
> +				node = NULL;
> +				ret = -1;
> +				goto out;
> +			}
>  			priority = 0;
>  			cache_expand(cache);
>  		}
> @@ -479,7 +506,9 @@ next_object:
>  	hash->ch_count++;
>  	list_add(&node->cn_hash, &hash->ch_list);
>  	pthread_mutex_unlock(&hash->ch_mutex);
> +	ret = 1;
>  
> +out:
>  	if (purged) {
>  		pthread_mutex_lock(&cache->c_mutex);
>  		cache->c_count -= purged;
> @@ -487,7 +516,7 @@ next_object:
>  	}
>  
>  	*nodep = node;
> -	return 1;
> +	return ret;
>  }
>  
>  void
> -- 
> 2.55.0
> 
> 

  reply	other threads:[~2026-09-04 16:20 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-03 18:42 [PATCH 0/2 V2] xfs_repair: improve caching in the face of ENOMEM Eric Sandeen
2026-09-03 18:42 ` [PATCH 1/2] xfs_repair: distinguish true ENOMEM from "not found" in cache_node_get Eric Sandeen
2026-09-04 16:20   ` Darrick J. Wong [this message]
2026-09-04 16:29     ` Eric Sandeen
2026-09-03 18:42 ` [PATCH 2/2] xfs_repair: do not allow cache growth to overflow Eric Sandeen

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=20260904162038.GD1933798@frogsfrogsfrogs \
    --to=djwong@kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=sandeen@redhat.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