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. MFC after: 2 week
This commit is contained in:
parent
73dddffc31
commit
52dee2bc03
@ -604,7 +604,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 *);
|
||||
|
@ -760,8 +760,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;
|
||||
@ -840,7 +841,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);
|
||||
@ -849,9 +850,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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3327,8 +3327,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;
|
||||
@ -3339,6 +3341,7 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
|
||||
return (error);
|
||||
*clpp = clp;
|
||||
|
||||
nmp = VFSTONFS(vp->v_mount);
|
||||
nfhp = VTONFS(vp)->n_fhp;
|
||||
recallp = malloc(sizeof(*recallp), M_NFSLAYRECALL, M_WAITOK);
|
||||
NFSLOCKCLSTATE();
|
||||
@ -3363,6 +3366,7 @@ nfscl_doclose(vnode_t vp, struct nfsclclient **clpp, NFSPROC_T *p)
|
||||
nfscl_retoncloselayout(vp, clp, nfhp->nfh_fh, nfhp->nfh_len, &recallp);
|
||||
|
||||
/* Now process the opens against the server. */
|
||||
LIST_INIT(&delayed);
|
||||
lookformore:
|
||||
LIST_FOREACH(op, NFSCLOPENHASH(clp, nfhp->nfh_fh, nfhp->nfh_len),
|
||||
nfso_hash) {
|
||||
@ -3376,8 +3380,16 @@ lookformore:
|
||||
op->nfso_opencnt));
|
||||
#endif
|
||||
NFSUNLOCKCLSTATE();
|
||||
nfsrpc_doclose(VFSTONFS(vp->v_mount), op, p);
|
||||
if (NFSHASNFSV4N(nmp))
|
||||
error = nfsrpc_doclose(nmp, op, p, false, true);
|
||||
else
|
||||
error = 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;
|
||||
}
|
||||
}
|
||||
@ -3388,6 +3400,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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user