git: 69e838e971cd - stable/12 - nfscl: Handle NFSv4.1/4.2 Close RPC NFSERR_DELAY replies better
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 19 Nov 2021 05:32:55 UTC
The branch stable/12 has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=69e838e971cda85da388369fa3bc6169bc44c45d commit 69e838e971cda85da388369fa3bc6169bc44c45d Author: Rick Macklem <rmacklem@FreeBSD.org> AuthorDate: 2021-10-18 22:02:21 +0000 Commit: Rick Macklem <rmacklem@FreeBSD.org> CommitDate: 2021-11-19 05:28:07 +0000 nfscl: Handle NFSv4.1/4.2 Close RPC NFSERR_DELAY replies better Without this patch, if a NFSv4.1/4.2 server replies NFSERR_DELAY to a Close operation, the client loops retrying the Close while holding a shared lock on the clientID. This shared lock blocks returns of delegations, even though the server has issued a CB_RECALL to request the delegation return. This patch delays doing a retry of a Close that received a reply of NFSERR_DELAY until after the shared lock on the clientID is released, for NFSv4.1/4.2. To fix this for NFSv4.0 would be very difficult and since the only known NFSv4 server to reply NFSERR_DELAY to Close only does NFSv4.1/4.2, this fix is hoped to be sufficient. This problem was detected during a recent IETF working group NFSv4 testing event. (cherry picked from commit 52dee2bc035545f7ae2b838d8a0449f65043cd8a) --- sys/fs/nfs/nfs_var.h | 3 ++- sys/fs/nfsclient/nfs_clrpcops.c | 11 +++++++---- sys/fs/nfsclient/nfs_clstate.c | 27 ++++++++++++++++++++++++--- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index d10aa05a6b6f..24702dab442a 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -571,7 +571,8 @@ void nfscl_dumpstate(struct nfsmount *, int, int, int, int); void nfscl_dupopen(vnode_t, int); int nfscl_getclose(vnode_t, struct nfsclclient **); int nfscl_doclose(vnode_t, struct nfsclclient **, NFSPROC_T *); -void nfsrpc_doclose(struct nfsmount *, struct nfsclopen *, NFSPROC_T *); +int nfsrpc_doclose(struct nfsmount *, struct nfsclopen *, NFSPROC_T *, bool, + bool); int nfscl_deleg(mount_t, struct nfsclclient *, u_int8_t *, int, struct ucred *, NFSPROC_T *, struct nfscldeleg **); void nfscl_lockinit(struct nfsv4lock *); diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 615b48abe6bb..9cfab016eecb 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -727,8 +727,9 @@ nfsrpc_close(vnode_t vp, int doclose, NFSPROC_T *p) /* * Close the open. */ -void -nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p) +int +nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p, + bool loop_on_delayed, bool freeop) { struct nfsrv_descript nfsd, *nd = &nfsd; struct nfscllockowner *lp, *nlp; @@ -807,7 +808,7 @@ nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p) nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR); NFSUNLOCKCLSTATE(); do { - error = nfscl_tryclose(op, tcred, nmp, p, true); + error = nfscl_tryclose(op, tcred, nmp, p, loop_on_delayed); if (error == NFSERR_GRACE) (void) nfs_catnap(PZERO, error, "nfs_close"); } while (error == NFSERR_GRACE); @@ -816,9 +817,11 @@ nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p) LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp) nfscl_freelockowner(lp, 0); - nfscl_freeopen(op, 0, true); + if (freeop && error != NFSERR_DELAY) + nfscl_freeopen(op, 0, true); NFSUNLOCKCLSTATE(); NFSFREECRED(tcred); + return (error); } /* diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index 1825b56eabd0..387d043b4dbd 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -3231,8 +3231,10 @@ int nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) { struct nfsclclient *clp; + struct nfsmount *nmp; struct nfsclowner *owp, *nowp; - struct nfsclopen *op; + struct nfsclopen *op, *nop; + struct nfsclopenhead delayed; struct nfscldeleg *dp; struct nfsfh *nfhp; struct nfsclrecalllayout *recallp; @@ -3244,6 +3246,7 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) return (error); *clpp = clp; + nmp = VFSTONFS(vnode_mount(vp)); nfhp = VTONFS(vp)->n_fhp; recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL, M_WAITOK); NFSLOCKCLSTATE(); @@ -3269,6 +3272,7 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p) &lyp); /* Now process the opens against the server. */ + LIST_INIT(&delayed); lookformore: LIST_FOREACH(owp, &clp->nfsc_owner, nfsow_list) { op = LIST_FIRST(&owp->nfsow_open); @@ -3280,9 +3284,19 @@ lookformore: KASSERT((op->nfso_opencnt == 0), ("nfscl: bad open cnt on server")); NFSUNLOCKCLSTATE(); - nfsrpc_doclose(VFSTONFS(vnode_mount(vp)), op, - p); + if (NFSHASNFSV4N(nmp)) + nfsrpc_doclose(nmp, op, p, false, + true); + else + nfsrpc_doclose(nmp, op, p, true, + true); NFSLOCKCLSTATE(); + if (error == NFSERR_DELAY) { + nfscl_unlinkopen(op); + op->nfso_own = NULL; + LIST_INSERT_HEAD(&delayed, op, + nfso_list); + } goto lookformore; } op = LIST_NEXT(op, nfso_list); @@ -3309,6 +3323,13 @@ lookformore: * used by the function, but calling free() with a NULL pointer is ok. */ free(recallp, M_NFSLAYRECALL); + + /* Now, loop retrying the delayed closes. */ + LIST_FOREACH_SAFE(op, &delayed, nfso_list, nop) { + nfsrpc_doclose(nmp, op, p, true, false); + LIST_REMOVE(op, nfso_list); + nfscl_freeopen(op, 0, false); + } return (0); }