@@ -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;
@@ -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;
}
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@kernelim.com> --- include/linux/sunrpc/clnt.h | 9 +++++++ net/sunrpc/clnt.c | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+)