@@ -180,7 +180,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
clp->cl_proto = cl_init->proto;
clp->cl_nconnect = cl_init->nconnect;
clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1;
- clp->cl_net = get_net(cl_init->net);
+ clp->cl_net = cl_init->net;
#if IS_ENABLED(CONFIG_NFS_LOCALIO)
seqlock_init(&clp->cl_boot_lock);
@@ -244,13 +244,15 @@ static void pnfs_init_server(struct nfs_server *server)
*/
void nfs_free_client(struct nfs_client *clp)
{
+ struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
+
nfs_localio_disable_client(clp);
/* -EIO all pending I/O */
if (!IS_ERR(clp->cl_rpcclient))
rpc_shutdown_client(clp->cl_rpcclient);
- put_net(clp->cl_net);
+ wake_up_var(&nn->nfs_client_list);
put_nfs_version(clp->cl_nfs_mod);
kfree(clp->cl_hostname);
kfree(clp->cl_acceptor);
@@ -2562,9 +2562,37 @@ static void nfs_net_exit(struct net *net)
nfs_clients_exit(net);
}
+static bool all_clients_gone(struct nfs_net *nn)
+{
+ bool gone;
+
+ spin_lock(&nn->nfs_client_lock);
+ gone = list_empty(&nn->nfs_client_list);
+ spin_unlock(&nn->nfs_client_lock);
+
+ return gone;
+}
+
+static void nfs_net_pre_exit(struct net *net)
+{
+ struct nfs_net *nn = net_generic(net, nfs_net_id);
+ struct nfs_server *server;
+ struct nfs_client *clp;
+
+ spin_lock(&nn->nfs_client_lock);
+ list_for_each_entry(server, &nn->nfs_volume_list, master_link)
+ nfs_server_shutdown(server);
+ list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link)
+ rpc_clnt_shutdown(clp->cl_rpcclient);
+ spin_unlock(&nn->nfs_client_lock);
+
+ wait_var_event(&nn->nfs_client_list, all_clients_gone(nn));
+}
+
static struct pernet_operations nfs_net_ops = {
.init = nfs_net_init,
.exit = nfs_net_exit,
+ .pre_exit = nfs_net_pre_exit,
.id = &nfs_net_id,
.size = sizeof(struct nfs_net),
};
An NFS client holds a reference to the net namespace. This can cause nfs_clients to stick around after a container dies spontaneously. The container orchestrator sees that there are no more tasks in the container, detaches the filesystems and tears down the networking. Unfortunately, there can still be RPCs in flight that will end up attempting to retransmit indefinitely. No userland tasks have a way to reach that net namespace any longer though, so there is no hope of it being rescued. Instead of keeping a net reference in struct nfs_client, add a nfs_net pre_exit routine that kills off the nfs_server and any remaining nfs_clients, and then waits for the nfs_client_list to go empty. Signed-off-by: Jeff Layton <jlayton@kernel.org> --- fs/nfs/client.c | 6 ++++-- fs/nfs/inode.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-)