[PATCH RFC 8/9] sunrpc: don't hold a struct net reference in rpc_xprt

From: Jeff Layton
Date: Mon Mar 17 2025 - 17:04:01 EST


Currently each rpc_xprt holds a reference to struct net. This is
problematic for at least a couple of reasons:

1/ a container with an nfs mount inside can spontaneously die and take
network connectivity with it. When this happens, the netns and rpc_clnt
stick around and the client continually tries to retransmit any RPCs in
a net namespace that is otherwise defunct.

2/ the gssproxy rpc_clnt is torn down when the netns exits, but that
can never happen due to the circular dependency. The result is that any
container that runs gssproxy will leak the netns on exit.

Instead of holding a reference to the netns in rpc_xprt, add a pre_exit
routine that will shut down all rpc_clnt's associated with the netns,
and then wait for the list to go empty.

Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
---
include/linux/sunrpc/xprt.h | 1 -
net/sunrpc/clnt.c | 2 ++
net/sunrpc/sunrpc_syms.c | 29 +++++++++++++++++++++++++++++
net/sunrpc/xprt.c | 3 +--
4 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
index 81b952649d35e3ad4fa8c7e77388ac2ceb44ce60..67c41917e3d83fc0b5c2240f2f1243a2b67da0ec 100644
--- a/include/linux/sunrpc/xprt.h
+++ b/include/linux/sunrpc/xprt.h
@@ -296,7 +296,6 @@ struct rpc_xprt {
} stat;

struct net *xprt_net;
- netns_tracker ns_tracker;
const char *servername;
const char *address_strings[RPC_DISPLAY_MAX];
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 0028858b12d97e7b45f4c24cfbd761ba2a734b32..80cd1ddd155db64fc5b2449ebf54714d80a2838c 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -91,6 +91,8 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)

spin_lock(&sn->rpc_client_lock);
list_del(&clnt->cl_clients);
+ if (list_empty(&sn->all_clients))
+ wake_up_var(&sn->all_clients);
spin_unlock(&sn->rpc_client_lock);
}

diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index bab6cab2940524a970422b62b3fa4212c61c4f43..d919f77522a7c6f7c477b2674f0b3a54155c91d9 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -77,9 +77,38 @@ static __net_exit void sunrpc_exit_net(struct net *net)
WARN_ON_ONCE(!list_empty(&sn->all_clients));
}

+static void shutdown_all_clients(struct sunrpc_net *sn)
+{
+ struct rpc_clnt *clnt;
+
+ lockdep_assert_held(&sn->rpc_client_lock);
+
+ list_for_each_entry(clnt, &sn->all_clients, cl_clients)
+ rpc_clnt_shutdown(clnt);
+}
+
+static bool all_clients_gone(struct sunrpc_net *sn)
+{
+ bool empty;
+
+ spin_lock(&sn->rpc_client_lock);
+ empty = list_empty(&sn->all_clients);
+ spin_unlock(&sn->rpc_client_lock);
+ return empty;
+}
+
+static void sunrpc_pre_exit_net(struct net *net)
+{
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ shutdown_all_clients(sn);
+ wait_var_event(&sn->all_clients, all_clients_gone(sn));
+}
+
static struct pernet_operations sunrpc_net_ops = {
.init = sunrpc_init_net,
.exit = sunrpc_exit_net,
+ .pre_exit = sunrpc_pre_exit_net,
.id = &sunrpc_net_id,
.size = sizeof(struct sunrpc_net),
};
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 09f245cda5262a572c450237419c80b183a83568..040cc9bf92cfa8f28edaee707a09a9e8d9955c41 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -1849,7 +1849,6 @@ EXPORT_SYMBOL_GPL(xprt_alloc);

void xprt_free(struct rpc_xprt *xprt)
{
- put_net_track(xprt->xprt_net, &xprt->ns_tracker);
xprt_free_all_slots(xprt);
xprt_free_id(xprt);
rpc_sysfs_xprt_destroy(xprt);
@@ -2047,7 +2046,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net)

xprt_init_xid(xprt);

- xprt->xprt_net = get_net_track(net, &xprt->ns_tracker, GFP_KERNEL);
+ xprt->xprt_net = net;
}

/**

--
2.48.1