Commit 7196dbb0 authored by Trond Myklebust's avatar Trond Myklebust Committed by Anna Schumaker

SUNRPC: Allow changing of the TCP timeout parameters on the fly

When the NFSv4 server tells us the lease period, we usually want
to adjust down the timeout parameters on the TCP connection to
ensure that we don't miss lease renewals due to a faulty connection.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 8d1b8c62
...@@ -137,6 +137,9 @@ struct rpc_xprt_ops { ...@@ -137,6 +137,9 @@ struct rpc_xprt_ops {
void (*release_request)(struct rpc_task *task); void (*release_request)(struct rpc_task *task);
void (*close)(struct rpc_xprt *xprt); void (*close)(struct rpc_xprt *xprt);
void (*destroy)(struct rpc_xprt *xprt); void (*destroy)(struct rpc_xprt *xprt);
void (*set_connect_timeout)(struct rpc_xprt *xprt,
unsigned long connect_timeout,
unsigned long reconnect_timeout);
void (*print_stats)(struct rpc_xprt *xprt, struct seq_file *seq); void (*print_stats)(struct rpc_xprt *xprt, struct seq_file *seq);
int (*enable_swap)(struct rpc_xprt *xprt); int (*enable_swap)(struct rpc_xprt *xprt);
void (*disable_swap)(struct rpc_xprt *xprt); void (*disable_swap)(struct rpc_xprt *xprt);
...@@ -221,6 +224,7 @@ struct rpc_xprt { ...@@ -221,6 +224,7 @@ struct rpc_xprt {
struct timer_list timer; struct timer_list timer;
unsigned long last_used, unsigned long last_used,
idle_timeout, idle_timeout,
connect_timeout,
max_reconnect_timeout; max_reconnect_timeout;
/* /*
......
...@@ -55,6 +55,8 @@ struct sock_xprt { ...@@ -55,6 +55,8 @@ struct sock_xprt {
size_t rcvsize, size_t rcvsize,
sndsize; sndsize;
struct rpc_timeout tcp_timeout;
/* /*
* Saved socket callback addresses * Saved socket callback addresses
*/ */
...@@ -81,6 +83,7 @@ struct sock_xprt { ...@@ -81,6 +83,7 @@ struct sock_xprt {
#define XPRT_SOCK_CONNECTING 1U #define XPRT_SOCK_CONNECTING 1U
#define XPRT_SOCK_DATA_READY (2) #define XPRT_SOCK_DATA_READY (2)
#define XPRT_SOCK_UPD_TIMEOUT (3)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -2684,6 +2684,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2684,6 +2684,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
{ {
struct rpc_xprt_switch *xps; struct rpc_xprt_switch *xps;
struct rpc_xprt *xprt; struct rpc_xprt *xprt;
unsigned long connect_timeout;
unsigned long reconnect_timeout; unsigned long reconnect_timeout;
unsigned char resvport; unsigned char resvport;
int ret = 0; int ret = 0;
...@@ -2696,6 +2697,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2696,6 +2697,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
return -EAGAIN; return -EAGAIN;
} }
resvport = xprt->resvport; resvport = xprt->resvport;
connect_timeout = xprt->connect_timeout;
reconnect_timeout = xprt->max_reconnect_timeout; reconnect_timeout = xprt->max_reconnect_timeout;
rcu_read_unlock(); rcu_read_unlock();
...@@ -2705,7 +2707,10 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2705,7 +2707,10 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
goto out_put_switch; goto out_put_switch;
} }
xprt->resvport = resvport; xprt->resvport = resvport;
xprt->max_reconnect_timeout = reconnect_timeout; if (xprt->ops->set_connect_timeout != NULL)
xprt->ops->set_connect_timeout(xprt,
connect_timeout,
reconnect_timeout);
rpc_xprt_switch_set_roundrobin(xps); rpc_xprt_switch_set_roundrobin(xps);
if (setup) { if (setup) {
...@@ -2722,24 +2727,35 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt, ...@@ -2722,24 +2727,35 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
} }
EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt); EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt);
struct connect_timeout_data {
unsigned long connect_timeout;
unsigned long reconnect_timeout;
};
static int static int
rpc_xprt_cap_max_reconnect_timeout(struct rpc_clnt *clnt, rpc_xprt_set_connect_timeout(struct rpc_clnt *clnt,
struct rpc_xprt *xprt, struct rpc_xprt *xprt,
void *data) void *data)
{ {
unsigned long timeout = *((unsigned long *)data); struct connect_timeout_data *timeo = data;
if (timeout < xprt->max_reconnect_timeout) if (xprt->ops->set_connect_timeout)
xprt->max_reconnect_timeout = timeout; xprt->ops->set_connect_timeout(xprt,
timeo->connect_timeout,
timeo->reconnect_timeout);
return 0; return 0;
} }
void void
rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo) rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo)
{ {
struct connect_timeout_data timeout = {
.connect_timeout = timeo,
.reconnect_timeout = timeo,
};
rpc_clnt_iterate_for_each_xprt(clnt, rpc_clnt_iterate_for_each_xprt(clnt,
rpc_xprt_cap_max_reconnect_timeout, rpc_xprt_set_connect_timeout,
&timeo); &timeout);
} }
EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout); EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout);
......
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
#include "sunrpc.h" #include "sunrpc.h"
static void xs_close(struct rpc_xprt *xprt); static void xs_close(struct rpc_xprt *xprt);
static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
struct socket *sock);
/* /*
* xprtsock tunables * xprtsock tunables
...@@ -666,6 +668,9 @@ static int xs_tcp_send_request(struct rpc_task *task) ...@@ -666,6 +668,9 @@ static int xs_tcp_send_request(struct rpc_task *task)
if (task->tk_flags & RPC_TASK_SENT) if (task->tk_flags & RPC_TASK_SENT)
zerocopy = false; zerocopy = false;
if (test_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state))
xs_tcp_set_socket_timeouts(xprt, transport->sock);
/* Continue transmitting the packet/record. We must be careful /* Continue transmitting the packet/record. We must be careful
* to cope with writespace callbacks arriving _after_ we have * to cope with writespace callbacks arriving _after_ we have
* called sendmsg(). */ * called sendmsg(). */
...@@ -2238,11 +2243,20 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt) ...@@ -2238,11 +2243,20 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt)
static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt, static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
struct socket *sock) struct socket *sock)
{ {
unsigned int keepidle = DIV_ROUND_UP(xprt->timeout->to_initval, HZ); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
unsigned int keepcnt = xprt->timeout->to_retries + 1; unsigned int keepidle;
unsigned int keepcnt;
unsigned int opt_on = 1; unsigned int opt_on = 1;
unsigned int timeo; unsigned int timeo;
spin_lock_bh(&xprt->transport_lock);
keepidle = DIV_ROUND_UP(xprt->timeout->to_initval, HZ);
keepcnt = xprt->timeout->to_retries + 1;
timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
(xprt->timeout->to_retries + 1);
clear_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
spin_unlock_bh(&xprt->transport_lock);
/* TCP Keepalive options */ /* TCP Keepalive options */
kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
(char *)&opt_on, sizeof(opt_on)); (char *)&opt_on, sizeof(opt_on));
...@@ -2254,12 +2268,38 @@ static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt, ...@@ -2254,12 +2268,38 @@ static void xs_tcp_set_socket_timeouts(struct rpc_xprt *xprt,
(char *)&keepcnt, sizeof(keepcnt)); (char *)&keepcnt, sizeof(keepcnt));
/* TCP user timeout (see RFC5482) */ /* TCP user timeout (see RFC5482) */
timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
(xprt->timeout->to_retries + 1);
kernel_setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, kernel_setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT,
(char *)&timeo, sizeof(timeo)); (char *)&timeo, sizeof(timeo));
} }
static void xs_tcp_set_connect_timeout(struct rpc_xprt *xprt,
unsigned long connect_timeout,
unsigned long reconnect_timeout)
{
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
struct rpc_timeout to;
unsigned long initval;
spin_lock_bh(&xprt->transport_lock);
if (reconnect_timeout < xprt->max_reconnect_timeout)
xprt->max_reconnect_timeout = reconnect_timeout;
if (connect_timeout < xprt->connect_timeout) {
memcpy(&to, xprt->timeout, sizeof(to));
initval = DIV_ROUND_UP(connect_timeout, to.to_retries + 1);
/* Arbitrary lower limit */
if (initval < XS_TCP_INIT_REEST_TO << 1)
initval = XS_TCP_INIT_REEST_TO << 1;
to.to_initval = initval;
to.to_maxval = initval;
memcpy(&transport->tcp_timeout, &to,
sizeof(transport->tcp_timeout));
xprt->timeout = &transport->tcp_timeout;
xprt->connect_timeout = connect_timeout;
}
set_bit(XPRT_SOCK_UPD_TIMEOUT, &transport->sock_state);
spin_unlock_bh(&xprt->transport_lock);
}
static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
{ {
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
...@@ -2728,6 +2768,7 @@ static struct rpc_xprt_ops xs_tcp_ops = { ...@@ -2728,6 +2768,7 @@ static struct rpc_xprt_ops xs_tcp_ops = {
.set_retrans_timeout = xprt_set_retrans_timeout_def, .set_retrans_timeout = xprt_set_retrans_timeout_def,
.close = xs_tcp_shutdown, .close = xs_tcp_shutdown,
.destroy = xs_destroy, .destroy = xs_destroy,
.set_connect_timeout = xs_tcp_set_connect_timeout,
.print_stats = xs_tcp_print_stats, .print_stats = xs_tcp_print_stats,
.enable_swap = xs_enable_swap, .enable_swap = xs_enable_swap,
.disable_swap = xs_disable_swap, .disable_swap = xs_disable_swap,
...@@ -3014,6 +3055,8 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args) ...@@ -3014,6 +3055,8 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
xprt->timeout = &xs_tcp_default_timeout; xprt->timeout = &xs_tcp_default_timeout;
xprt->max_reconnect_timeout = xprt->timeout->to_maxval; xprt->max_reconnect_timeout = xprt->timeout->to_maxval;
xprt->connect_timeout = xprt->timeout->to_initval *
(xprt->timeout->to_retries + 1);
INIT_WORK(&transport->recv_worker, xs_tcp_data_receive_workfn); INIT_WORK(&transport->recv_worker, xs_tcp_data_receive_workfn);
INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket); INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment