Skip to content

Commit

Permalink
NFSv4.1: Don't rebind to the same source port when reconnecting to th…
Browse files Browse the repository at this point in the history
…e server

NFSv2, v3 and NFSv4 servers often have duplicate replay caches that look
at the source port when deciding whether or not an RPC call is a replay
of a previous call. This requires clients to perform strange TCP gymnastics
in order to ensure that when they reconnect to the server, they bind
to the same source port.

NFSv4.1 and NFSv4.2 have sessions that provide proper replay semantics,
that do not look at the source port of the connection. This patch therefore
ensures they can ignore the rebind requirement.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
  • Loading branch information
Trond Myklebust committed Nov 4, 2019
1 parent 52f98f1 commit e6237b6
Show file tree
Hide file tree
Showing 8 changed files with 20 additions and 5 deletions.
3 changes: 2 additions & 1 deletion fs/lockd/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ nlm_bind_host(struct nlm_host *host)
.version = host->h_version,
.authflavor = RPC_AUTH_UNIX,
.flags = (RPC_CLNT_CREATE_NOPING |
RPC_CLNT_CREATE_AUTOBIND),
RPC_CLNT_CREATE_AUTOBIND |
RPC_CLNT_CREATE_REUSEPORT),
.cred = host->h_cred,
};

Expand Down
3 changes: 3 additions & 0 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ int nfs_create_rpc_client(struct nfs_client *clp,
args.flags |= RPC_CLNT_CREATE_INFINITE_SLOTS;
if (test_bit(NFS_CS_NOPING, &clp->cl_flags))
args.flags |= RPC_CLNT_CREATE_NOPING;
if (test_bit(NFS_CS_REUSEPORT, &clp->cl_flags))
args.flags |= RPC_CLNT_CREATE_REUSEPORT;

if (!IS_ERR(clp->cl_rpcclient))
return 0;
Expand Down Expand Up @@ -670,6 +672,7 @@ static int nfs_init_server(struct nfs_server *server,
.timeparms = &timeparms,
.cred = server->cred,
.nconnect = data->nfs_server.nconnect,
.init_flags = (1UL << NFS_CS_REUSEPORT),
};
struct nfs_client *clp;
int error;
Expand Down
5 changes: 4 additions & 1 deletion fs/nfs/nfs4client.c
Original file line number Diff line number Diff line change
Expand Up @@ -879,8 +879,11 @@ static int nfs4_set_client(struct nfs_server *server,
};
struct nfs_client *clp;

if (minorversion > 0 && proto == XPRT_TRANSPORT_TCP)
if (minorversion == 0)
__set_bit(NFS_CS_REUSEPORT, &cl_init.init_flags);
else if (proto == XPRT_TRANSPORT_TCP)
cl_init.nconnect = nconnect;

if (server->flags & NFS_MOUNT_NORESVPORT)
__set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
if (server->options & NFS_OPTION_MIGRATION)
Expand Down
1 change: 1 addition & 0 deletions include/linux/nfs_fs_sb.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct nfs_client {
#define NFS_CS_TSM_POSSIBLE 5 /* - Maybe state migration */
#define NFS_CS_NOPING 6 /* - don't ping on connect */
#define NFS_CS_DS 7 /* - Server is a DS */
#define NFS_CS_REUSEPORT 8 /* - reuse src port on reconnect */
struct sockaddr_storage cl_addr; /* server identifier */
size_t cl_addrlen;
char * cl_hostname; /* hostname of server */
Expand Down
1 change: 1 addition & 0 deletions include/linux/sunrpc/clnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ struct rpc_add_xprt_test {
#define RPC_CLNT_CREATE_NO_IDLE_TIMEOUT (1UL << 8)
#define RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT (1UL << 9)
#define RPC_CLNT_CREATE_SOFTERR (1UL << 10)
#define RPC_CLNT_CREATE_REUSEPORT (1UL << 11)

struct rpc_clnt *rpc_create(struct rpc_create_args *args);
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
Expand Down
3 changes: 2 additions & 1 deletion include/linux/sunrpc/xprt.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ struct rpc_xprt {
unsigned int min_reqs; /* min number of slots */
unsigned int num_reqs; /* total slots */
unsigned long state; /* transport state */
unsigned char resvport : 1; /* use a reserved port */
unsigned char resvport : 1, /* use a reserved port */
reuseport : 1; /* reuse port on reconnect */
atomic_t swapper; /* we're swapping over this
transport */
unsigned int bind_index; /* bind function index */
Expand Down
7 changes: 6 additions & 1 deletion net/sunrpc/clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,9 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
xprt->resvport = 1;
if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)
xprt->resvport = 0;
xprt->reuseport = 0;
if (args->flags & RPC_CLNT_CREATE_REUSEPORT)
xprt->reuseport = 1;

clnt = rpc_create_xprt(args, xprt);
if (IS_ERR(clnt) || args->nconnect <= 1)
Expand Down Expand Up @@ -2906,7 +2909,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
struct rpc_xprt *xprt;
unsigned long connect_timeout;
unsigned long reconnect_timeout;
unsigned char resvport;
unsigned char resvport, reuseport;
int ret = 0;

rcu_read_lock();
Expand All @@ -2918,6 +2921,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
return -EAGAIN;
}
resvport = xprt->resvport;
reuseport = xprt->reuseport;
connect_timeout = xprt->connect_timeout;
reconnect_timeout = xprt->max_reconnect_timeout;
rcu_read_unlock();
Expand All @@ -2928,6 +2932,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
goto out_put_switch;
}
xprt->resvport = resvport;
xprt->reuseport = reuseport;
if (xprt->ops->set_connect_timeout != NULL)
xprt->ops->set_connect_timeout(xprt,
connect_timeout,
Expand Down
2 changes: 1 addition & 1 deletion net/sunrpc/xprtsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -1752,7 +1752,7 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)

static void xs_set_srcport(struct sock_xprt *transport, struct socket *sock)
{
if (transport->srcport == 0)
if (transport->srcport == 0 && transport->xprt.reuseport)
transport->srcport = xs_sock_getport(sock);
}

Expand Down

0 comments on commit e6237b6

Please sign in to comment.