1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-11-23 07:31:31 +00:00

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
This commit is contained in:
Rick Macklem 2020-04-22 21:00:14 +00:00
parent 87e9ade239
commit 897d7d45ba
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=360205
3 changed files with 103 additions and 32 deletions

View File

@ -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,

View File

@ -932,7 +932,7 @@ nfsrpc_openconfirm(vnode_t vp, u_int8_t *nfhp, int fhlen,
*/
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

View File

@ -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);
@ -2043,6 +2046,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".
*/
@ -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)) {