[PATCH 4/6] SUNRPC: Use shared release pattern for the unix_gid cache

From: Chuck Lever

Date: Fri May 01 2026 - 10:54:11 EST


From: Chuck Lever <chuck.lever@xxxxxxxxxx>

unix_gid_put() is already correct in that put_group_info() runs
inside its call_rcu() callback, after the RCU grace period.
This patch is a consistency change rather than a bug fix:
the three other cache_detail .put callbacks (svc_export,
svc_expkey, ip_map) now use the queue_rcu_work() pattern via
sunrpc_cache_queue_release(), and routing unix_gid through the same
path keeps a single release mechanism for all four caches.

Replace the rcu_head field with an rcu_work, rename
unix_gid_free() to unix_gid_release() and convert it to take
a work_struct, and have unix_gid_put() invoke INIT_RCU_WORK()
and sunrpc_cache_queue_release() in place of call_rcu().
Switch unix_gid_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@xxxxxxxxxx>
---
net/sunrpc/svcauth_unix.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 14688813c242..762cf03574b4 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -420,7 +420,7 @@ struct unix_gid {
struct cache_head h;
kuid_t uid;
struct group_info *gi;
- struct rcu_head rcu;
+ struct rcu_work rwork;
};

static int unix_gid_hash(kuid_t uid)
@@ -428,23 +428,23 @@ static int unix_gid_hash(kuid_t uid)
return hash_long(from_kuid(&init_user_ns, uid), GID_HASHBITS);
}

-static void unix_gid_free(struct rcu_head *rcu)
+static void unix_gid_release(struct work_struct *work)
{
- struct unix_gid *ug = container_of(rcu, struct unix_gid, rcu);
- struct cache_head *item = &ug->h;
+ struct unix_gid *ug = container_of(to_rcu_work(work),
+ struct unix_gid, rwork);

- if (test_bit(CACHE_VALID, &item->flags) &&
- !test_bit(CACHE_NEGATIVE, &item->flags))
+ if (test_bit(CACHE_VALID, &ug->h.flags) &&
+ !test_bit(CACHE_NEGATIVE, &ug->h.flags))
put_group_info(ug->gi);
kfree(ug);
}

static void unix_gid_put(struct kref *kref)
{
- struct cache_head *item = container_of(kref, struct cache_head, ref);
- struct unix_gid *ug = container_of(item, struct unix_gid, h);
+ struct unix_gid *ug = container_of(kref, struct unix_gid, h.ref);

- call_rcu(&ug->rcu, unix_gid_free);
+ INIT_RCU_WORK(&ug->rwork, unix_gid_release);
+ sunrpc_cache_queue_release(&ug->rwork);
}

static int unix_gid_match(struct cache_head *corig, struct cache_head *cnew)
@@ -899,8 +899,7 @@ void unix_gid_cache_destroy(struct net *net)

sn->unix_gid_cache = NULL;
cache_purge(cd);
- cache_unregister_net(cd, net);
- cache_destroy_net(cd, net);
+ sunrpc_cache_destroy_net(cd, net);
}

static struct unix_gid *unix_gid_lookup(struct cache_detail *cd, kuid_t uid)

--
2.53.0