Linux XFS filesystem development
 help / color / mirror / Atom feed
From: Eric Sandeen <sandeen@redhat.com>
To: linux-xfs@vger.kernel.org
Cc: djwong@kernel.org, aalbersh@kernel.org,
	Eric Sandeen <sandeen@redhat.com>
Subject: [PATCH 2/3] libxfs: Return -ENOMEM on actual cache node allocation failures
Date: Fri, 25 Sep 2026 14:41:44 -0500	[thread overview]
Message-ID: <20260925194535.397036-3-sandeen@redhat.com> (raw)
In-Reply-To: <20260925194535.397036-1-sandeen@redhat.com>

Today, if we get an underlying ENOMEM from an actual cache node
allocation attempt, it's indistinguishable from "current cache size
is filled" and we'll keep doubling cache size to no avail until the
size itself overflows.

This patch differentiates "no node returned" between the "cache full;
grow it" case and the "underlying allocation actually got ENOMEM" case
so that the caller can do the right thing, and return true ENOMEM
up the stack for callers to handle.

Finally, actually check this returned error at the cache_node_get
callsite (in __cache_lookup).

Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 libxfs/cache.c | 22 ++++++++++++++++------
 libxfs/rdwr.c  |  9 ++++-----
 2 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/libxfs/cache.c b/libxfs/cache.c
index a6dabf92..60bfcdc5 100644
--- a/libxfs/cache.c
+++ b/libxfs/cache.c
@@ -285,7 +285,8 @@ cache_shake(
  * Allocate a new hash node (updating atomic counter in the process),
  * unless doing so will push us over the maximum cache size.
  * Return 1 with nodep set on success.
- * Return 0 and NULL nodep otherwise (cache full, or alloc failure)
+ * Return 0 and NULL nodep if the cache is full (caller should shake/expand).
+ * Return -ENOMEM and NULL nodep if the underlying allocation failed.
  */
 static int
 cache_node_allocate(
@@ -315,7 +316,7 @@ cache_node_allocate(
 		cache->c_count--;
 		pthread_mutex_unlock(&cache->c_mutex);
 		*nodep = NULL;
-		return 0;
+		return -ENOMEM;
 	}
 	pthread_mutex_init(&node->cn_mutex, NULL);
 	list_head_init(&node->cn_mru);
@@ -373,8 +374,8 @@ __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 zero if hit in cache, one if a new node was allocated.  A node
- * is always returned.
+ * Returns zero if hit in cache, one if a new node was allocated.  Returns
+ * -ENOMEM if allocation fails after cache shaking is exhausted.
  */
 int
 cache_node_get(
@@ -391,6 +392,7 @@ cache_node_get(
 	unsigned int		hashidx;
 	int			priority = 0;
 	int			purged = 0;
+	int			error = 0;
 
 	hashidx = cache->hash(key, cache->c_hashsize, cache->c_hashshift);
 	hash = cache->c_hash + hashidx;
@@ -464,8 +466,8 @@ next_object:
 		/*
 		 * not found, allocate a new entry
 		 */
-		cache_node_allocate(cache, key, &node);
-		if (node)
+		error = cache_node_allocate(cache, key, &node);
+		if (error > 0)
 			break;
 
 		priority = cache_shake(cache, priority, false);
@@ -475,6 +477,14 @@ 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.  If the last
+			 * attempt failed with a real ENOMEM rather than a
+			 * full cache, neither shaking nor growing can help;
+			 * return the error to the caller.
+			 */
+			if (error < 0)
+				return error;
 			priority = 0;
 			cache_expand(cache);
 		}
diff --git a/libxfs/rdwr.c b/libxfs/rdwr.c
index 90f2d566..d8ba832e 100644
--- a/libxfs/rdwr.c
+++ b/libxfs/rdwr.c
@@ -411,17 +411,16 @@ __cache_lookup(
 	struct cache_node	*cn = NULL;
 	struct cache		*bcache = key->buftarg->bcache;
 	struct xfs_buf		*bp;
+	int			ret;
 
 	*bpp = NULL;
 
-	cache_node_get(bcache, key, &cn);
-	if (!cn)
-		return -ENOMEM;
+	ret = cache_node_get(bcache, key, &cn);
+	if (ret < 0)
+		return ret;
 	bp = container_of(cn, struct xfs_buf, b_node);
 
 	if (use_xfs_buf_lock) {
-		int		ret;
-
 		ret = pthread_mutex_trylock(&bp->b_lock);
 		if (ret) {
 			ASSERT(ret == EAGAIN);
-- 
2.55.0


  parent reply	other threads:[~2026-09-25 19:45 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-25 19:41 [PATCH 0/3 V3] libxfs: better cache allocation error handling Eric Sandeen
2026-09-25 19:41 ` [PATCH 1/3] libxfs: change cache_node_allocate function signature Eric Sandeen
2026-09-25 22:56   ` Darrick J. Wong
2026-09-25 19:41 ` Eric Sandeen [this message]
2026-09-25 22:58   ` [PATCH 2/3] libxfs: Return -ENOMEM on actual cache node allocation failures Darrick J. Wong
2026-09-28 15:07     ` Eric Sandeen
2026-09-25 19:41 ` [PATCH 3/3] libxfs: do not allow cache growth to overflow Eric Sandeen
2026-09-25 22:59   ` Darrick J. Wong
2026-09-28  8:16 ` [PATCH 0/3 V3] libxfs: better cache allocation error handling Christoph Hellwig
2026-09-28 15:05   ` 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=20260925194535.397036-3-sandeen@redhat.com \
    --to=sandeen@redhat.com \
    --cc=aalbersh@kernel.org \
    --cc=djwong@kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    /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