summaryrefslogtreecommitdiffstats
path: root/sys/fs
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2017-04-25 11:36:25 +0000
committerrmacklem <rmacklem@FreeBSD.org>2017-04-25 11:36:25 +0000
commit2d266130827322bcf0294089f3cce89e1200294f (patch)
tree63cc880b742441f6d36e558bfc7f99cb145e78f8 /sys/fs
parent46363633335040e5c23b3a216f5996e320867460 (diff)
downloadFreeBSD-src-2d266130827322bcf0294089f3cce89e1200294f.zip
FreeBSD-src-2d266130827322bcf0294089f3cce89e1200294f.tar.gz
MFC: r310491
Fix NFSv4.1 client recovery from NFS4ERR_BAD_SESSION errors. For most NFSv4.1 servers, a NFS4ERR_BAD_SESSION error is a rare failure that indicates that the server has lost session/open/lock state. However, recent testing by cperciva@ against the AmazonEFS server found several problems with client recovery from this due to it generating this failure frequently. Briefly, the problems fixed are: - If all session slots were in use at the time of the failure, some processes would continue to loop waiting for a slot on the old session forever. - If an RPC that doesn't use open/lock state failed with NFS4ERR_BAD_SESSION, it would fail the RPC/syscall instead of initiating recovery and then looping to retry the RPC. - If a successful reply to an RPC for an old session wasn't processed until after a new session was created for a NFS4ERR_BAD_SESSION error, it would erroneously update the new session and corrupt it. - The use of the first element of the session list in the nfs mount structure (which is always the current metadata session) was slightly racey. With changes for the above problems it became more racey, so all uses of this head pointer was wrapped with a NFSLOCKMNT()/NFSUNLOCKMNT(). - Although the kernel malloc() usually allocates more bytes than requested and, as such, this wouldn't have caused problems, the allocation of a session structure was 1 byte smaller than it should have been. (Null termination byte for the string not included in byte count.) There are probably still problems with a pNFS data server that fails with NFS4ERR_BAD_SESSION, but I have no server that does this to test against (the AmazonEFS server doesn't do pNFS), so I can't fix these yet. Although this patch is fairly large, it should only affect the handling of NFS4ERR_BAD_SESSION error replies from an NFSv4.1 server. Thanks go to cperciva@ for the extension testing he did to help isolate/fix these problems.
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nfs/nfs.h3
-rw-r--r--sys/fs/nfs/nfs_commonkrpc.c113
-rw-r--r--sys/fs/nfs/nfs_commonport.c3
-rw-r--r--sys/fs/nfs/nfs_commonsubs.c158
-rw-r--r--sys/fs/nfs/nfsclstate.h1
-rw-r--r--sys/fs/nfsclient/nfs_clcomsubs.c11
-rw-r--r--sys/fs/nfsclient/nfs_clport.c4
-rw-r--r--sys/fs/nfsclient/nfs_clrpcops.c164
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c70
-rw-r--r--sys/fs/nfsclient/nfs_clvfsops.c1
-rw-r--r--sys/fs/nfsclient/nfsmount.h13
11 files changed, 360 insertions, 181 deletions
diff --git a/sys/fs/nfs/nfs.h b/sys/fs/nfs/nfs.h
index 5781459..066b6a7 100644
--- a/sys/fs/nfs/nfs.h
+++ b/sys/fs/nfs/nfs.h
@@ -601,6 +601,7 @@ struct nfsrv_descript {
uint8_t nd_sessionid[NFSX_V4SESSIONID]; /* Session id */
uint32_t nd_slotid; /* Slotid for this RPC */
SVCXPRT *nd_xprt; /* Server RPC handle */
+ uint32_t *nd_sequence; /* Sequence Op. ptr */
};
#define nd_princlen nd_gssnamelen
@@ -636,6 +637,7 @@ struct nfsrv_descript {
#define ND_HASSEQUENCE 0x04000000
#define ND_CACHETHIS 0x08000000
#define ND_LASTOP 0x10000000
+#define ND_LOOPBADSESS 0x20000000
/*
* ND_GSS should be the "or" of all GSS type authentications.
@@ -649,6 +651,7 @@ struct nfsv4_opflag {
int modifyfs;
int lktype;
int needsseq;
+ int loopbadsess;
};
/*
diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c
index 1c0189f..3e98c83e 100644
--- a/sys/fs/nfs/nfs_commonkrpc.c
+++ b/sys/fs/nfs/nfs_commonkrpc.c
@@ -90,6 +90,7 @@ uint32_t nfscl_nfs4_done_probes[NFSV41_NPROCS + 1];
NFSSTATESPINLOCK;
NFSREQSPINLOCK;
NFSDLOCKMUTEX;
+NFSCLSTATEMUTEX;
extern struct nfsstats newnfsstats;
extern struct nfsreqhead nfsd_reqq;
extern int nfscl_ticks;
@@ -474,13 +475,13 @@ int
newnfs_request(struct nfsrv_descript *nd, struct nfsmount *nmp,
struct nfsclient *clp, struct nfssockreq *nrp, vnode_t vp,
struct thread *td, struct ucred *cred, u_int32_t prog, u_int32_t vers,
- u_char *retsum, int toplevel, u_int64_t *xidp, struct nfsclsession *sep)
+ u_char *retsum, int toplevel, u_int64_t *xidp, struct nfsclsession *dssep)
{
- u_int32_t retseq, retval, *tl;
+ uint32_t retseq, retval, slotseq, *tl;
time_t waituntil;
int i = 0, j = 0, opcnt, set_sigset = 0, slot;
int trycnt, error = 0, usegssname = 0, secflavour = AUTH_SYS;
- int freeslot, timeo;
+ int freeslot, maxslot, reterr, slotpos, timeo;
u_int16_t procnum;
u_int trylater_delay = 1;
struct nfs_feedback_arg nf;
@@ -492,7 +493,10 @@ newnfs_request(struct nfsrv_descript *nd, struct nfsmount *nmp,
char *srv_principal = NULL, *clnt_principal = NULL;
sigset_t oldset;
struct ucred *authcred;
+ struct nfsclsession *sep;
+ uint8_t sessionid[NFSX_V4SESSIONID];
+ sep = dssep;
if (xidp != NULL)
*xidp = 0;
/* Reject requests while attempting a forced unmount. */
@@ -804,7 +808,7 @@ tryagain:
nd->nd_procnum != NFSV4PROC_CBNULL) {
/* If sep == NULL, set it to the default in nmp. */
if (sep == NULL && nmp != NULL)
- sep = NFSMNT_MDSSESSION(nmp);
+ sep = nfsmnt_mdssession(nmp);
/*
* and now the actual NFS xdr.
*/
@@ -848,18 +852,25 @@ tryagain:
NFSX_V4SESSIONID +
4 * NFSX_UNSIGNED);
mtx_lock(&sep->nfsess_mtx);
- tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
- retseq = fxdr_unsigned(uint32_t, *tl++);
- slot = fxdr_unsigned(int, *tl++);
- freeslot = slot;
- if (retseq != sep->nfsess_slotseq[slot])
- printf("retseq diff 0x%x\n", retseq);
- retval = fxdr_unsigned(uint32_t, *++tl);
- if ((retval + 1) < sep->nfsess_foreslots)
- sep->nfsess_foreslots = (retval + 1);
- else if ((retval + 1) > sep->nfsess_foreslots)
- sep->nfsess_foreslots = (retval < 64) ?
- (retval + 1) : 64;
+ if (bcmp(tl, sep->nfsess_sessionid,
+ NFSX_V4SESSIONID) == 0) {
+ tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
+ retseq = fxdr_unsigned(uint32_t, *tl++);
+ slot = fxdr_unsigned(int, *tl++);
+ freeslot = slot;
+ if (retseq != sep->nfsess_slotseq[slot])
+ printf("retseq diff 0x%x\n",
+ retseq);
+ retval = fxdr_unsigned(uint32_t, *++tl);
+ if ((retval + 1) < sep->nfsess_foreslots
+ )
+ sep->nfsess_foreslots = (retval
+ + 1);
+ else if ((retval + 1) >
+ sep->nfsess_foreslots)
+ sep->nfsess_foreslots = (retval
+ < 64) ? (retval + 1) : 64;
+ }
mtx_unlock(&sep->nfsess_mtx);
/* Grab the op and status for the next one. */
@@ -872,6 +883,76 @@ tryagain:
}
}
if (nd->nd_repstat != 0) {
+ if (nd->nd_repstat == NFSERR_BADSESSION &&
+ nmp != NULL && dssep == NULL) {
+ /*
+ * If this is a client side MDS RPC, mark
+ * the MDS session defunct and initiate
+ * recovery, as required.
+ * The nfsess_defunct field is protected by
+ * the NFSLOCKMNT()/nm_mtx lock and not the
+ * nfsess_mtx lock to simplify its handling,
+ * for the MDS session. This lock is also
+ * sufficient for nfsess_sessionid, since it
+ * never changes in the structure.
+ */
+ NFSCL_DEBUG(1, "Got badsession\n");
+ NFSLOCKCLSTATE();
+ NFSLOCKMNT(nmp);
+ sep = NFSMNT_MDSSESSION(nmp);
+ if (bcmp(sep->nfsess_sessionid, nd->nd_sequence,
+ NFSX_V4SESSIONID) == 0) {
+ /* Initiate recovery. */
+ sep->nfsess_defunct = 1;
+ NFSCL_DEBUG(1, "Marked defunct\n");
+ if (nmp->nm_clp != NULL) {
+ nmp->nm_clp->nfsc_flags |=
+ NFSCLFLAGS_RECOVER;
+ wakeup(nmp->nm_clp);
+ }
+ }
+ NFSUNLOCKCLSTATE();
+ /*
+ * Sleep for up to 1sec waiting for a new
+ * session.
+ */
+ mtx_sleep(&nmp->nm_sess, &nmp->nm_mtx, PZERO,
+ "nfsbadsess", hz);
+ /*
+ * Get the session again, in case a new one
+ * has been created during the sleep.
+ */
+ sep = NFSMNT_MDSSESSION(nmp);
+ NFSUNLOCKMNT(nmp);
+ if ((nd->nd_flag & ND_LOOPBADSESS) != 0) {
+ reterr = nfsv4_sequencelookup(nmp, sep,
+ &slotpos, &maxslot, &slotseq,
+ sessionid);
+ if (reterr == 0) {
+ /* Fill in new session info. */
+ NFSCL_DEBUG(1,
+ "Filling in new sequence\n");
+ tl = nd->nd_sequence;
+ bcopy(sessionid, tl,
+ NFSX_V4SESSIONID);
+ tl += NFSX_V4SESSIONID /
+ NFSX_UNSIGNED;
+ *tl++ = txdr_unsigned(slotseq);
+ *tl++ = txdr_unsigned(slotpos);
+ *tl = txdr_unsigned(maxslot);
+ }
+ if (reterr == NFSERR_BADSESSION ||
+ reterr == 0) {
+ NFSCL_DEBUG(1,
+ "Badsession looping\n");
+ m_freem(nd->nd_mrep);
+ nd->nd_mrep = NULL;
+ goto tryagain;
+ }
+ nd->nd_repstat = reterr;
+ NFSCL_DEBUG(1, "Got err=%d\n", reterr);
+ }
+ }
if (((nd->nd_repstat == NFSERR_DELAY ||
nd->nd_repstat == NFSERR_GRACE) &&
(nd->nd_flag & ND_NFSV4) &&
diff --git a/sys/fs/nfs/nfs_commonport.c b/sys/fs/nfs/nfs_commonport.c
index 556ef42..76ce5de3 100644
--- a/sys/fs/nfs/nfs_commonport.c
+++ b/sys/fs/nfs/nfs_commonport.c
@@ -129,6 +129,7 @@ struct mtx nfs_state_mutex;
struct mtx nfs_nameid_mutex;
struct mtx nfs_req_mutex;
struct mtx nfs_slock_mutex;
+struct mtx nfs_clstate_mutex;
/* local functions */
static int nfssvc_call(struct thread *, struct nfssvc_args *, struct ucred *);
@@ -565,6 +566,7 @@ newnfs_portinit(void)
/* Initialize SMP locks used by both client and server. */
mtx_init(&newnfsd_mtx, "newnfsd_mtx", NULL, MTX_DEF);
mtx_init(&nfs_state_mutex, "nfs_state_mutex", NULL, MTX_DEF);
+ mtx_init(&nfs_clstate_mutex, "nfs_clstate_mutex", NULL, MTX_DEF);
}
/*
@@ -630,6 +632,7 @@ nfscommon_modevent(module_t mod, int type, void *data)
mtx_destroy(&nfs_nameid_mutex);
mtx_destroy(&newnfsd_mtx);
mtx_destroy(&nfs_state_mutex);
+ mtx_destroy(&nfs_clstate_mutex);
mtx_destroy(&nfs_sockl_mutex);
mtx_destroy(&nfs_slock_mutex);
mtx_destroy(&nfs_req_mutex);
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 60615cd..3ada3ea 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -90,65 +90,65 @@ extern int nfsrv_lughashsize;
* Define it here, since it is used by both the client and server.
*/
struct nfsv4_opflag nfsv4_opflag[NFSV41_NOPS] = {
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* undef */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* undef */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* undef */
- { 0, 1, 0, 0, LK_SHARED, 1 }, /* Access */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Close */
- { 0, 2, 0, 1, LK_EXCLUSIVE, 1 }, /* Commit */
- { 1, 2, 1, 1, LK_EXCLUSIVE, 1 }, /* Create */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Delegpurge */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Delegreturn */
- { 0, 1, 0, 0, LK_SHARED, 1 }, /* Getattr */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* GetFH */
- { 2, 1, 1, 1, LK_EXCLUSIVE, 1 }, /* Link */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Lock */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* LockT */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* LockU */
- { 1, 2, 0, 0, LK_EXCLUSIVE, 1 }, /* Lookup */
- { 1, 2, 0, 0, LK_EXCLUSIVE, 1 }, /* Lookupp */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* NVerify */
- { 1, 1, 0, 1, LK_EXCLUSIVE, 1 }, /* Open */
- { 1, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* OpenAttr */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* OpenConfirm */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* OpenDowngrade */
- { 1, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* PutFH */
- { 1, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* PutPubFH */
- { 1, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* PutRootFH */
- { 0, 1, 0, 0, LK_SHARED, 1 }, /* Read */
- { 0, 1, 0, 0, LK_SHARED, 1 }, /* Readdir */
- { 0, 1, 0, 0, LK_SHARED, 1 }, /* ReadLink */
- { 0, 2, 1, 1, LK_EXCLUSIVE, 1 }, /* Remove */
- { 2, 1, 1, 1, LK_EXCLUSIVE, 1 }, /* Rename */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Renew */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* RestoreFH */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* SaveFH */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* SecInfo */
- { 0, 2, 1, 1, LK_EXCLUSIVE, 1 }, /* Setattr */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* SetClientID */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* SetClientIDConfirm */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Verify */
- { 0, 2, 1, 1, LK_EXCLUSIVE, 1 }, /* Write */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* ReleaseLockOwner */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Backchannel Ctrl */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Bind Conn to Sess */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 0 }, /* Exchange ID */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 0 }, /* Create Session */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 0 }, /* Destroy Session */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Free StateID */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Get Dir Deleg */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Get Device Info */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Get Device List */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Layout Commit */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Layout Get */
- { 0, 1, 0, 0, LK_EXCLUSIVE, 1 }, /* Layout Return */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Secinfo No name */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Sequence */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Set SSV */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Test StateID */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Want Delegation */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 0 }, /* Destroy ClientID */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1 }, /* Reclaim Complete */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* undef */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* undef */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* undef */
+ { 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Access */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Close */
+ { 0, 2, 0, 1, LK_EXCLUSIVE, 1, 1 }, /* Commit */
+ { 1, 2, 1, 1, LK_EXCLUSIVE, 1, 1 }, /* Create */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Delegpurge */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Delegreturn */
+ { 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Getattr */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* GetFH */
+ { 2, 1, 1, 1, LK_EXCLUSIVE, 1, 1 }, /* Link */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Lock */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* LockT */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* LockU */
+ { 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Lookup */
+ { 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Lookupp */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* NVerify */
+ { 1, 1, 0, 1, LK_EXCLUSIVE, 1, 0 }, /* Open */
+ { 1, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* OpenAttr */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* OpenConfirm */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* OpenDowngrade */
+ { 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* PutFH */
+ { 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* PutPubFH */
+ { 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* PutRootFH */
+ { 0, 1, 0, 0, LK_SHARED, 1, 0 }, /* Read */
+ { 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Readdir */
+ { 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* ReadLink */
+ { 0, 2, 1, 1, LK_EXCLUSIVE, 1, 1 }, /* Remove */
+ { 2, 1, 1, 1, LK_EXCLUSIVE, 1, 1 }, /* Rename */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Renew */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* RestoreFH */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* SaveFH */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* SecInfo */
+ { 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 }, /* Setattr */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* SetClientID */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* SetClientIDConfirm */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Verify */
+ { 0, 2, 1, 1, LK_EXCLUSIVE, 1, 0 }, /* Write */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* ReleaseLockOwner */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Backchannel Ctrl */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Bind Conn to Sess */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 }, /* Exchange ID */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 }, /* Create Session */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 }, /* Destroy Session */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Free StateID */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Get Dir Deleg */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Get Device Info */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Get Device List */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Layout Commit */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Layout Get */
+ { 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Layout Return */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Secinfo No name */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 0 }, /* Sequence */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Set SSV */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Test StateID */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Want Delegation */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 0, 0 }, /* Destroy ClientID */
+ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Reclaim Complete */
};
#endif /* !APPLEKEXT */
@@ -4122,22 +4122,35 @@ nfsv4_setsequence(struct nfsmount *nmp, struct nfsrv_descript *nd,
error = nfsv4_sequencelookup(nmp, sep, &slotpos, &maxslot, &slotseq,
sessionid);
- if (error != 0)
- return;
- KASSERT(maxslot >= 0, ("nfscl_setsequence neg maxslot"));
/* Build the Sequence arguments. */
NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID + 4 * NFSX_UNSIGNED);
+ nd->nd_sequence = tl;
bcopy(sessionid, tl, NFSX_V4SESSIONID);
tl += NFSX_V4SESSIONID / NFSX_UNSIGNED;
nd->nd_slotseq = tl;
- *tl++ = txdr_unsigned(slotseq);
- *tl++ = txdr_unsigned(slotpos);
- *tl++ = txdr_unsigned(maxslot);
- if (dont_replycache == 0)
- *tl = newnfs_true;
- else
- *tl = newnfs_false;
+ if (error == 0) {
+ *tl++ = txdr_unsigned(slotseq);
+ *tl++ = txdr_unsigned(slotpos);
+ *tl++ = txdr_unsigned(maxslot);
+ if (dont_replycache == 0)
+ *tl = newnfs_true;
+ else
+ *tl = newnfs_false;
+ } else {
+ /*
+ * There are two errors and the rest of the session can
+ * just be zeros.
+ * NFSERR_BADSESSION: This bad session should just generate
+ * the same error again when the RPC is retried.
+ * ESTALE: A forced dismount is in progress and will cause the
+ * RPC to fail later.
+ */
+ *tl++ = 0;
+ *tl++ = 0;
+ *tl++ = 0;
+ *tl = 0;
+ }
nd->nd_flag |= ND_HASSEQUENCE;
}
@@ -4153,6 +4166,13 @@ nfsv4_sequencelookup(struct nfsmount *nmp, struct nfsclsession *sep,
maxslot = -1;
mtx_lock(&sep->nfsess_mtx);
do {
+ if (nmp != NULL && sep->nfsess_defunct != 0) {
+ /* Just return the bad session. */
+ bcopy(sep->nfsess_sessionid, sessionid,
+ NFSX_V4SESSIONID);
+ mtx_unlock(&sep->nfsess_mtx);
+ return (NFSERR_BADSESSION);
+ }
bitval = 1;
for (i = 0; i < sep->nfsess_foreslots; i++) {
if ((bitval & sep->nfsess_slots) == 0) {
diff --git a/sys/fs/nfs/nfsclstate.h b/sys/fs/nfs/nfsclstate.h
index c66d68a..ffc50ea 100644
--- a/sys/fs/nfs/nfsclstate.h
+++ b/sys/fs/nfs/nfsclstate.h
@@ -65,6 +65,7 @@ struct nfsclsession {
uint16_t nfsess_foreslots;
uint16_t nfsess_backslots;
uint8_t nfsess_sessionid[NFSX_V4SESSIONID];
+ uint8_t nfsess_defunct; /* Non-zero for old sessions */
};
/*
diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c
index 1fc7d1b..2e53fa0 100644
--- a/sys/fs/nfsclient/nfs_clcomsubs.c
+++ b/sys/fs/nfsclient/nfs_clcomsubs.c
@@ -200,13 +200,16 @@ nfscl_reqstart(struct nfsrv_descript *nd, int procnum, struct nfsmount *nmp,
*tl = txdr_unsigned(opcnt);
if ((nd->nd_flag & ND_NFSV41) != 0 &&
nfsv4_opflag[nfsv4_opmap[procnum].op].needsseq > 0) {
+ if (nfsv4_opflag[nfsv4_opmap[procnum].op].loopbadsess >
+ 0)
+ nd->nd_flag |= ND_LOOPBADSESS;
NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
*tl = txdr_unsigned(NFSV4OP_SEQUENCE);
- if (sep == NULL)
- nfsv4_setsequence(nmp, nd,
- NFSMNT_MDSSESSION(nmp),
+ if (sep == NULL) {
+ sep = nfsmnt_mdssession(nmp);
+ nfsv4_setsequence(nmp, nd, sep,
nfs_bigreply[procnum]);
- else
+ } else
nfsv4_setsequence(nmp, nd, sep,
nfs_bigreply[procnum]);
}
diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c
index 68d33f0..333c237 100644
--- a/sys/fs/nfsclient/nfs_clport.c
+++ b/sys/fs/nfsclient/nfs_clport.c
@@ -78,7 +78,6 @@ extern short nfsv4_cbport;
extern int nfscl_enablecallb;
extern int nfs_numnfscbd;
extern int nfscl_inited;
-struct mtx nfs_clstate_mutex;
struct mtx ncl_iod_mutex;
NFSDLOCKMUTEX;
@@ -1310,8 +1309,6 @@ nfscl_modevent(module_t mod, int type, void *data)
if (loaded)
return (0);
newnfs_portinit();
- mtx_init(&nfs_clstate_mutex, "nfs_clstate_mutex", NULL,
- MTX_DEF);
mtx_init(&ncl_iod_mutex, "ncl_iod_mutex", NULL, MTX_DEF);
nfscl_init();
NFSD_LOCK();
@@ -1335,7 +1332,6 @@ nfscl_modevent(module_t mod, int type, void *data)
ncl_call_invalcaches = NULL;
nfsd_call_nfscl = NULL;
/* and get rid of the mutexes */
- mtx_destroy(&nfs_clstate_mutex);
mtx_destroy(&ncl_iod_mutex);
loaded = 0;
break;
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 642d184..0950b06 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -384,6 +384,7 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
u_int32_t rflags, deleg;
nfsattrbit_t attrbits;
int error, ret, acesize, limitby;
+ struct nfsclsession *tsep;
dp = *dpp;
*dpp = NULL;
@@ -392,8 +393,9 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
*tl++ = txdr_unsigned(op->nfso_own->nfsow_seqid);
*tl++ = txdr_unsigned(mode & NFSV4OPEN_ACCESSBOTH);
*tl++ = txdr_unsigned((mode >> NFSLCK_SHIFT) & NFSV4OPEN_DENYBOTH);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
(void) nfsm_strtom(nd, op->nfso_own->nfsow_owner, NFSV4CL_LOCKNAMELEN);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OPEN_NOCREATE);
@@ -555,7 +557,7 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
}
if (nd->nd_repstat != 0 && error == 0)
error = nd->nd_repstat;
- if (error == NFSERR_STALECLIENTID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALECLIENTID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
if (!error)
@@ -602,7 +604,7 @@ nfsrpc_opendowngrade(vnode_t vp, u_int32_t mode, struct nfsclopen *op,
}
if (nd->nd_repstat && error == 0)
error = nd->nd_repstat;
- if (error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
@@ -760,7 +762,7 @@ nfsrpc_closerpc(struct nfsrv_descript *nd, struct nfsmount *nmp,
if (nd->nd_repstat == 0)
NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
error = nd->nd_repstat;
- if (error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
@@ -801,7 +803,7 @@ nfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
op->nfso_stateid.other[2] = *tl;
}
error = nd->nd_repstat;
- if (error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(op->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
@@ -826,32 +828,52 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
nfsquad_t confirm;
u_int32_t lease;
static u_int32_t rev = 0;
- struct nfsclds *dsp, *ndsp, *tdsp;
+ struct nfsclds *dsp;
+ struct nfsclsession *tsep;
if (nfsboottime.tv_sec == 0)
NFSSETBOOTTIME(nfsboottime);
clp->nfsc_rev = rev++;
if (NFSHASNFSV4N(nmp)) {
+ /*
+ * Either there was no previous session or the
+ * previous session has failed, so...
+ * do an ExchangeID followed by the CreateSession.
+ */
error = nfsrpc_exchangeid(nmp, clp, &nmp->nm_sockreq,
NFSV4EXCH_USEPNFSMDS | NFSV4EXCH_USENONPNFS, &dsp, cred, p);
NFSCL_DEBUG(1, "aft exch=%d\n", error);
- if (error == 0) {
+ if (error == 0)
error = nfsrpc_createsession(nmp, &dsp->nfsclds_sess,
&nmp->nm_sockreq,
dsp->nfsclds_sess.nfsess_sequenceid, 1, cred, p);
- if (error == 0) {
- NFSLOCKMNT(nmp);
- TAILQ_FOREACH_SAFE(tdsp, &nmp->nm_sess,
- nfsclds_list, ndsp)
- nfscl_freenfsclds(tdsp);
- TAILQ_INIT(&nmp->nm_sess);
- TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp,
- nfsclds_list);
- NFSUNLOCKMNT(nmp);
- } else
- nfscl_freenfsclds(dsp);
- NFSCL_DEBUG(1, "aft createsess=%d\n", error);
- }
+ if (error == 0) {
+ NFSLOCKMNT(nmp);
+ /*
+ * The old sessions cannot be safely free'd
+ * here, since they may still be used by
+ * in-progress RPCs.
+ */
+ tsep = NULL;
+ if (TAILQ_FIRST(&nmp->nm_sess) != NULL)
+ tsep = NFSMNT_MDSSESSION(nmp);
+ TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp,
+ nfsclds_list);
+ /*
+ * Wake up RPCs waiting for a slot on the
+ * old session. These will then fail with
+ * NFSERR_BADSESSION and be retried with the
+ * new session by nfsv4_setsequence().
+ * Also wakeup() processes waiting for the
+ * new session.
+ */
+ if (tsep != NULL)
+ wakeup(&tsep->nfsess_slots);
+ wakeup(&nmp->nm_sess);
+ NFSUNLOCKMNT(nmp);
+ } else
+ nfscl_freenfsclds(dsp);
+ NFSCL_DEBUG(1, "aft createsess=%d\n", error);
if (error == 0 && reclaim == 0) {
error = nfsrpc_reclaimcomplete(nmp, cred, p);
NFSCL_DEBUG(1, "aft reclaimcomp=%d\n", error);
@@ -872,6 +894,7 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
mtx_init(&dsp->nfsclds_sess.nfsess_mtx, "nfssession", NULL, MTX_DEF);
NFSLOCKMNT(nmp);
TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp, nfsclds_list);
+ tsep = NFSMNT_MDSSESSION(nmp);
NFSUNLOCKMNT(nmp);
nfscl_reqstart(nd, NFSPROC_SETCLIENTID, nmp, NULL, 0, NULL, NULL);
@@ -933,8 +956,8 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
return (error);
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
- NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0] = *tl++;
- NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1] = *tl++;
+ tsep->nfsess_clientid.lval[0] = *tl++;
+ tsep->nfsess_clientid.lval[1] = *tl++;
confirm.lval[0] = *tl++;
confirm.lval[1] = *tl;
mbuf_freem(nd->nd_mrep);
@@ -946,8 +969,8 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
nfscl_reqstart(nd, NFSPROC_SETCLIENTIDCFRM, nmp, NULL, 0, NULL,
NULL);
NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl++ = tsep->nfsess_clientid.lval[1];
*tl++ = confirm.lval[0];
*tl = confirm.lval[1];
nd->nd_flag |= ND_USEGSSNAME;
@@ -1108,7 +1131,7 @@ nfsrpc_setattr(vnode_t vp, struct vattr *vap, NFSACL_T *aclp,
else
error = nfsrpc_setaclrpc(vp, cred, p, aclp, &stateid,
stuff);
- if (error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
@@ -1365,7 +1388,7 @@ nfsrpc_read(vnode_t vp, struct uio *uiop, struct ucred *cred,
&lckp);
error = nfsrpc_readrpc(vp, uiop, newcred, &stateid, p, nap,
attrflagp, stuff);
- if (error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
@@ -1535,7 +1558,7 @@ nfsrpc_write(vnode_t vp, struct uio *uiop, int *iomode, int *must_commit,
else
error = nfsrpc_writerpc(vp, uiop, iomode, must_commit,
newcred, &stateid, p, nap, attrflagp, stuff);
- if (error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALESTATEID)
nfscl_initiate_recovery(nmp->nm_clp);
if (lckp != NULL)
nfscl_lockderef(lckp);
@@ -1961,6 +1984,7 @@ nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
nfsv4stateid_t stateid;
u_int32_t rflags;
struct nfsmount *nmp;
+ struct nfsclsession *tsep;
nmp = VFSTONFS(dvp->v_mount);
np = VTONFS(dvp);
@@ -1980,8 +2004,9 @@ nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
*tl++ = txdr_unsigned(NFSV4OPEN_ACCESSWRITE |
NFSV4OPEN_ACCESSREAD);
*tl++ = txdr_unsigned(NFSV4OPEN_DENYNONE);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
(void) nfsm_strtom(nd, owp->nfsow_owner, NFSV4CL_LOCKNAMELEN);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(NFSV4OPEN_CREATE);
@@ -2175,7 +2200,7 @@ nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
}
if (nd->nd_repstat != 0 && error == 0)
error = nd->nd_repstat;
- if (error == NFSERR_STALECLIENTID || error == NFSERR_BADSESSION)
+ if (error == NFSERR_STALECLIENTID)
nfscl_initiate_recovery(owp->nfsow_clp);
nfsmout:
if (!error)
@@ -3837,6 +3862,7 @@ nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
struct nfsnode *np;
struct nfsmount *nmp;
+ struct nfsclsession *tsep;
nmp = VFSTONFS(vp->v_mount);
NFSCL_REQSTART(nd, NFSPROC_LOCKT, vp);
@@ -3849,8 +3875,9 @@ nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
tl += 2;
txdr_hyper(len, tl);
tl += 2;
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
nfscl_filllockowner(id, own, flags);
np = VTONFS(vp);
NFSBCOPY(np->n_fhp->nfh_fh, &own[NFSV4CL_LOCKNAMELEN],
@@ -3890,8 +3917,7 @@ nfsrpc_lockt(struct nfsrv_descript *nd, vnode_t vp,
error = EBADRPC;
if (!error)
error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
- } else if (nd->nd_repstat == NFSERR_STALECLIENTID ||
- nd->nd_repstat == NFSERR_BADSESSION)
+ } else if (nd->nd_repstat == NFSERR_STALECLIENTID)
nfscl_initiate_recovery(clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
@@ -3941,8 +3967,7 @@ nfsrpc_locku(struct nfsrv_descript *nd, struct nfsmount *nmp,
lp->nfsl_stateid.other[0] = *tl++;
lp->nfsl_stateid.other[1] = *tl++;
lp->nfsl_stateid.other[2] = *tl;
- } else if (nd->nd_repstat == NFSERR_STALESTATEID ||
- nd->nd_repstat == NFSERR_BADSESSION)
+ } else if (nd->nd_repstat == NFSERR_STALESTATEID)
nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
@@ -3961,6 +3986,7 @@ nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
u_int32_t *tl;
int error, size;
uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
+ struct nfsclsession *tsep;
nfscl_reqstart(nd, NFSPROC_LOCK, nmp, nfhp, fhlen, NULL, NULL);
NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
@@ -3986,8 +4012,9 @@ nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
*tl++ = lp->nfsl_open->nfso_stateid.other[1];
*tl++ = lp->nfsl_open->nfso_stateid.other[2];
*tl++ = txdr_unsigned(lp->nfsl_seqid);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
NFSBCOPY(nfhp, &own[NFSV4CL_LOCKNAMELEN], fhlen);
(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
@@ -4028,8 +4055,7 @@ nfsrpc_lock(struct nfsrv_descript *nd, struct nfsmount *nmp, vnode_t vp,
error = EBADRPC;
if (!error)
error = nfsm_advance(nd, NFSM_RNDUP(size), -1);
- } else if (nd->nd_repstat == NFSERR_STALESTATEID ||
- nd->nd_repstat == NFSERR_BADSESSION)
+ } else if (nd->nd_repstat == NFSERR_STALESTATEID)
nfscl_initiate_recovery(lp->nfsl_open->nfso_own->nfsow_clp);
nfsmout:
mbuf_freem(nd->nd_mrep);
@@ -4229,25 +4255,36 @@ nfsrpc_renew(struct nfsclclient *clp, struct nfsclds *dsp, struct ucred *cred,
struct nfsmount *nmp;
int error;
struct nfssockreq *nrp;
+ struct nfsclsession *tsep;
nmp = clp->nfsc_nmp;
if (nmp == NULL)
return (0);
- nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL,
- &dsp->nfsclds_sess);
+ if (dsp == NULL)
+ nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL, NULL);
+ else
+ nfscl_reqstart(nd, NFSPROC_RENEW, nmp, NULL, 0, NULL,
+ &dsp->nfsclds_sess);
if (!NFSHASNFSV4N(nmp)) {
/* NFSv4.1 just uses a Sequence Op and not a Renew. */
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
}
- nrp = dsp->nfsclds_sockp;
+ nrp = NULL;
+ if (dsp != NULL)
+ nrp = dsp->nfsclds_sockp;
if (nrp == NULL)
/* If NULL, use the MDS socket. */
nrp = &nmp->nm_sockreq;
nd->nd_flag |= ND_USEGSSNAME;
- error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
- NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
+ if (dsp == NULL)
+ error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
+ NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
+ else
+ error = newnfs_request(nd, nmp, NULL, nrp, NULL, p, cred,
+ NFS_PROG, NFS_VER4, NULL, 1, NULL, &dsp->nfsclds_sess);
if (error)
return (error);
error = nd->nd_repstat;
@@ -4266,6 +4303,7 @@ nfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
u_int32_t *tl;
int error;
uint8_t own[NFSV4CL_LOCKNAMELEN + NFSX_V4FHMAX];
+ struct nfsclsession *tsep;
if (NFSHASNFSV4N(nmp)) {
/* For NFSv4.1, do a FreeStateID. */
@@ -4276,8 +4314,9 @@ nfsrpc_rellockown(struct nfsmount *nmp, struct nfscllockowner *lp,
nfscl_reqstart(nd, NFSPROC_RELEASELCKOWN, nmp, NULL, 0, NULL,
NULL);
NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
NFSBCOPY(lp->nfsl_owner, own, NFSV4CL_LOCKNAMELEN);
NFSBCOPY(fh, &own[NFSV4CL_LOCKNAMELEN], fhlen);
(void)nfsm_strtom(nd, own, NFSV4CL_LOCKNAMELEN + fhlen);
@@ -4515,7 +4554,7 @@ nfsrpc_exchangeid(struct nfsmount *nmp, struct nfsclclient *clp,
error = NFSERR_BADXDR;
goto nfsmout;
}
- dsp = malloc(sizeof(struct nfsclds) + len, M_NFSCLDS,
+ dsp = malloc(sizeof(struct nfsclds) + len + 1, M_NFSCLDS,
M_WAITOK | M_ZERO);
dsp->nfsclds_expire = NFSD_MONOSEC + clp->nfsc_renew;
dsp->nfsclds_servownlen = len;
@@ -4658,10 +4697,12 @@ nfsrpc_destroysession(struct nfsmount *nmp, struct nfsclclient *clp,
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
int error;
+ struct nfsclsession *tsep;
nfscl_reqstart(nd, NFSPROC_DESTROYSESSION, nmp, NULL, 0, NULL, NULL);
NFSM_BUILD(tl, uint32_t *, NFSX_V4SESSIONID);
- bcopy(NFSMNT_MDSSESSION(nmp)->nfsess_sessionid, tl, NFSX_V4SESSIONID);
+ tsep = nfsmnt_mdssession(nmp);
+ bcopy(tsep->nfsess_sessionid, tl, NFSX_V4SESSIONID);
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
@@ -4683,11 +4724,13 @@ nfsrpc_destroyclient(struct nfsmount *nmp, struct nfsclclient *clp,
struct nfsrv_descript nfsd;
struct nfsrv_descript *nd = &nfsd;
int error;
+ struct nfsclsession *tsep;
nfscl_reqstart(nd, NFSPROC_DESTROYCLIENT, nmp, NULL, 0, NULL, NULL);
NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
- *tl++ = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[0];
- *tl = NFSMNT_MDSSESSION(nmp)->nfsess_clientid.lval[1];
+ tsep = nfsmnt_mdssession(nmp);
+ *tl++ = tsep->nfsess_clientid.lval[0];
+ *tl = tsep->nfsess_clientid.lval[1];
nd->nd_flag |= ND_USEGSSNAME;
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
@@ -5155,6 +5198,7 @@ nfsrpc_getlayout(struct nfsmount *nmp, vnode_t vp, struct nfsfh *nfhp,
struct nfsclflayouthead flh;
int error = 0, islocked, layoutlen, recalled, retonclose;
nfsv4stateid_t stateid;
+ struct nfsclsession *tsep;
*lypp = NULL;
/*
@@ -5169,7 +5213,8 @@ nfsrpc_getlayout(struct nfsmount *nmp, vnode_t vp, struct nfsfh *nfhp,
if (recalled != 0)
return (EIO);
LIST_INIT(&flh);
- layoutlen = NFSMNT_MDSSESSION(nmp)->nfsess_maxcache -
+ tsep = nfsmnt_mdssession(nmp);
+ layoutlen = tsep->nfsess_maxcache -
(NFSX_STATEID + 3 * NFSX_UNSIGNED);
if (lyp == NULL) {
stateid.seqid = 0;
@@ -5266,7 +5311,8 @@ nfsrpc_fillsa(struct nfsmount *nmp, struct sockaddr_storage *ssp,
if (msad != NULL && msad->sin_family == AF_INET &&
ssd->sin_addr.s_addr == msad->sin_addr.s_addr &&
ssd->sin_port == msad->sin_port &&
- (tdsp->nfsclds_flags & NFSCLDS_DS) != 0) {
+ (tdsp->nfsclds_flags & NFSCLDS_DS) != 0 &&
+ tdsp->nfsclds_sess.nfsess_defunct == 0) {
*dspp = tdsp;
NFSUNLOCKMNT(nmp);
NFSCL_DEBUG(4, "fnd same addr\n");
@@ -5306,7 +5352,8 @@ nfsrpc_fillsa(struct nfsmount *nmp, struct sockaddr_storage *ssp,
IN6_ARE_ADDR_EQUAL(&ssd6->sin6_addr,
&msad6->sin6_addr) &&
ssd6->sin6_port == msad6->sin6_port &&
- (tdsp->nfsclds_flags & NFSCLDS_DS) != 0) {
+ (tdsp->nfsclds_flags & NFSCLDS_DS) != 0 &&
+ tdsp->nfsclds_sess.nfsess_defunct == 0) {
*dspp = tdsp;
NFSUNLOCKMNT(nmp);
return (0);
@@ -5859,7 +5906,8 @@ nfscl_getsameserver(struct nfsmount *nmp, struct nfsclds *newdsp,
if (dsp->nfsclds_servownlen == newdsp->nfsclds_servownlen &&
dsp->nfsclds_servownlen != 0 &&
!NFSBCMP(dsp->nfsclds_serverown, newdsp->nfsclds_serverown,
- dsp->nfsclds_servownlen)) {
+ dsp->nfsclds_servownlen) &&
+ dsp->nfsclds_sess.nfsess_defunct == 0) {
NFSCL_DEBUG(4, "fnd same fdsp=%p dsp=%p flg=0x%x\n",
TAILQ_FIRST(&nmp->nm_sess), dsp,
dsp->nfsclds_flags);
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 2600b80..8c28fff 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -2470,8 +2470,11 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
if (recover_done_time < NFSD_MONOSEC) {
recover_done_time = NFSD_MONOSEC +
clp->nfsc_renew;
+ NFSCL_DEBUG(1, "Doing recovery..\n");
nfscl_recover(clp, cred, p);
} else {
+ NFSCL_DEBUG(1, "Clear Recovery dt=%u ms=%jd\n",
+ recover_done_time, (intmax_t)NFSD_MONOSEC);
NFSLOCKCLSTATE();
clp->nfsc_flags &= ~NFSCLFLAGS_RECOVER;
NFSUNLOCKCLSTATE();
@@ -2481,8 +2484,7 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {
clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
clidrev = clp->nfsc_clientidrev;
- error = nfsrpc_renew(clp,
- TAILQ_FIRST(&clp->nfsc_nmp->nm_sess), cred, p);
+ error = nfsrpc_renew(clp, NULL, cred, p);
if (error == NFSERR_CBPATHDOWN)
cbpathdown = 1;
else if (error == NFSERR_STALECLIENTID ||
@@ -2494,24 +2496,27 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
(void) nfscl_hasexpired(clp, clidrev, p);
}
- /* Do renews for any DS sessions. */
checkdsrenew:
- NFSLOCKMNT(clp->nfsc_nmp);
- /* Skip first entry, since the MDS is handled above. */
- dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
- if (dsp != NULL)
- dsp = TAILQ_NEXT(dsp, nfsclds_list);
- while (dsp != NULL) {
- if (dsp->nfsclds_expire <= NFSD_MONOSEC) {
- dsp->nfsclds_expire = NFSD_MONOSEC +
- clp->nfsc_renew;
- NFSUNLOCKMNT(clp->nfsc_nmp);
- (void)nfsrpc_renew(clp, dsp, cred, p);
- goto checkdsrenew;
+ if (NFSHASNFSV4N(clp->nfsc_nmp)) {
+ /* Do renews for any DS sessions. */
+ NFSLOCKMNT(clp->nfsc_nmp);
+ /* Skip first entry, since the MDS is handled above. */
+ dsp = TAILQ_FIRST(&clp->nfsc_nmp->nm_sess);
+ if (dsp != NULL)
+ dsp = TAILQ_NEXT(dsp, nfsclds_list);
+ while (dsp != NULL) {
+ if (dsp->nfsclds_expire <= NFSD_MONOSEC &&
+ dsp->nfsclds_sess.nfsess_defunct == 0) {
+ dsp->nfsclds_expire = NFSD_MONOSEC +
+ clp->nfsc_renew;
+ NFSUNLOCKMNT(clp->nfsc_nmp);
+ (void)nfsrpc_renew(clp, dsp, cred, p);
+ goto checkdsrenew;
+ }
+ dsp = TAILQ_NEXT(dsp, nfsclds_list);
}
- dsp = TAILQ_NEXT(dsp, nfsclds_list);
+ NFSUNLOCKMNT(clp->nfsc_nmp);
}
- NFSUNLOCKMNT(clp->nfsc_nmp);
TAILQ_INIT(&dh);
NFSLOCKCLSTATE();
@@ -3163,6 +3168,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
int changed, gotone, laytype, recalltype;
uint32_t iomode;
struct nfsclrecalllayout *recallp = NULL;
+ struct nfsclsession *tsep;
gotseq_ok = 0;
nfsrvd_rephead(nd);
@@ -3472,13 +3478,12 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
error = NFSERR_SERVERFAULT;
} else
error = NFSERR_SEQUENCEPOS;
- if (error == 0)
+ if (error == 0) {
+ tsep = nfsmnt_mdssession(clp->nfsc_nmp);
error = nfsv4_seqsession(seqid, slotid,
- highslot,
- NFSMNT_MDSSESSION(clp->nfsc_nmp)->
- nfsess_cbslots, &rep,
- NFSMNT_MDSSESSION(clp->nfsc_nmp)->
- nfsess_backslots);
+ highslot, tsep->nfsess_cbslots, &rep,
+ tsep->nfsess_backslots);
+ }
NFSUNLOCKCLSTATE();
if (error == 0) {
gotseq_ok = 1;
@@ -3546,8 +3551,8 @@ out:
NFSLOCKCLSTATE();
clp = nfscl_getclntsess(sessionid);
if (clp != NULL) {
- nfsv4_seqsess_cacherep(slotid,
- NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_cbslots,
+ tsep = nfsmnt_mdssession(clp->nfsc_nmp);
+ nfsv4_seqsess_cacherep(slotid, tsep->nfsess_cbslots,
NFSERR_OK, &rep);
NFSUNLOCKCLSTATE();
} else {
@@ -3603,15 +3608,17 @@ nfscl_getmnt(int minorvers, uint8_t *sessionid, u_int32_t cbident,
struct nfsclclient *clp;
mount_t mp;
int error;
+ struct nfsclsession *tsep;
*clpp = NULL;
NFSLOCKCLSTATE();
LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
+ tsep = nfsmnt_mdssession(clp->nfsc_nmp);
if (minorvers == NFSV4_MINORVERSION) {
if (clp->nfsc_cbident == cbident)
break;
- } else if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->
- nfsess_sessionid, sessionid, NFSX_V4SESSIONID))
+ } else if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
+ NFSX_V4SESSIONID))
break;
}
if (clp == NULL) {
@@ -3650,11 +3657,14 @@ static struct nfsclclient *
nfscl_getclntsess(uint8_t *sessionid)
{
struct nfsclclient *clp;
+ struct nfsclsession *tsep;
- LIST_FOREACH(clp, &nfsclhead, nfsc_list)
- if (!NFSBCMP(NFSMNT_MDSSESSION(clp->nfsc_nmp)->nfsess_sessionid,
- sessionid, NFSX_V4SESSIONID))
+ LIST_FOREACH(clp, &nfsclhead, nfsc_list) {
+ tsep = nfsmnt_mdssession(clp->nfsc_nmp);
+ if (!NFSBCMP(tsep->nfsess_sessionid, sessionid,
+ NFSX_V4SESSIONID))
break;
+ }
return (clp);
}
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 67beb76..61e9792a 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -1271,6 +1271,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
krbnamelen + dirlen + srvkrbnamelen + 2,
M_NEWNFSMNT, M_WAITOK | M_ZERO);
TAILQ_INIT(&nmp->nm_bufq);
+ TAILQ_INIT(&nmp->nm_sess);
if (clval == 0)
clval = (u_int64_t)nfsboottime.tv_sec;
nmp->nm_clval = clval++;
diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h
index f5b56bf..9eb2c10 100644
--- a/sys/fs/nfsclient/nfsmount.h
+++ b/sys/fs/nfsclient/nfsmount.h
@@ -112,9 +112,22 @@ struct nfsmount {
/*
* Get a pointer to the MDS session, which is always the first element
* in the list.
+ * This macro can only be safely used when the NFSLOCKMNT() lock is held.
+ * The inline function can be used when the lock isn't held.
*/
#define NFSMNT_MDSSESSION(m) (&(TAILQ_FIRST(&((m)->nm_sess))->nfsclds_sess))
+static __inline struct nfsclsession *
+nfsmnt_mdssession(struct nfsmount *nmp)
+{
+ struct nfsclsession *tsep;
+
+ mtx_lock(&nmp->nm_mtx);
+ tsep = NFSMNT_MDSSESSION(nmp);
+ mtx_unlock(&nmp->nm_mtx);
+ return (tsep);
+}
+
#ifndef NFS_DEFAULT_NAMETIMEO
#define NFS_DEFAULT_NAMETIMEO 60
#endif
OpenPOWER on IntegriCloud