On Tue, Aug 11, 2026, at 8:03 AM, Jeff Layton wrote:
> svc_register() registers each [program, version] in turn. On failure the
> caller tears the listener down -- svc_setup_socket() frees the svc_sock and
> svc_create_socket() releases the socket -- but the entries that were already
> set stay in rpcbind, now pointing at a closed port. XPT_RPCB_UNREG is set
> later, in svc_udp_init()/svc_tcp_init(), so svc_delete_xprt() never runs for
> this transport and nothing clears them.
>
> Unwind on failure, and stop the walk there rather than registering the
> programs after it. rpcbind matches RPCBPROC_UNSET on
> [program, version, netid] and ignores the address, so the unwind clears the
> netid, not just the port -- the same granularity svc_delete_xprt() already
> unregisters at.
>
> Assisted-by: LLM
> Signed-off-by: Jeff Layton <[email protected]>
> ---
> net/sunrpc/svc.c | 31 ++++++++++++++++++++++++++++++-
> 1 file changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
> index 4f402bbf97ba..9ffa87007004 100644
> --- a/net/sunrpc/svc.c
> +++ b/net/sunrpc/svc.c
> @@ -1183,6 +1183,29 @@ int svc_generic_rpcbind_set(struct net *net,
> }
> EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set);
>
> +/*
> + * Undo the [program, version] registrations that this svc_register()
> call
> + * already made, stopping at version @nvers of program @nprog.
> + *
> + * Note that rpcbind matches RPCBPROC_UNSET on [program, version,
> netid] and
> + * ignores the address, so this clears the netid rather than the one
> port.
> + * That is the granularity svc_delete_xprt() unregisters at as well.
> + */
> +static void svc_unwind_register(const struct svc_serv *serv, struct
> net *net,
> + const int family, const unsigned short proto,
> + unsigned int nprog, unsigned int nvers)
> +{
> + unsigned int p, i;
> +
> + for (p = 0; p <= nprog; p++) {
> + struct svc_program *progp = &serv->sv_programs[p];
> + unsigned int last = p < nprog ? progp->pg_nvers : nvers;
> +
> + for (i = 0; i < last; i++)
> + progp->pg_rpcbind_set(net, progp, i, family, proto, 0);
> + }
> +}
For an IPv4 listener, the port-zero callback falls back through
__svc_rpcb_register4() to rpcb_register(). PMAPPROC_UNSET ignores
its protocol argument, so unwinding a partially successful TCP
registration also removes the mappings for existing listeners on
other transports, I would think.
It might be that the best the kernel can do here is tear everything
down if one registration fails.
--
Chuck Lever