forked from Minki/linux
NFS client bugfixes for Linux 4.8
Highlights include: - Stable patch from Olga to fix RPCSEC_GSS upcalls when the same user needs multiple different security services (e.g. krb5i and krb5p). - Stable patch to fix a regression introduced by the use of SO_REUSEPORT, and that prevented the use of multiple different NFS versions to the same server. - TCP socket reconnection timer fixes. - Patch from Neil to disable the use of IPv6 temporary addresses. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXrh03AAoJEGcL54qWCgDyp4EQALwZpmYCxWJE5xSHW95Fs124 HYM8g4LznOfs3/ohInb1ja2FaQqUy0XEk3pSjNKfyYgjuwB4qJSOpnAqoIKxJFGB h4582leYZOZYMMCGslS2I4zcElBYO1WjnKNyb7MpZjCHmN0AdFfIcOXd2K7eL9hM /poImcs5KfMGIEJqmKqMUxmJ3RjxpK3LySQAes/Y5odOiHC4SGJdGUmSeuPGTbQd YjFWVHRFU6kVAzPd2Jl46Sgy6SpDaVz82HodXCSY+8lklmIkbIsVqJs0VWo3WkfL r5WLQ3PzZvloQ7o/E9tZGiB/LEi7roa51hYsG4sleN6Kap5vwyWg0QIKjqyJdFxB JmFanlCMfae3zNz4cusvgu1okvMnNqO4uRXJIAKfk64k775N9ebY7TXAZUK4/UbY 4nxCHcxygamP/k/8HYFpc4964tMaimIs9JUdojad5a3dzffwXcgEC/0HPUih9R+i DO/cbVtWeDkmQPLrUqFfOAbmQdyAjELrv48d5BVIst49uuCULU2LlDlVLiAvaZvq s2YNmr7lkHowvgaH4ShL89wuyyD14Xu5/f49oFBFNKEQay9YthQ8s3XmdZBG7Zl0 oyA1XJjWEq3p8nvPGIqFD26w75ppUbAWLTHsyoU0YfEYrZJrF9jPxowI7WlHgfVo Io79x1sbgTrckjG+osAf =UHph -----END PGP SIGNATURE----- Merge tag 'nfs-for-4.8-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs Pull NFS client bugfixes from Trond Myklebust: "Highlights include: - Stable patch from Olga to fix RPCSEC_GSS upcalls when the same user needs multiple different security services (e.g. krb5i and krb5p). - Stable patch to fix a regression introduced by the use of SO_REUSEPORT, and that prevented the use of multiple different NFS versions to the same server. - TCP socket reconnection timer fixes. - Patch from Neil to disable the use of IPv6 temporary addresses" * tag 'nfs-for-4.8-2' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: NFSv4: Cap the transport reconnection timer at 1/2 lease period NFSv4: Cleanup the setting of the nfs4 lease period SUNRPC: Limit the reconnect backoff timer to the max RPC message timeout SUNRPC: Fix reconnection timeouts NFSv4.2: LAYOUTSTATS may return NFS4ERR_ADMIN/DELEG_REVOKED SUNRPC: disable the use of IPv6 temporary addresses. SUNRPC: allow for upcalls for same uid but different gss service SUNRPC: Fix up socket autodisconnect SUNRPC: Handle EADDRNOTAVAIL on connection failures
This commit is contained in:
commit
9909170065
@ -338,6 +338,8 @@ nfs42_layoutstat_done(struct rpc_task *task, void *calldata)
|
||||
case 0:
|
||||
break;
|
||||
case -NFS4ERR_EXPIRED:
|
||||
case -NFS4ERR_ADMIN_REVOKED:
|
||||
case -NFS4ERR_DELEG_REVOKED:
|
||||
case -NFS4ERR_STALE_STATEID:
|
||||
case -NFS4ERR_OLD_STATEID:
|
||||
case -NFS4ERR_BAD_STATEID:
|
||||
|
@ -396,6 +396,10 @@ extern void nfs4_schedule_state_renewal(struct nfs_client *);
|
||||
extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
|
||||
extern void nfs4_kill_renewd(struct nfs_client *);
|
||||
extern void nfs4_renew_state(struct work_struct *);
|
||||
extern void nfs4_set_lease_period(struct nfs_client *clp,
|
||||
unsigned long lease,
|
||||
unsigned long lastrenewed);
|
||||
|
||||
|
||||
/* nfs4state.c */
|
||||
struct rpc_cred *nfs4_get_clid_cred(struct nfs_client *clp);
|
||||
|
@ -4237,12 +4237,9 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str
|
||||
err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
|
||||
trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err);
|
||||
if (err == 0) {
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
|
||||
spin_lock(&clp->cl_lock);
|
||||
clp->cl_lease_time = fsinfo->lease_time * HZ;
|
||||
clp->cl_last_renewal = now;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
nfs4_set_lease_period(server->nfs_client,
|
||||
fsinfo->lease_time * HZ,
|
||||
now);
|
||||
break;
|
||||
}
|
||||
err = nfs4_handle_exception(server, err, &exception);
|
||||
|
@ -136,6 +136,26 @@ nfs4_kill_renewd(struct nfs_client *clp)
|
||||
cancel_delayed_work_sync(&clp->cl_renewd);
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs4_set_lease_period - Sets the lease period on a nfs_client
|
||||
*
|
||||
* @clp: pointer to nfs_client
|
||||
* @lease: new value for lease period
|
||||
* @lastrenewed: time at which lease was last renewed
|
||||
*/
|
||||
void nfs4_set_lease_period(struct nfs_client *clp,
|
||||
unsigned long lease,
|
||||
unsigned long lastrenewed)
|
||||
{
|
||||
spin_lock(&clp->cl_lock);
|
||||
clp->cl_lease_time = lease;
|
||||
clp->cl_last_renewal = lastrenewed;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
|
||||
/* Cap maximum reconnect timeout at 1/2 lease period */
|
||||
rpc_cap_max_reconnect_timeout(clp->cl_rpcclient, lease >> 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
|
@ -277,20 +277,17 @@ static int nfs41_setup_state_renewal(struct nfs_client *clp)
|
||||
{
|
||||
int status;
|
||||
struct nfs_fsinfo fsinfo;
|
||||
unsigned long now;
|
||||
|
||||
if (!test_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state)) {
|
||||
nfs4_schedule_state_renewal(clp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
now = jiffies;
|
||||
status = nfs4_proc_get_lease_time(clp, &fsinfo);
|
||||
if (status == 0) {
|
||||
/* Update lease time and schedule renewal */
|
||||
spin_lock(&clp->cl_lock);
|
||||
clp->cl_lease_time = fsinfo.lease_time * HZ;
|
||||
clp->cl_last_renewal = jiffies;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
|
||||
nfs4_set_lease_period(clp, fsinfo.lease_time * HZ, now);
|
||||
nfs4_schedule_state_renewal(clp);
|
||||
}
|
||||
|
||||
|
@ -195,6 +195,8 @@ int rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *,
|
||||
struct rpc_xprt *,
|
||||
void *),
|
||||
void *data);
|
||||
void rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
|
||||
unsigned long timeo);
|
||||
|
||||
const char *rpc_proc_name(const struct rpc_task *task);
|
||||
#endif /* __KERNEL__ */
|
||||
|
@ -218,7 +218,8 @@ struct rpc_xprt {
|
||||
struct work_struct task_cleanup;
|
||||
struct timer_list timer;
|
||||
unsigned long last_used,
|
||||
idle_timeout;
|
||||
idle_timeout,
|
||||
max_reconnect_timeout;
|
||||
|
||||
/*
|
||||
* Send stuff
|
||||
|
@ -340,12 +340,14 @@ gss_release_msg(struct gss_upcall_msg *gss_msg)
|
||||
}
|
||||
|
||||
static struct gss_upcall_msg *
|
||||
__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid)
|
||||
__gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth)
|
||||
{
|
||||
struct gss_upcall_msg *pos;
|
||||
list_for_each_entry(pos, &pipe->in_downcall, list) {
|
||||
if (!uid_eq(pos->uid, uid))
|
||||
continue;
|
||||
if (auth && pos->auth->service != auth->service)
|
||||
continue;
|
||||
atomic_inc(&pos->count);
|
||||
dprintk("RPC: %s found msg %p\n", __func__, pos);
|
||||
return pos;
|
||||
@ -365,7 +367,7 @@ gss_add_msg(struct gss_upcall_msg *gss_msg)
|
||||
struct gss_upcall_msg *old;
|
||||
|
||||
spin_lock(&pipe->lock);
|
||||
old = __gss_find_upcall(pipe, gss_msg->uid);
|
||||
old = __gss_find_upcall(pipe, gss_msg->uid, gss_msg->auth);
|
||||
if (old == NULL) {
|
||||
atomic_inc(&gss_msg->count);
|
||||
list_add(&gss_msg->list, &pipe->in_downcall);
|
||||
@ -714,7 +716,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
|
||||
err = -ENOENT;
|
||||
/* Find a matching upcall */
|
||||
spin_lock(&pipe->lock);
|
||||
gss_msg = __gss_find_upcall(pipe, uid);
|
||||
gss_msg = __gss_find_upcall(pipe, uid, NULL);
|
||||
if (gss_msg == NULL) {
|
||||
spin_unlock(&pipe->lock);
|
||||
goto err_put_ctx;
|
||||
|
@ -2638,6 +2638,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
|
||||
{
|
||||
struct rpc_xprt_switch *xps;
|
||||
struct rpc_xprt *xprt;
|
||||
unsigned long reconnect_timeout;
|
||||
unsigned char resvport;
|
||||
int ret = 0;
|
||||
|
||||
@ -2649,6 +2650,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
|
||||
return -EAGAIN;
|
||||
}
|
||||
resvport = xprt->resvport;
|
||||
reconnect_timeout = xprt->max_reconnect_timeout;
|
||||
rcu_read_unlock();
|
||||
|
||||
xprt = xprt_create_transport(xprtargs);
|
||||
@ -2657,6 +2659,7 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
|
||||
goto out_put_switch;
|
||||
}
|
||||
xprt->resvport = resvport;
|
||||
xprt->max_reconnect_timeout = reconnect_timeout;
|
||||
|
||||
rpc_xprt_switch_set_roundrobin(xps);
|
||||
if (setup) {
|
||||
@ -2673,6 +2676,27 @@ out_put_switch:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_clnt_add_xprt);
|
||||
|
||||
static int
|
||||
rpc_xprt_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
|
||||
struct rpc_xprt *xprt,
|
||||
void *data)
|
||||
{
|
||||
unsigned long timeout = *((unsigned long *)data);
|
||||
|
||||
if (timeout < xprt->max_reconnect_timeout)
|
||||
xprt->max_reconnect_timeout = timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo)
|
||||
{
|
||||
rpc_clnt_iterate_for_each_xprt(clnt,
|
||||
rpc_xprt_cap_max_reconnect_timeout,
|
||||
&timeo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
|
||||
static void rpc_show_header(void)
|
||||
{
|
||||
|
@ -680,6 +680,20 @@ out:
|
||||
spin_unlock_bh(&xprt->transport_lock);
|
||||
}
|
||||
|
||||
static bool
|
||||
xprt_has_timer(const struct rpc_xprt *xprt)
|
||||
{
|
||||
return xprt->idle_timeout != 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xprt_schedule_autodisconnect(struct rpc_xprt *xprt)
|
||||
__must_hold(&xprt->transport_lock)
|
||||
{
|
||||
if (list_empty(&xprt->recv) && xprt_has_timer(xprt))
|
||||
mod_timer(&xprt->timer, xprt->last_used + xprt->idle_timeout);
|
||||
}
|
||||
|
||||
static void
|
||||
xprt_init_autodisconnect(unsigned long data)
|
||||
{
|
||||
@ -688,6 +702,8 @@ xprt_init_autodisconnect(unsigned long data)
|
||||
spin_lock(&xprt->transport_lock);
|
||||
if (!list_empty(&xprt->recv))
|
||||
goto out_abort;
|
||||
/* Reset xprt->last_used to avoid connect/autodisconnect cycling */
|
||||
xprt->last_used = jiffies;
|
||||
if (test_and_set_bit(XPRT_LOCKED, &xprt->state))
|
||||
goto out_abort;
|
||||
spin_unlock(&xprt->transport_lock);
|
||||
@ -725,6 +741,7 @@ void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)
|
||||
goto out;
|
||||
xprt->snd_task =NULL;
|
||||
xprt->ops->release_xprt(xprt, NULL);
|
||||
xprt_schedule_autodisconnect(xprt);
|
||||
out:
|
||||
spin_unlock_bh(&xprt->transport_lock);
|
||||
wake_up_bit(&xprt->state, XPRT_LOCKED);
|
||||
@ -888,11 +905,6 @@ static void xprt_timer(struct rpc_task *task)
|
||||
spin_unlock_bh(&xprt->transport_lock);
|
||||
}
|
||||
|
||||
static inline int xprt_has_timer(struct rpc_xprt *xprt)
|
||||
{
|
||||
return xprt->idle_timeout != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xprt_prepare_transmit - reserve the transport before sending a request
|
||||
* @task: RPC task about to send a request
|
||||
@ -1280,9 +1292,7 @@ void xprt_release(struct rpc_task *task)
|
||||
if (!list_empty(&req->rq_list))
|
||||
list_del(&req->rq_list);
|
||||
xprt->last_used = jiffies;
|
||||
if (list_empty(&xprt->recv) && xprt_has_timer(xprt))
|
||||
mod_timer(&xprt->timer,
|
||||
xprt->last_used + xprt->idle_timeout);
|
||||
xprt_schedule_autodisconnect(xprt);
|
||||
spin_unlock_bh(&xprt->transport_lock);
|
||||
if (req->rq_buffer)
|
||||
xprt->ops->buf_free(req->rq_buffer);
|
||||
|
@ -177,7 +177,6 @@ static struct ctl_table sunrpc_table[] = {
|
||||
* increase over time if the server is down or not responding.
|
||||
*/
|
||||
#define XS_TCP_INIT_REEST_TO (3U * HZ)
|
||||
#define XS_TCP_MAX_REEST_TO (5U * 60 * HZ)
|
||||
|
||||
/*
|
||||
* TCP idle timeout; client drops the transport socket if it is idle
|
||||
@ -2173,6 +2172,8 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
||||
write_unlock_bh(&sk->sk_callback_lock);
|
||||
}
|
||||
xs_udp_do_set_buffer_size(xprt);
|
||||
|
||||
xprt->stat.connect_start = jiffies;
|
||||
}
|
||||
|
||||
static void xs_udp_setup_socket(struct work_struct *work)
|
||||
@ -2236,6 +2237,7 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
||||
unsigned int keepcnt = xprt->timeout->to_retries + 1;
|
||||
unsigned int opt_on = 1;
|
||||
unsigned int timeo;
|
||||
unsigned int addr_pref = IPV6_PREFER_SRC_PUBLIC;
|
||||
|
||||
/* TCP Keepalive options */
|
||||
kernel_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
|
||||
@ -2247,6 +2249,16 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
||||
kernel_setsockopt(sock, SOL_TCP, TCP_KEEPCNT,
|
||||
(char *)&keepcnt, sizeof(keepcnt));
|
||||
|
||||
/* Avoid temporary address, they are bad for long-lived
|
||||
* connections such as NFS mounts.
|
||||
* RFC4941, section 3.6 suggests that:
|
||||
* Individual applications, which have specific
|
||||
* knowledge about the normal duration of connections,
|
||||
* MAY override this as appropriate.
|
||||
*/
|
||||
kernel_setsockopt(sock, SOL_IPV6, IPV6_ADDR_PREFERENCES,
|
||||
(char *)&addr_pref, sizeof(addr_pref));
|
||||
|
||||
/* TCP user timeout (see RFC5482) */
|
||||
timeo = jiffies_to_msecs(xprt->timeout->to_initval) *
|
||||
(xprt->timeout->to_retries + 1);
|
||||
@ -2295,6 +2307,10 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
||||
/* SYN_SENT! */
|
||||
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
|
||||
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
||||
break;
|
||||
case -EADDRNOTAVAIL:
|
||||
/* Source port number is unavailable. Try a new one! */
|
||||
transport->srcport = 0;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
@ -2369,6 +2385,25 @@ out:
|
||||
xprt_wake_pending_tasks(xprt, status);
|
||||
}
|
||||
|
||||
static unsigned long xs_reconnect_delay(const struct rpc_xprt *xprt)
|
||||
{
|
||||
unsigned long start, now = jiffies;
|
||||
|
||||
start = xprt->stat.connect_start + xprt->reestablish_timeout;
|
||||
if (time_after(start, now))
|
||||
return start - now;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xs_reconnect_backoff(struct rpc_xprt *xprt)
|
||||
{
|
||||
xprt->reestablish_timeout <<= 1;
|
||||
if (xprt->reestablish_timeout > xprt->max_reconnect_timeout)
|
||||
xprt->reestablish_timeout = xprt->max_reconnect_timeout;
|
||||
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
|
||||
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
||||
}
|
||||
|
||||
/**
|
||||
* xs_connect - connect a socket to a remote endpoint
|
||||
* @xprt: pointer to transport structure
|
||||
@ -2386,6 +2421,7 @@ out:
|
||||
static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
|
||||
{
|
||||
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
||||
unsigned long delay = 0;
|
||||
|
||||
WARN_ON_ONCE(!xprt_lock_connect(xprt, task, transport));
|
||||
|
||||
@ -2397,19 +2433,15 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task)
|
||||
/* Start by resetting any existing state */
|
||||
xs_reset_transport(transport);
|
||||
|
||||
queue_delayed_work(xprtiod_workqueue,
|
||||
&transport->connect_worker,
|
||||
xprt->reestablish_timeout);
|
||||
xprt->reestablish_timeout <<= 1;
|
||||
if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
|
||||
xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
|
||||
if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO)
|
||||
xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO;
|
||||
} else {
|
||||
delay = xs_reconnect_delay(xprt);
|
||||
xs_reconnect_backoff(xprt);
|
||||
|
||||
} else
|
||||
dprintk("RPC: xs_connect scheduled xprt %p\n", xprt);
|
||||
queue_delayed_work(xprtiod_workqueue,
|
||||
&transport->connect_worker, 0);
|
||||
}
|
||||
|
||||
queue_delayed_work(xprtiod_workqueue,
|
||||
&transport->connect_worker,
|
||||
delay);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2961,6 +2993,8 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
|
||||
xprt->ops = &xs_tcp_ops;
|
||||
xprt->timeout = &xs_tcp_default_timeout;
|
||||
|
||||
xprt->max_reconnect_timeout = xprt->timeout->to_maxval;
|
||||
|
||||
INIT_WORK(&transport->recv_worker, xs_tcp_data_receive_workfn);
|
||||
INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_setup_socket);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user