svc_delete_xprt() unregisters each listener it destroys. One NFSD_CMD_LISTENER_SET that removes listeners therefore pays one local rpcbind timeout for each of them, under nfsd_mutex, on top of the one the create loop already bounds. One failure is enough to know that the rest of the teardown will not fare better. When the call gets no answer, clear XPT_RPCB_UNREG on every remaining transport in the same net. Assisted-by: LLM Signed-off-by: Jeff Layton --- net/sunrpc/svc_xprt.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 40040af588fb..7e471c92f23a 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -1101,6 +1101,22 @@ static void call_xpt_users(struct svc_xprt *xprt) spin_unlock(&xprt->xpt_lock); } +/* + * If rpcbind stops answering, every listener still to be destroyed would + * only wait out the same timeout again. Drop the flag on all of the + * remaining listeners. + */ +static void svc_xprt_clear_rpcb_unreg(struct svc_serv *serv, struct net *net) +{ + struct svc_xprt *xprt; + + spin_lock_bh(&serv->sv_lock); + list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) + if (xprt->xpt_net == net) + clear_bit(XPT_RPCB_UNREG, &xprt->xpt_flags); + spin_unlock_bh(&serv->sv_lock); +} + /* * Remove a dead transport */ @@ -1115,11 +1131,15 @@ static void svc_delete_xprt(struct svc_xprt *xprt) struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt); struct socket *sock = svsk->sk_sock; + unsigned int failures = svc_rpcb_failure_count(serv); if (svc_register(serv, xprt->xpt_net, sock->sk->sk_family, sock->sk->sk_protocol, 0) < 0) pr_warn("failed to unregister %s with rpcbind\n", xprt->xpt_class->xcl_name); + + if (svc_rpcb_failure_count(serv) != failures) + svc_xprt_clear_rpcb_unreg(serv, xprt->xpt_net); } if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags)) -- 2.55.0