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@xxxxxxxxxx> --- fs/nfs/client.c | 6 ++++-- fs/nfs/inode.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 3b0918ade53cd331d76baaa86fd2adec5d945b78..8f41cb88f88c2b4d7f10424484b6e70ac2b8835a 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -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); diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 1aa67fca69b2fbd8afb1c51be78198220b1e13c7..0352e7e6ee270562a971d031ba02bdec96496288 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -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), }; -- 2.48.1