This adds an `rpc_portgroup` structure to describe a group of IP addresses comprising one logical server with multiple ports. The remote endpoint can be in a single server exposing multiple network interfaces, or multiple remote machines implementing a distributed server architecture. Combined with nconnect, the multiple transports try to make use of the multiple addresses given so that the connections are spread across the ports. Signed-off-by: Dan Aloni <dan@xxxxxxxxxxxx> --- include/linux/sunrpc/clnt.h | 9 +++++++ net/sunrpc/clnt.c | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h index 02e7a5863d28..f8c0c33281a8 100644 --- a/include/linux/sunrpc/clnt.h +++ b/include/linux/sunrpc/clnt.h @@ -115,12 +115,21 @@ struct rpc_procinfo { const char * p_name; /* name of procedure */ }; +#define RPC_MAX_PORTS 64 + +struct rpc_portgroup { + int nr; + struct sockaddr_storage addrs[RPC_MAX_PORTS]; +}; + struct rpc_create_args { struct net *net; int protocol; struct sockaddr *address; size_t addrsize; struct sockaddr *saddress; + struct rpc_portgroup *localports; /* additional local addresses */ + struct rpc_portgroup *remoteports; /* additional remote addresses */ const struct rpc_timeout *timeout; const char *servername; const char *nodename; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 612f0a641f4c..5f335a873f03 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -500,6 +500,44 @@ static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args, return clnt; } +struct rpc_clnt_portgroup_iter { + struct rpc_portgroup *pg; + int idx; +}; + +static void take_iter_portgroup_addr(struct rpc_clnt_portgroup_iter *iter, + struct sockaddr **address) +{ + struct rpc_portgroup *pg = iter->pg; + struct sockaddr *existing = *address; + struct sockaddr *new; + + if (!pg || pg->nr == 0) + return; + if (iter->idx >= pg->nr) + iter->idx = 0; + + /* Take port from existing address, or use autobind (0) */ + new = (struct sockaddr *)&pg->addrs[iter->idx++]; + + switch (new->sa_family) { + case AF_INET: + ((struct sockaddr_in *)new)->sin_port = + existing ? ((struct sockaddr_in *)existing)->sin_port + : 0; + break; + case AF_INET6: + ((struct sockaddr_in6 *)new)->sin6_port = + existing ? ((struct sockaddr_in6 *)existing)->sin6_port + : 0; + break; + default: + return; + } + + *address = new; +} + /** * rpc_create - create an RPC client and transport with one call * @args: rpc_clnt create argument structure @@ -522,6 +560,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) .servername = args->servername, .bc_xprt = args->bc_xprt, }; + struct rpc_clnt_portgroup_iter iter_localports = { .pg = args->localports }; + struct rpc_clnt_portgroup_iter iter_remoteports = { .pg = args->remoteports }; char servername[48]; struct rpc_clnt *clnt; int i; @@ -573,6 +613,10 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) xprtargs.servername = servername; } + /* If localports or remoteports are specified, first entry overrides */ + take_iter_portgroup_addr(&iter_localports, &xprtargs.srcaddr); + take_iter_portgroup_addr(&iter_remoteports, &xprtargs.dstaddr); + xprt = xprt_create_transport(&xprtargs); if (IS_ERR(xprt)) return (struct rpc_clnt *)xprt; @@ -595,6 +639,9 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) return clnt; for (i = 0; i < args->nconnect - 1; i++) { + take_iter_portgroup_addr(&iter_localports, &xprtargs.srcaddr); + take_iter_portgroup_addr(&iter_remoteports, &xprtargs.dstaddr); + if (rpc_clnt_add_xprt(clnt, &xprtargs, NULL, NULL) < 0) break; } -- 2.26.2