NFSv4.1: Don't loop forever in nfs4_proc_create_session

If a server for some reason keeps sending NFS4ERR_DELAY errors, we can end
up looping forever inside nfs4_proc_create_session, and so the usual
mechanisms for detecting if the nfs_client is dead don't work.

Fix this by ensuring that we loop inside the nfs4_state_manager thread
instead.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Trond Myklebust 2011-04-24 14:28:18 -04:00
parent fb8a5ba811
commit fd954ae124
4 changed files with 40 additions and 53 deletions

View File

@ -47,6 +47,7 @@ enum nfs4_client_state {
NFS4CLNT_LAYOUTRECALL, NFS4CLNT_LAYOUTRECALL,
NFS4CLNT_SESSION_RESET, NFS4CLNT_SESSION_RESET,
NFS4CLNT_RECALL_SLOT, NFS4CLNT_RECALL_SLOT,
NFS4CLNT_LEASE_CONFIRM,
}; };
enum nfs4_session_state { enum nfs4_session_state {

View File

@ -3754,18 +3754,17 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
if (status != -NFS4ERR_CLID_INUSE) if (status != -NFS4ERR_CLID_INUSE)
break; break;
if (signalled()) if (loop != 0) {
++clp->cl_id_uniquifier;
break; break;
if (loop++ & 1) }
ssleep(clp->cl_lease_time / HZ + 1); ++loop;
else ssleep(clp->cl_lease_time / HZ + 1);
if (++clp->cl_id_uniquifier == 0)
break;
} }
return status; return status;
} }
static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
struct nfs4_setclientid_res *arg, struct nfs4_setclientid_res *arg,
struct rpc_cred *cred) struct rpc_cred *cred)
{ {
@ -3790,26 +3789,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp,
return status; return status;
} }
int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
struct nfs4_setclientid_res *arg,
struct rpc_cred *cred)
{
long timeout = 0;
int err;
do {
err = _nfs4_proc_setclientid_confirm(clp, arg, cred);
switch (err) {
case 0:
return err;
case -NFS4ERR_RESOURCE:
/* The IBM lawyers misread another document! */
case -NFS4ERR_DELAY:
err = nfs4_delay(clp->cl_rpcclient, &timeout);
}
} while (err == 0);
return err;
}
struct nfs4_delegreturndata { struct nfs4_delegreturndata {
struct nfs4_delegreturnargs args; struct nfs4_delegreturnargs args;
struct nfs4_delegreturnres res; struct nfs4_delegreturnres res;
@ -5222,20 +5201,10 @@ int nfs4_proc_create_session(struct nfs_client *clp)
int status; int status;
unsigned *ptr; unsigned *ptr;
struct nfs4_session *session = clp->cl_session; struct nfs4_session *session = clp->cl_session;
long timeout = 0;
int err;
dprintk("--> %s clp=%p session=%p\n", __func__, clp, session); dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);
do { status = _nfs4_proc_create_session(clp);
status = _nfs4_proc_create_session(clp);
if (status == -NFS4ERR_DELAY) {
err = nfs4_delay(clp->cl_rpcclient, &timeout);
if (err)
status = err;
}
} while (status == -NFS4ERR_DELAY);
if (status) if (status)
goto out; goto out;

View File

@ -64,10 +64,15 @@ static LIST_HEAD(nfs4_clientid_list);
int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{ {
struct nfs4_setclientid_res clid; struct nfs4_setclientid_res clid = {
.clientid = clp->cl_clientid,
.confirm = clp->cl_confirm,
};
unsigned short port; unsigned short port;
int status; int status;
if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
goto do_confirm;
port = nfs_callback_tcpport; port = nfs_callback_tcpport;
if (clp->cl_addr.ss_family == AF_INET6) if (clp->cl_addr.ss_family == AF_INET6)
port = nfs_callback_tcpport6; port = nfs_callback_tcpport6;
@ -75,10 +80,14 @@ int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
if (status != 0) if (status != 0)
goto out; goto out;
clp->cl_clientid = clid.clientid;
clp->cl_confirm = clid.confirm;
set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
do_confirm:
status = nfs4_proc_setclientid_confirm(clp, &clid, cred); status = nfs4_proc_setclientid_confirm(clp, &clid, cred);
if (status != 0) if (status != 0)
goto out; goto out;
clp->cl_clientid = clid.clientid; clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
nfs4_schedule_state_renewal(clp); nfs4_schedule_state_renewal(clp);
out: out:
return status; return status;
@ -230,13 +239,18 @@ int nfs41_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
{ {
int status; int status;
if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state))
goto do_confirm;
nfs4_begin_drain_session(clp); nfs4_begin_drain_session(clp);
status = nfs4_proc_exchange_id(clp, cred); status = nfs4_proc_exchange_id(clp, cred);
if (status != 0) if (status != 0)
goto out; goto out;
set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
do_confirm:
status = nfs4_proc_create_session(clp); status = nfs4_proc_create_session(clp);
if (status != 0) if (status != 0)
goto out; goto out;
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
nfs41_setup_state_renewal(clp); nfs41_setup_state_renewal(clp);
nfs_mark_client_ready(clp, NFS_CS_READY); nfs_mark_client_ready(clp, NFS_CS_READY);
out: out:
@ -1584,20 +1598,22 @@ static int nfs4_recall_slot(struct nfs_client *clp) { return 0; }
*/ */
static void nfs4_set_lease_expired(struct nfs_client *clp, int status) static void nfs4_set_lease_expired(struct nfs_client *clp, int status)
{ {
if (nfs4_has_session(clp)) { switch (status) {
switch (status) { case -NFS4ERR_CLID_INUSE:
case -NFS4ERR_DELAY: case -NFS4ERR_STALE_CLIENTID:
case -NFS4ERR_CLID_INUSE: clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
case -EAGAIN: break;
break; case -NFS4ERR_DELAY:
case -EAGAIN:
ssleep(1);
break;
case -EKEYEXPIRED: case -EKEYEXPIRED:
nfs4_warn_keyexpired(clp->cl_hostname); nfs4_warn_keyexpired(clp->cl_hostname);
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
* in nfs4_exchange_id */ * in nfs4_exchange_id */
default: default:
return; return;
}
} }
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
} }

View File

@ -47,6 +47,7 @@ struct nfs_client {
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
u64 cl_clientid; /* constant */ u64 cl_clientid; /* constant */
nfs4_verifier cl_confirm; /* Clientid verifier */
unsigned long cl_state; unsigned long cl_state;
spinlock_t cl_lock; spinlock_t cl_lock;