Thread (19 messages) 19 messages, 4 authors, 2026-05-10
COLD31d

[PATCH 3/6] SUNRPC: Defer ip_map sub-object cleanup past RCU grace period

From: Chuck Lever <cel@kernel.org>
Date: 2026-05-01 14:51:34
Also in: linux-nfs, lkml
Subsystem: kernel nfsd, sunrpc, and lockd servers, networking [general], nfs, sunrpc, and lockd clients, the rest · Maintainers: Chuck Lever, Jeff Layton, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Trond Myklebust, Anna Schumaker, Linus Torvalds

From: Chuck Lever <chuck.lever@oracle.com>

ip_map_put() is already correct in that auth_domain_put() of
im->m_client reaches svcauth_unix_domain_release(), which defers
the actual kfree() of the unix_domain with call_rcu(); the ip_map
itself is freed via kfree_rcu(). Readers in c_show() under
cache_seq_start_rcu() therefore observe both objects until the
next RCU grace period.

This patch is a consistency change rather than a bug fix. The
svc_export and svc_expkey caches were converted to the
queue_rcu_work() pattern in commit 48db892356d6 ("NFSD: Defer
sub-object cleanup in export put callbacks") because path_put()
and auth_domain_put() must run in process context after the RCU
grace period. The next patch routes unix_gid through the same
mechanism. Sending ip_map through sunrpc_cache_queue_release()
unifies all four cache_detail .put callbacks on a single release
path and removes the implicit reliance on every current and
future auth_ops .domain_release implementation deferring its own
kfree() behind call_rcu().

Replace the rcu_head field with an rcu_work, move the kfree() and
auth_domain_put() into a new ip_map_release() taking a
work_struct, and have ip_map_put() invoke INIT_RCU_WORK() and
sunrpc_cache_queue_release() in place of kfree_rcu(). Switch
ip_map_cache_destroy() to sunrpc_cache_destroy_net() so
per-namespace teardown waits for outstanding release work
before freeing the cache_detail.

Assisted-by: Claude:claude-opus-4-7[1m]
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
 net/sunrpc/svcauth_unix.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 64a2658faddb..14688813c242 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -103,18 +103,26 @@ struct ip_map {
 	char			m_class[8]; /* e.g. "nfsd" */
 	struct in6_addr		m_addr;
 	struct unix_domain	*m_client;
-	struct rcu_head		m_rcu;
+	struct rcu_work		m_rwork;
 };
 
+static void ip_map_release(struct work_struct *work)
+{
+	struct ip_map *im = container_of(to_rcu_work(work),
+					 struct ip_map, m_rwork);
+
+	if (test_bit(CACHE_VALID, &im->h.flags) &&
+	    !test_bit(CACHE_NEGATIVE, &im->h.flags))
+		auth_domain_put(&im->m_client->h);
+	kfree(im);
+}
+
 static void ip_map_put(struct kref *kref)
 {
-	struct cache_head *item = container_of(kref, struct cache_head, ref);
-	struct ip_map *im = container_of(item, struct ip_map,h);
+	struct ip_map *im = container_of(kref, struct ip_map, h.ref);
 
-	if (test_bit(CACHE_VALID, &item->flags) &&
-	    !test_bit(CACHE_NEGATIVE, &item->flags))
-		auth_domain_put(&im->m_client->h);
-	kfree_rcu(im, m_rcu);
+	INIT_RCU_WORK(&im->m_rwork, ip_map_release);
+	sunrpc_cache_queue_release(&im->m_rwork);
 }
 
 static inline int hash_ip6(const struct in6_addr *ip)
@@ -1569,6 +1577,5 @@ void ip_map_cache_destroy(struct net *net)
 
 	sn->ip_map_cache = NULL;
 	cache_purge(cd);
-	cache_unregister_net(cd, net);
-	cache_destroy_net(cd, net);
+	sunrpc_cache_destroy_net(cd, net);
 }
-- 
2.53.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help