--- fs/nfs/nfs_var.h.orig 2017-05-26 17:26:36.190031000 -0400 +++ fs/nfs/nfs_var.h 2017-05-26 17:27:05.894831000 -0400 @@ -401,7 +401,7 @@ int nfsrpc_closerpc(struct nfsrv_descrip 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 *); + struct ucred *, NFSPROC_T *, int *); int nfsrpc_getattr(vnode_t, struct ucred *, NFSPROC_T *, struct nfsvattr *, void *); int nfsrpc_getattrnovp(struct nfsmount *, u_int8_t *, int, int, --- fs/nfsclient/nfs_clstate.c.orig 2017-05-26 17:26:51.520438000 -0400 +++ fs/nfsclient/nfs_clstate.c 2017-05-26 17:27:05.915325000 -0400 @@ -903,7 +903,7 @@ nfscl_getcl(struct mount *mp, struct ucr clidinusedelay = 120; trystalecnt = 3; do { - error = nfsrpc_setclient(nmp, clp, 0, cred, p); + error = nfsrpc_setclient(nmp, clp, 0, cred, p, NULL); if (error == NFSERR_STALECLIENTID || error == NFSERR_STALEDONTRECOVER || error == NFSERR_BADSESSION || @@ -1933,7 +1933,7 @@ nfscl_umount(struct nfsmount *nmp, NFSPR (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, cred, p, NULL); nfscl_cleanclient(clp); nmp->nm_clp = NULL; NFSFREECRED(cred); @@ -1963,7 +1963,7 @@ nfscl_recover(struct nfsclclient *clp, s struct nfsreq *rep; u_int64_t len; u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode; - int i, igotlock = 0, error, trycnt, firstlock; + int i, igotlock = 0, error, trycnt, firstlock, retok; struct nfscllayout *lyp, *nlyp; /* @@ -1993,8 +1993,10 @@ nfscl_recover(struct nfsclclient *clp, s LIST_INIT(&clp->nfsc_layouthash[i]); trycnt = 5; + retok = 0; + tcred = NULL; do { - error = nfsrpc_setclient(nmp, clp, 1, cred, p); + error = nfsrpc_setclient(nmp, clp, 1, cred, p, &retok); } while ((error == NFSERR_STALECLIENTID || error == NFSERR_BADSESSION || error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); @@ -2026,6 +2028,10 @@ nfscl_recover(struct nfsclclient *clp, s } NFSUNLOCKREQ(); + /* If nfsrpc_setclient() sets retok != 0, no more recovery is needed. */ + if (retok != 0) + goto out; + /* * Now, mark all delegations "need reclaim". */ @@ -2259,12 +2265,14 @@ nfscl_recover(struct nfsclclient *clp, s 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); } /* @@ -2313,7 +2321,7 @@ nfscl_hasexpired(struct nfsclclient *clp cred = newnfs_getcred(); trycnt = 5; do { - error = nfsrpc_setclient(nmp, clp, 0, cred, p); + error = nfsrpc_setclient(nmp, clp, 0, cred, p, NULL); } while ((error == NFSERR_STALECLIENTID || error == NFSERR_BADSESSION || error == NFSERR_STALEDONTRECOVER) && --trycnt > 0); --- fs/nfsclient/nfs_clrpcops.c.orig 2017-05-26 17:26:51.522899000 -0400 +++ fs/nfsclient/nfs_clrpcops.c 2017-05-26 17:27:05.930762000 -0400 @@ -818,7 +818,7 @@ nfsmout: */ APPLESTATIC int nfsrpc_setclient(struct nfsmount *nmp, struct nfsclclient *clp, int reclaim, - struct ucred *cred, NFSPROC_T *p) + struct ucred *cred, NFSPROC_T *p, int *retokp) { u_int32_t *tl; struct nfsrv_descript nfsd; @@ -830,26 +830,72 @@ nfsrpc_setclient(struct nfsmount *nmp, s 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 (retokp != NULL) + *retokp = 0; 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) + error = NFSERR_BADSESSION; + dsp = 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) + bcopy(odsp->nfsclds_serverown, + dsp->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, 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; + } else if (retokp != NULL) + *retokp = 1; + } + if (error != 0) { + /* + * Either there was no previous session or the + * CreateSession attempt 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) + error = nfsrpc_createsession(nmp, + &dsp->nfsclds_sess, &nmp->nm_sockreq, + dsp->nfsclds_sess.nfsess_sequenceid, 1, + cred, p); + NFSCL_DEBUG(1, "aft createsess=%d\n", error); + } if (error == 0) { NFSLOCKMNT(nmp); /* @@ -860,8 +906,7 @@ nfsrpc_setclient(struct nfsmount *nmp, s tsep = NULL; if (TAILQ_FIRST(&nmp->nm_sess) != NULL) tsep = NFSMNT_MDSSESSION(nmp); - TAILQ_INSERT_HEAD(&nmp->nm_sess, dsp, - nfsclds_list); + 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 @@ -874,9 +919,8 @@ nfsrpc_setclient(struct nfsmount *nmp, s 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);