@@ -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)
@@ -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);
}
@@ -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),
};
@@ -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;
}
/**
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@kernel.org> --- 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(-)