Skip to content

Commit

Permalink
Merge tag 'nfs-for-4.8-2' of git://git.linux-nfs.org/projects/trondmy…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
torvalds committed Aug 12, 2016
2 parents c239ae1 + 8d48032 commit 9909170
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 37 deletions.
2 changes: 2 additions & 0 deletions fs/nfs/nfs42proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 3 additions & 6 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions fs/nfs/nfs4renewd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 3 additions & 6 deletions fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 2 additions & 0 deletions include/linux/sunrpc/clnt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__ */
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 @@ -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
Expand Down
8 changes: 5 additions & 3 deletions net/sunrpc/auth_gss/auth_gss.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
24 changes: 24 additions & 0 deletions net/sunrpc/clnt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -2673,6 +2676,27 @@ int rpc_clnt_add_xprt(struct rpc_clnt *clnt,
}
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)
{
Expand Down
26 changes: 18 additions & 8 deletions net/sunrpc/xprt.c
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,20 @@ void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie)
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)
{
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
60 changes: 47 additions & 13 deletions net/sunrpc/xprtsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2369,6 +2385,25 @@ static void xs_tcp_setup_socket(struct work_struct *work)
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
Expand All @@ -2386,6 +2421,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)
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));

Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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);

Expand Down

0 comments on commit 9909170

Please sign in to comment.