--- /dev/null
+commit 0e94118815e8ff9c1142117764ee3e6cddba0395
+Author: Chuck Lever <chuck.lever@oracle.com>
+Date: Fri Oct 1 15:04:20 2010 -0400
+
+ libnfs.a: Allow multiple RPC listeners to share listener port number
+
+ Normally, when "-p" is not specified on the mountd command line, the
+ TI-RPC library chooses random port numbers for each listener. If a
+ port number _is_ specified on the command line, all the listeners
+ will get the same port number, so SO_REUSEADDR needs to be set on
+ each socket.
+
+ Thus we can't let TI-RPC create the listener sockets for us in this
+ case; we must create them ourselves and then set SO_REUSEADDR (and
+ other socket options) by hand.
+
+ Different versions of the same RPC program have to share the same
+ listener and SVCXPRT, so we have to cache xprts we create, and re-use
+ them when additional requests for registration come from the
+ application.
+
+ Though it doesn't look like it, this fix was "copied" from the legacy
+ rpc_init() function. It's more complicated for TI-RPC, of course,
+ since you can have an arbitrary number of listeners, not just two
+ (one for AF_INET UDP and one for AF_INET TCP).
+
+ Fix for:
+
+ https://bugzilla.linux-nfs.org/show_bug.cgi?id=190
+
+ There have been no reports of problems with specifying statd's
+ listener port, but I expect this is a problem for statd too.
+
+ Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+
+diff --git a/support/nfs/svc_create.c b/support/nfs/svc_create.c
+index 59ba505..fdc4846 100644
+--- a/support/nfs/svc_create.c
++++ b/support/nfs/svc_create.c
+@@ -27,6 +27,7 @@
+ #include <memory.h>
+ #include <signal.h>
+ #include <unistd.h>
++#include <errno.h>
+ #include <netdb.h>
+
+ #include <netinet/in.h>
+@@ -41,11 +42,68 @@
+ #include "tcpwrapper.h"
+ #endif
+
++#include "sockaddr.h"
+ #include "rpcmisc.h"
+ #include "xlog.h"
+
+ #ifdef HAVE_LIBTIRPC
+
++#define SVC_CREATE_XPRT_CACHE_SIZE (8)
++static SVCXPRT *svc_create_xprt_cache[SVC_CREATE_XPRT_CACHE_SIZE] = { NULL, };
++
++/*
++ * Cache an SVC xprt, in case there are more programs or versions to
++ * register against it.
++ */
++static void
++svc_create_cache_xprt(SVCXPRT *xprt)
++{
++ unsigned int i;
++
++ /* Check if we've already got this one... */
++ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
++ if (svc_create_xprt_cache[i] == xprt)
++ return;
++
++ /* No, we don't. Cache it. */
++ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++)
++ if (svc_create_xprt_cache[i] == NULL) {
++ svc_create_xprt_cache[i] = xprt;
++ return;
++ }
++
++ xlog(L_ERROR, "%s: Failed to cache an xprt", __func__);
++}
++
++/*
++ * Find a previously cached SVC xprt structure with the given bind address
++ * and transport semantics.
++ *
++ * Returns pointer to a SVC xprt.
++ *
++ * If no matching SVC XPRT can be found, NULL is returned.
++ */
++static SVCXPRT *
++svc_create_find_xprt(const struct sockaddr *bindaddr, const struct netconfig *nconf)
++{
++ unsigned int i;
++
++ for (i = 0; i < SVC_CREATE_XPRT_CACHE_SIZE; i++) {
++ SVCXPRT *xprt = svc_create_xprt_cache[i];
++ struct sockaddr *sap;
++
++ if (xprt == NULL)
++ continue;
++ if (strcmp(nconf->nc_netid, xprt->xp_netid) != 0)
++ continue;
++ sap = (struct sockaddr *)xprt->xp_ltaddr.buf;
++ if (!nfs_compare_sockaddr(bindaddr, sap))
++ continue;
++ return xprt;
++ }
++ return NULL;
++}
++
+ /*
+ * Set up an appropriate bind address, given @port and @nconf.
+ *
+@@ -98,17 +156,112 @@ svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
+ return ai;
+ }
+
++/*
++ * Create a listener socket on a specific bindaddr, and set
++ * special socket options to allow it to share the same port
++ * as other listeners.
++ *
++ * Returns an open, bound, and possibly listening network
++ * socket on success.
++ *
++ * Otherwise returns -1 if some error occurs.
++ */
++static int
++svc_create_sock(const struct sockaddr *sap, socklen_t salen,
++ struct netconfig *nconf)
++{
++ int fd, type, protocol;
++ int one = 1;
++
++ switch(nconf->nc_semantics) {
++ case NC_TPI_CLTS:
++ type = SOCK_DGRAM;
++ break;
++ case NC_TPI_COTS_ORD:
++ type = SOCK_STREAM;
++ break;
++ default:
++ xlog(D_GENERAL, "%s: Unrecognized bind address semantics: %u",
++ __func__, nconf->nc_semantics);
++ return -1;
++ }
++
++ if (strcmp(nconf->nc_proto, NC_UDP) == 0)
++ protocol = (int)IPPROTO_UDP;
++ else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
++ protocol = (int)IPPROTO_TCP;
++ else {
++ xlog(D_GENERAL, "%s: Unrecognized bind address protocol: %s",
++ __func__, nconf->nc_proto);
++ return -1;
++ }
++
++ fd = socket((int)sap->sa_family, type, protocol);
++ if (fd == -1) {
++ xlog(L_ERROR, "Could not make a socket: (%d) %m",
++ errno);
++ return -1;
++ }
++
++#ifdef IPV6_SUPPORTED
++ if (sap->sa_family == AF_INET6) {
++ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
++ &one, sizeof(one)) == -1) {
++ xlog(L_ERROR, "Failed to set IPV6_V6ONLY: (%d) %m",
++ errno);
++ (void)close(fd);
++ return -1;
++ }
++ }
++#endif /* IPV6_SUPPORTED */
++
++ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
++ &one, sizeof(one)) == -1) {
++ xlog(L_ERROR, "Failed to set SO_REUSEADDR: (%d) %m",
++ errno);
++ (void)close(fd);
++ return -1;
++ }
++
++ if (bind(fd, sap, salen) == -1) {
++ xlog(L_ERROR, "Could not bind socket: (%d) %m",
++ errno);
++ (void)close(fd);
++ return -1;
++ }
++
++ if (nconf->nc_semantics == NC_TPI_COTS_ORD)
++ if (listen(fd, SOMAXCONN) == -1) {
++ xlog(L_ERROR, "Could not listen on socket: (%d) %m",
++ errno);
++ (void)close(fd);
++ return -1;
++ }
++
++ return fd;
++}
++
++/*
++ * The simple case is allowing the TI-RPC library to create a
++ * transport itself, given just the bind address and transport
++ * semantics.
++ *
++ * The port is chosen at random by the library; we don't know
++ * what it is. So the new xprt cannot be cached here.
++ *
++ * Returns the count of started listeners (one or zero).
++ */
+ static unsigned int
+-svc_create_nconf(const char *name, const rpcprog_t program,
++svc_create_nconf_rand_port(const char *name, const rpcprog_t program,
+ const rpcvers_t version,
+ void (*dispatch)(struct svc_req *, SVCXPRT *),
+- const uint16_t port, struct netconfig *nconf)
++ struct netconfig *nconf)
+ {
+ struct t_bind bindaddr;
+ struct addrinfo *ai;
+ SVCXPRT *xprt;
+
+- ai = svc_create_bindaddr(nconf, port);
++ ai = svc_create_bindaddr(nconf, 0);
+ if (ai == NULL)
+ return 0;
+
+@@ -119,7 +272,7 @@ svc_create_nconf(const char *name, const rpcprog_t program,
+ freeaddrinfo(ai);
+ if (xprt == NULL) {
+ xlog(D_GENERAL, "Failed to create listener xprt "
+- "(%s, %u, %s)", name, version, nconf->nc_netid);
++ "(%s, %u, %s)", name, version, nconf->nc_netid);
+ return 0;
+ }
+
+@@ -133,6 +286,81 @@ svc_create_nconf(const char *name, const rpcprog_t program,
+ return 1;
+ }
+
++/*
++ * If a port is specified on the command line, that port value will be
++ * the same for all listeners created here. Create each listener socket
++ * in advance and set SO_REUSEADDR, rather than allowing the RPC library
++ * to create the listeners for us on a randomly chosen port (RPC_ANYFD).
++ *
++ * Also, to support multiple RPC versions on the same listener, register
++ * any new versions on the same transport that is already handling other
++ * versions on the same bindaddr and transport. To accomplish this,
++ * cache previously created xprts on a list, and check that list before
++ * creating a new socket for this [program, version].
++ *
++ * Returns the count of started listeners (one or zero).
++ */
++static unsigned int
++svc_create_nconf_fixed_port(const char *name, const rpcprog_t program,
++ const rpcvers_t version,
++ void (*dispatch)(struct svc_req *, SVCXPRT *),
++ const uint16_t port, struct netconfig *nconf)
++{
++ struct addrinfo *ai;
++ SVCXPRT *xprt;
++
++ ai = svc_create_bindaddr(nconf, port);
++ if (ai == NULL)
++ return 0;
++
++ xprt = svc_create_find_xprt(ai->ai_addr, nconf);
++ if (xprt == NULL) {
++ int fd;
++
++ fd = svc_create_sock(ai->ai_addr, ai->ai_addrlen, nconf);
++ if (fd == -1)
++ goto out_free;
++
++ xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
++ if (xprt == NULL) {
++ xlog(D_GENERAL, "Failed to create listener xprt "
++ "(%s, %u, %s)", name, version, nconf->nc_netid);
++ (void)close(fd);
++ goto out_free;
++ }
++ }
++
++ if (!svc_reg(xprt, program, version, dispatch, nconf)) {
++ /* svc_reg(3) destroys @xprt in this case */
++ xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
++ name, version, nconf->nc_netid);
++ goto out_free;
++ }
++
++ svc_create_cache_xprt(xprt);
++
++ freeaddrinfo(ai);
++ return 1;
++
++out_free:
++ freeaddrinfo(ai);
++ return 0;
++}
++
++static unsigned int
++svc_create_nconf(const char *name, const rpcprog_t program,
++ const rpcvers_t version,
++ void (*dispatch)(struct svc_req *, SVCXPRT *),
++ const uint16_t port, struct netconfig *nconf)
++{
++ if (port != 0)
++ return svc_create_nconf_fixed_port(name, program,
++ version, dispatch, port, nconf);
++
++ return svc_create_nconf_rand_port(name, program,
++ version, dispatch, nconf);
++}
++
+ /**
+ * nfs_svc_create - start up RPC svc listeners
+ * @name: C string containing name of new service