nfscl: return delegations in the NFS VOP_RECLAIM()

After a vnode is recycled it can no longer be
acquired via vfs_hash_get() and, as such,
a delegation for the vnode cannot be recalled.

In the unlikely event that a delegation still
exists when the vnode is being recycled, return
the delegation since it will no longer be
recallable.

Until you have this patch in your NFSv4 client,
you should consider avoiding the use of delegations.

MFC after:	2 weeks
This commit is contained in:
Rick Macklem 2021-04-25 17:57:55 -07:00
parent d55bf492f8
commit aad780464f
3 changed files with 50 additions and 10 deletions

View File

@ -604,6 +604,7 @@ void nfscl_lockinit(struct nfsv4lock *);
void nfscl_lockexcl(struct nfsv4lock *, void *);
void nfscl_lockunlock(struct nfsv4lock *);
void nfscl_lockderef(struct nfsv4lock *);
void nfscl_delegreturnvp(vnode_t, NFSPROC_T *);
void nfscl_docb(struct nfsrv_descript *, NFSPROC_T *);
void nfscl_releasealllocks(struct nfsclclient *, vnode_t, NFSPROC_T *, void *,
int);

View File

@ -303,7 +303,7 @@ ncl_reclaim(struct vop_reclaim_args *ap)
ncl_releasesillyrename(vp, td);
NFSUNLOCKNODE(np);
if (NFS_ISV4(vp) && vp->v_type == VREG)
if (NFS_ISV4(vp) && vp->v_type == VREG) {
/*
* We can now safely close any remaining NFSv4 Opens for
* this file. Most opens will have already been closed by
@ -311,6 +311,14 @@ ncl_reclaim(struct vop_reclaim_args *ap)
* called, so we need to do it again here.
*/
(void) nfsrpc_close(vp, 1, td);
/*
* It it unlikely a delegation will still exist, but
* if one does, it must be returned before calling
* vfs_hash_remove(), since it cannot be recalled once the
* nfs node is no longer available.
*/
nfscl_delegreturnvp(vp, td);
}
vfs_hash_remove(vp);

View File

@ -152,7 +152,8 @@ static int nfscl_trylock(struct nfsmount *, vnode_t , u_int8_t *,
struct ucred *, NFSPROC_T *);
static int nfsrpc_reopen(struct nfsmount *, u_int8_t *, int, u_int32_t,
struct nfsclopen *, struct nfscldeleg **, struct ucred *, NFSPROC_T *);
static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *);
static void nfscl_freedeleg(struct nfscldeleghead *, struct nfscldeleg *,
bool);
static int nfscl_errmap(struct nfsrv_descript *, u_int32_t);
static void nfscl_cleanup_common(struct nfsclclient *, u_int8_t *);
static int nfscl_recalldeleg(struct nfsclclient *, struct nfsmount *,
@ -1622,12 +1623,13 @@ nfscl_cleandeleg(struct nfscldeleg *dp)
* Free a delegation.
*/
static void
nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp)
nfscl_freedeleg(struct nfscldeleghead *hdp, struct nfscldeleg *dp, bool freeit)
{
TAILQ_REMOVE(hdp, dp, nfsdl_list);
LIST_REMOVE(dp, nfsdl_hash);
free(dp, M_NFSCLDELEG);
if (freeit)
free(dp, M_NFSCLDELEG);
nfsstatsv1.cldelegates--;
nfscl_delegcnt--;
}
@ -1725,7 +1727,7 @@ nfscl_expireclient(struct nfsclclient *clp, struct nfsmount *nmp,
printf("nfsv4 expired locks lost\n");
}
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
dp = ndp;
}
if (!TAILQ_EMPTY(&clp->nfsc_deleg))
@ -2257,7 +2259,7 @@ nfscl_recover(struct nfsclclient *clp, bool *retokp, struct ucred *cred,
* away. Ouch!!
*/
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
} else {
LIST_INSERT_HEAD(&extra_open, nop, nfso_list);
}
@ -3280,11 +3282,40 @@ nfscl_delegreturnall(struct nfsclclient *clp, NFSPROC_T *p)
TAILQ_FOREACH_SAFE(dp, &clp->nfsc_deleg, nfsdl_list, ndp) {
nfscl_cleandeleg(dp);
(void) nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
}
NFSFREECRED(cred);
}
/*
* Return any delegation for this vp.
*/
void
nfscl_delegreturnvp(vnode_t vp, NFSPROC_T *p)
{
struct nfsclclient *clp;
struct nfscldeleg *dp;
struct ucred *cred;
struct nfsnode *np;
np = VTONFS(vp);
cred = newnfs_getcred();
NFSLOCKCLSTATE();
clp = VFSTONFS(vp->v_mount)->nm_clp;
dp = nfscl_finddeleg(clp, np->n_fhp->nfh_fh,
np->n_fhp->nfh_len);
if (dp != NULL) {
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, false);
NFSUNLOCKCLSTATE();
newnfs_copycred(&dp->nfsdl_cred, cred);
nfscl_trydelegreturn(dp, cred, clp->nfsc_nmp, p);
free(dp, M_NFSCLDELEG);
} else
NFSUNLOCKCLSTATE();
NFSFREECRED(cred);
}
/*
* Do a callback RPC.
*/
@ -4515,7 +4546,7 @@ nfscl_removedeleg(vnode_t vp, NFSPROC_T *p, nfsv4stateid_t *stp)
*stp = dp->nfsdl_stateid;
retcnt = 1;
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
}
if (igotlock)
nfsv4_unlock(&clp->nfsc_lock, 0);
@ -4615,7 +4646,7 @@ nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
retcnt++;
*gotfdp = 1;
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
}
if (igotlock) {
nfsv4_unlock(&clp->nfsc_lock, 0);
@ -4651,7 +4682,7 @@ nfscl_renamedeleg(vnode_t fvp, nfsv4stateid_t *fstp, int *gotfdp, vnode_t tvp,
retcnt++;
*gottdp = 1;
nfscl_cleandeleg(dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp);
nfscl_freedeleg(&clp->nfsc_deleg, dp, true);
}
}
NFSUNLOCKCLSTATE();