[PATCH v2 4/8] SUNRPC: undo partial rpcbind registrations when svc_register() fails

Jeff Layton <[email protected]>
Newsgroups gmane.linux.network,gmane.linux.nfs,gmane.linux.kernel
Message-ID <[email protected]>
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);
+	}
+}
+
 /**
  * svc_register - register an RPC service with the local portmapper
  * @serv: svc_serv struct for the service to register
@@ -1191,7 +1214,8 @@ EXPORT_SYMBOL_GPL(svc_generic_rpcbind_set);
  * @proto: transport protocol number to advertise
  * @port: port to advertise
  *
- * Service is registered for any address in the passed-in protocol family
+ * Service is registered for any address in the passed-in protocol family.
+ * A @port of 0 unregisters instead, and then every program is attempted.
  */
 int svc_register(const struct svc_serv *serv, struct net *net,
 		 const int family, const unsigned short proto,
@@ -1216,6 +1240,11 @@ int svc_register(const struct svc_serv *serv, struct net *net,
 				printk(KERN_WARNING "svc: failed to register "
 					"%sv%u RPC service (errno %d).\n",
 					progp->pg_name, i, -ret);
+				if (port) {
+					svc_unwind_register(serv, net, family,
+							    proto, p, i);
+					return ret;
+				}
 				if (!error)
 					error = ret;
 				break;

-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.