aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Macklem <rmacklem@FreeBSD.org>2021-05-19 21:52:56 +0000
committerRick Macklem <rmacklem@FreeBSD.org>2021-06-02 23:58:53 +0000
commit5a8b2c5a46dffa3828ad16abee36ac4676f741bf (patch)
tree9ec57ef4c24cba2e2c707716e7a61aca47140d1a
parenta58a471b5d512c1ae81d3298b5cb317793373867 (diff)
downloadsrc-5a8b2c5a46dffa3828ad16abee36ac4676f741bf.tar.gz
src-5a8b2c5a46dffa3828ad16abee36ac4676f741bf.zip
nfscl: Fix NFSv4.1/4.2 mount recovery from an expired lease
The most difficult NFSv4 client recovery case happens when the lease has expired on the server. For NFSv4.0, the client will receive a NFSERR_EXPIRED reply from the server to indicate this has happened. For NFSv4.1/4.2, most RPCs have a Sequence operation and, as such, the client will receive a NFSERR_BADSESSION reply when the lease has expired for these RPCs. The client will then call nfscl_recover() to handle the NFSERR_BADSESSION reply. However, for the expired lease case, the first reclaim Open will fail with NFSERR_NOGRACE. This patch recognizes this case and calls nfscl_expireclient() to handle the recovery from an expired lease. This patch only affects NFSv4.1/4.2 mounts when the lease expires on the server, due to a network partitioning that exceeds the lease duration or similar. (cherry picked from commit c28cb257ddfe3339756f6fd659fa4a2efa4de2cb)
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c36
1 files changed, 36 insertions, 0 deletions
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index e705af31185b..e1b9d37b3a3e 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -1977,6 +1977,7 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
u_int32_t delegtype = NFSV4OPEN_DELEGATEWRITE, mode;
int i, igotlock = 0, error, trycnt, firstlock;
struct nfscllayout *lyp, *nlyp;
+ bool recovered_one;
/*
* First, lock the client structure, so everyone else will
@@ -2050,6 +2051,7 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
* Now traverse the state lists, doing Open and Lock Reclaims.
*/
tcred = newnfs_getcred();
+ recovered_one = false;
owp = LIST_FIRST(&clp->nfsc_owner);
while (owp != NULL) {
nowp = LIST_NEXT(owp, nfsow_list);
@@ -2083,6 +2085,7 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
op->nfso_mode, op, NULL, 0, &ndp, 1, delegtype,
tcred, p);
if (!error) {
+ recovered_one = true;
/* Handle any replied delegation */
if (ndp != NULL && ((ndp->nfsdl_flags & NFSCLDL_WRITE)
|| NFSMNT_RDONLY(nmp->nm_mountp))) {
@@ -2141,6 +2144,21 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
nfscl_freelockowner(lp, 0);
lp = nlp;
}
+ } else if (error == NFSERR_NOGRACE && !recovered_one &&
+ NFSHASNFSV4N(nmp)) {
+ /*
+ * For NFSv4.1/4.2, the NFSERR_EXPIRED case will
+ * actually end up here, since the client will do
+ * a recovery for NFSERR_BADSESSION, but will get
+ * an NFSERR_NOGRACE reply for the first "reclaim"
+ * attempt.
+ * So, call nfscl_expireclient() to recover the
+ * opens as best we can and then do a reclaim
+ * complete and return.
+ */
+ nfsrpc_reclaimcomplete(nmp, cred, p);
+ nfscl_expireclient(clp, nmp, tcred, p);
+ goto out;
}
}
if (error != 0 && error != NFSERR_BADSESSION)
@@ -2227,6 +2245,23 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
if (error) {
if (nop != NULL)
free(nop, M_NFSCLOPEN);
+ if (error == NFSERR_NOGRACE && !recovered_one &&
+ NFSHASNFSV4N(nmp)) {
+ /*
+ * For NFSv4.1/4.2, the NFSERR_EXPIRED case will
+ * actually end up here, since the client will do
+ * a recovery for NFSERR_BADSESSION, but will get
+ * an NFSERR_NOGRACE reply for the first "reclaim"
+ * attempt.
+ * So, call nfscl_expireclient() to recover the
+ * opens as best we can and then do a reclaim
+ * complete and return.
+ */
+ nfsrpc_reclaimcomplete(nmp, cred, p);
+ nfscl_expireclient(clp, nmp, tcred, p);
+ free(nowp, M_NFSCLOWNER);
+ goto out;
+ }
/*
* Couldn't reclaim it, so throw the state
* away. Ouch!!
@@ -2234,6 +2269,7 @@ nfscl_recover(struct nfsclclient *clp, struct ucred *cred, NFSPROC_T *p)
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
} else {
+ recovered_one = true;
LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
}
}