summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2020-04-22 21:00:14 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2020-04-22 21:00:14 +0000
commit897d7d45bac80abe2c23f8d5140b70809152fe0e (patch)
tree4d782e4c0688cdd39b03b67086a9a2dbdb324ec7 /sys
parent87e9ade239dc42783c41b69077bbb9c75ab0702a (diff)
downloadsrc-test2-897d7d45bac80abe2c23f8d5140b70809152fe0e.tar.gz
src-test2-897d7d45bac80abe2c23f8d5140b70809152fe0e.zip
Make the NFSv4.n client's recovery from NFSERR_BADSESSION RFC5661 conformant.
RFC5661 specifies that a client's recovery upon receipt of NFSERR_BADSESSION should first consist of a CreateSession operation using the extant ClientID. If that fails, then a full recovery beginning with the ExchangeID operation is to be done. Without this patch, the FreeBSD client did not attempt the CreateSession operation with the extant ClientID and went directly to a full recovery beginning with ExchangeID. I have had this patch several years, but since no extant NFSv4.n server required the CreateSession with extant ClientID, I have never committed it. I an committing it now, since I suspect some future NFSv4.n server will require this and it should not negatively impact recovery for extant NFSv4.n servers, since they should all return NFSERR_STATECLIENTID for this first CreateSession. The patched client has been tested for recovery against both the FreeBSD and Linux NFSv4.n servers and no problems have been observed. MFC after: 1 month
Notes
Notes: svn path=/head/; revision=360205
Diffstat (limited to 'sys')
-rw-r--r--sys/fs/nfs/nfs_var.h2
-rw-r--r--sys/fs/nfsclient/nfs_clrpcops.c86
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c47
3 files changed, 103 insertions, 32 deletions
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index b98e4e5b5cb1..e1371e9d113b 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -454,7 +454,7 @@ int nfsrpc_closerpc(struct nfsrv_descript *, struct nfsmount *,
int nfsrpc_openconfirm(vnode_t, u_int8_t *, int, struct nfsclopen *,
struct ucred *, NFSPROC_T *);
int nfsrpc_setclient(struct nfsmount *, struct nfsclclient *, int,
- struct ucred *, NFSPROC_T *);
+ bool *, struct ucred *, NFSPROC_T *);
int nfsrpc_getattr(vnode_t, struct ucred *, NFSPROC_T *,
struct nfsvattr *, void *);
int nfsrpc_getattrnovp(struct nfsmount *, u_int8_t *, int, int,
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 37da2a46570f..76c7b6ccf73a 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -932,7 +932,7 @@ nfsmout:
*/
APPLESTATIC int
nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
- struct ucred *cred, NFSPROC_T *p)
+ bool *retokp, struct ucred *cred, NFSPROC_T *p)
{
u_int32_t *tl;
struct nfsrv_descript nfsd;
@@ -944,26 +944,81 @@ 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;
+ struct nfsclds *dsp, *odsp;
struct in6_addr a6;
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, 0,
- NFSV4EXCH_USEPNFSMDS | NFSV4EXCH_USENONPNFS, &dsp, cred, p);
- NFSCL_DEBUG(1, "aft exch=%d\n", error);
- if (error == 0)
+ error = NFSERR_BADSESSION;
+ odsp = dsp = NULL;
+ if (retokp != NULL) {
+ NFSLOCKMNT(nmp);
+ odsp = TAILQ_FIRST(&nmp->nm_sess);
+ NFSUNLOCKMNT(nmp);
+ }
+ if (odsp != NULL) {
+ /*
+ * When a session already exists, first try a
+ * CreateSession with the extant ClientID.
+ */
+ dsp = malloc(sizeof(struct nfsclds) +
+ odsp->nfsclds_servownlen + 1, M_NFSCLDS,
+ M_WAITOK | M_ZERO);
+ dsp->nfsclds_expire = NFSD_MONOSEC + clp->nfsc_renew;
+ dsp->nfsclds_servownlen = odsp->nfsclds_servownlen;
+ dsp->nfsclds_sess.nfsess_clientid =
+ odsp->nfsclds_sess.nfsess_clientid;
+ dsp->nfsclds_sess.nfsess_sequenceid =
+ odsp->nfsclds_sess.nfsess_sequenceid;
+ dsp->nfsclds_flags = odsp->nfsclds_flags;
+ if (dsp->nfsclds_servownlen > 0)
+ memcpy(dsp->nfsclds_serverown,
+ odsp->nfsclds_serverown,
+ dsp->nfsclds_servownlen + 1);
+ mtx_init(&dsp->nfsclds_mtx, "nfsds", NULL, MTX_DEF);
+ mtx_init(&dsp->nfsclds_sess.nfsess_mtx, "nfssession",
+ NULL, MTX_DEF);
+ nfscl_initsessionslots(&dsp->nfsclds_sess);
error = nfsrpc_createsession(nmp, &dsp->nfsclds_sess,
&nmp->nm_sockreq, NULL,
dsp->nfsclds_sess.nfsess_sequenceid, 1, cred, p);
+ NFSCL_DEBUG(1, "create session for extant "
+ "ClientID=%d\n", error);
+ if (error != 0) {
+ nfscl_freenfsclds(dsp);
+ dsp = NULL;
+ /*
+ * If *retokp is true, return any error other
+ * than NFSERR_STALECLIENTID,
+ * NFSERR_BADSESSION or NFSERR_STALEDONTRECOVER
+ * so that nfscl_recover() will not loop.
+ */
+ if (*retokp)
+ return (NFSERR_IO);
+ } else
+ *retokp = true;
+ } else if (retokp != NULL && *retokp)
+ return (NFSERR_IO);
+ if (error != 0) {
+ /*
+ * Either there was no previous session or the
+ * CreateSession attempt failed, so...
+ * do an ExchangeID followed by the CreateSession.
+ */
+ clp->nfsc_rev = rev++;
+ error = nfsrpc_exchangeid(nmp, clp, &nmp->nm_sockreq, 0,
+ NFSV4EXCH_USEPNFSMDS | NFSV4EXCH_USENONPNFS, &dsp,
+ cred, p);
+ NFSCL_DEBUG(1, "aft exch=%d\n", error);
+ if (error == 0)
+ error = nfsrpc_createsession(nmp,
+ &dsp->nfsclds_sess, &nmp->nm_sockreq, NULL,
+ dsp->nfsclds_sess.nfsess_sequenceid, 1,
+ cred, p);
+ NFSCL_DEBUG(1, "aft createsess=%d\n", error);
+ }
if (error == 0) {
NFSLOCKMNT(nmp);
/*
@@ -988,9 +1043,8 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
wakeup(&tsep->nfsess_slots);
wakeup(&nmp->nm_sess);
NFSUNLOCKMNT(nmp);
- } else
+ } else if (dsp != NULL)
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);
@@ -1000,7 +1054,9 @@ nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim,
error = 0;
}
return (error);
- }
+ } else if (retokp != NULL && *retokp)
+ return (NFSERR_IO);
+ clp->nfsc_rev = rev++;
/*
* Allocate a single session structure for NFSv4.0, because some of
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 445cd379b95d..d69a9fcde882 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -110,7 +110,8 @@ static void nfscl_expireclient(struct nfsclclient *, struct nfsmount *,
struct ucred *, NFSPROC_T *);
static int nfscl_expireopen(struct nfsclclient *, struct nfsclopen *,
struct nfsmount *, struct ucred *, NFSPROC_T *);
-static void nfscl_recover(struct nfsclclient *, struct ucred *, NFSPROC_T *);
+static void nfscl_recover(struct nfsclclient *, bool *, struct ucred *,
+ NFSPROC_T *);
static void nfscl_insertlock(struct nfscllockowner *, struct nfscllock *,
struct nfscllock *, int);
static int nfscl_updatelock(struct nfscllockowner *, struct nfscllock **,
@@ -907,7 +908,7 @@ nfscl_getcl(struct mount *mp, struct ucred *cred, NFSPROC_T *p,
clidinusedelay = 120;
trystalecnt = 3;
do {
- error = nfsrpc_setclient(nmp, clp, 0, cred, p);
+ error = nfsrpc_setclient(nmp, clp, 0, NULL, cred, p);
if (error == NFSERR_STALECLIENTID ||
error == NFSERR_STALEDONTRECOVER ||
error == NFSERR_BADSESSION ||
@@ -1950,7 +1951,7 @@ nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
(void)nfsrpc_destroysession(nmp, clp, cred, p);
(void)nfsrpc_destroyclient(nmp, clp, cred, p);
} else
- (void)nfsrpc_setclient(nmp, clp, 0, cred, p);
+ (void)nfsrpc_setclient(nmp, clp, 0, NULL, cred, p);
nfscl_cleanclient(clp);
nmp->nm_clp = NULL;
NFSFREECRED(cred);
@@ -1966,7 +1967,8 @@ nfscl_umount(struct nfsmount *nmp, NFSPROC_T *p)
* corresponding state.
*/
static void
-nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
+nfscl_recover(struct nfsclclient *clp, bool *retokp, struct ucred *cred,
+ NFSPROC_T *p)
{
struct nfsclowner *owp, *nowp;
struct nfsclopen *op, *nop;
@@ -2010,8 +2012,9 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
LIST_INIT(&clp->nfsc_layouthash[i]);
trycnt = 5;
+ tcred = NULL;
do {
- error = nfsrpc_setclient(nmp, clp, 1, cred, p);
+ error = nfsrpc_setclient(nmp, clp, 1, retokp, cred, p);
} while ((error == NFSERR_STALECLIENTID ||
error == NFSERR_BADSESSION ||
error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
@@ -2044,6 +2047,13 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
NFSUNLOCKREQ();
/*
+ * If nfsrpc_setclient() returns *retokp == true,
+ * no more recovery is needed.
+ */
+ if (*retokp)
+ goto out;
+
+ /*
* Now, mark all delegations "need reclaim".
*/
TAILQ_FOREACH(dp, &clp->nfsc_deleg, nfsdl_list)
@@ -2276,12 +2286,14 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
if (NFSHASNFSV4N(nmp))
(void)nfsrpc_reclaimcomplete(nmp, cred, p);
+out:
NFSLOCKCLSTATE();
clp->nfsc_flags &= ~NFSCLFLAGS_RECVRINPROG;
wakeup(&clp->nfsc_flags);
nfsv4_unlock(&clp->nfsc_lock, 0);
NFSUNLOCKCLSTATE();
- NFSFREECRED(tcred);
+ if (tcred != NULL)
+ NFSFREECRED(tcred);
}
/*
@@ -2330,7 +2342,7 @@ nfscl_hasexpired(struct nfsclclient *clp, u_int32_t clidrev, NFSPROC_T *p)
cred = newnfs_getcred();
trycnt = 5;
do {
- error = nfsrpc_setclient(nmp, clp, 0, cred, p);
+ error = nfsrpc_setclient(nmp, clp, 0, NULL, cred, p);
} while ((error == NFSERR_STALECLIENTID ||
error == NFSERR_BADSESSION ||
error == NFSERR_STALEDONTRECOVER) && --trycnt > 0);
@@ -2539,6 +2551,7 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
struct nfscllayouthead rlh;
struct nfsclrecalllayout *recallp;
struct nfsclds *dsp;
+ bool retok;
cred = newnfs_getcred();
NFSLOCKCLSTATE();
@@ -2549,21 +2562,23 @@ nfscl_renewthread(struct nfsclclient *clp, NFSPROC_T *p)
cbpathdown = 0;
if (clp->nfsc_flags & NFSCLFLAGS_RECOVER) {
/*
- * Only allow one recover within 1/2 of the lease
+ * Only allow one full recover within 1/2 of the lease
* duration (nfsc_renew).
+ * retok is value/result. If passed in set to true,
+ * it indicates only a CreateSession operation should
+ * be attempted.
+ * If it is returned true, it indicates that the
+ * recovery only required a CreateSession.
*/
+ retok = true;
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();
+ retok = false;
}
+ NFSCL_DEBUG(1, "Doing recovery, only "
+ "createsession=%d\n", retok);
+ nfscl_recover(clp, &retok, cred, p);
}
if (clp->nfsc_expire <= NFSD_MONOSEC &&
(clp->nfsc_flags & NFSCLFLAGS_HASCLIENTID)) {