On 7/11/26 20:58, A FreeBSD User wrote:
Am Tage des Herren Sat, 11 Jul 2026 15:51:36 +0000 Rick Macklem <[email protected]> schrieb:The branch main has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=c52bcd09c2a6736fe841fd72e3cfb74de5a35b03 commit c52bcd09c2a6736fe841fd72e3cfb74de5a35b03 Author: Rick Macklem <[email protected]> AuthorDate: 2026-07-11 15:49:12 +0000 Commit: Rick Macklem <[email protected]> CommitDate: 2026-07-11 15:49:12 +0000 nfsd: Garbage collect stray NFSv4 stateWhen a file is deleted on the NFS server by another client,any NFSv4 state related to that file is left stranded. This happens because the NFSv4 operations that free the state use a CFH, which is set by a PutFH operation. However, the PutFH fails with ESTALE because the file has been deleted.This patch adds a function called nfsrv_freestrandedstate()that frees all the NFSv4 state related to a file and calls this function when PutFH will be replying ESTALE.While here, a helper function was defined to handle free'ngof the nfslockfile structure and replaces the two places where nearly identical code does this.Reported by: Richard Purdie <[email protected]>Tested by: Michael Halstead <[email protected]> MFC after: 2 weeks --- sys/fs/nfsserver/nfs_nfsdstate.c | 89 +++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c index 102af9bb2951..d790ab974d07 100644 --- a/sys/fs/nfsserver/nfs_nfsdstate.c +++ b/sys/fs/nfsserver/nfs_nfsdstate.c @@ -253,6 +253,7 @@ static void nfsrv_issuedelegation(struct vnode *vp, struct nfsclient *clp, nfsv4stateid_t *delegstateidp); static void nfsrv_clientlock(bool mlocked); static void nfsrv_clientunlock(bool mlocked); +static void nfsrv_freelockifnotinuse(struct nfslockfile *lfp);/** Lock the client structure, either with the mutex or the exclusive nfsd lock. @@ -1537,20 +1538,13 @@ nfsrv_freedeleglist(struct nfsstatehead *sthp) static void nfsrv_freedeleg(struct nfsstate *stp) { - struct nfslockfile *lfp;LIST_REMOVE(stp, ls_hash);LIST_REMOVE(stp, ls_list); LIST_REMOVE(stp, ls_file); if ((stp->ls_flags & NFSLCK_DELEGWRITE) != 0) nfsrv_writedelegcnt--; - lfp = stp->ls_lfp; - if (LIST_EMPTY(&lfp->lf_open) && - LIST_EMPTY(&lfp->lf_lock) && LIST_EMPTY(&lfp->lf_deleg) && - LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && - lfp->lf_usecount == 0 && - nfsv4_testlock(&lfp->lf_locallock_lck) == 0) - nfsrv_freenfslockfile(lfp); + nfsrv_freelockifnotinuse(stp->ls_lfp); free(stp, M_NFSDSTATE); VNET(nfsstatsv1_p)->srvdelegates--; nfsrv_openpluslock--; @@ -1632,12 +1626,7 @@ nfsrv_freeopen(struct nfsstate *stp, vnode_t vp, int cansleep, NFSPROC_T *p) * If there are locks associated with the open, the * nfslockfile structure can be freed via nfsrv_freelockowner(). */ - if (lfp != NULL && LIST_EMPTY(&lfp->lf_open) && - LIST_EMPTY(&lfp->lf_deleg) && LIST_EMPTY(&lfp->lf_lock) && - LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && - lfp->lf_usecount == 0 && - nfsv4_testlock(&lfp->lf_locallock_lck) == 0) - nfsrv_freenfslockfile(lfp); + nfsrv_freelockifnotinuse(lfp); free(stp, M_NFSDSTATE); VNET(nfsstatsv1_p)->srvopens--; nfsrv_openpluslock--; @@ -8881,3 +8870,75 @@ nfsrv_removedeleg(fhandle_t *fhp, struct nfsrv_descript *nd, NFSPROC_T *p) } NFSUNLOCKSTATE(); } + +/* + * Free the nfslockfile structure if not in use. + */ +static void +nfsrv_freelockifnotinuse(struct nfslockfile *lfp) +{ + + /* + * The nfslockfile is freed here if there are no locks + * associated with the open. + * If there are locks associated with the open, the + * nfslockfile structure can be freed via nfsrv_freelockowner(). + */ + if (lfp != NULL && LIST_EMPTY(&lfp->lf_open) && + LIST_EMPTY(&lfp->lf_deleg) && LIST_EMPTY(&lfp->lf_lock) && + LIST_EMPTY(&lfp->lf_locallock) && LIST_EMPTY(&lfp->lf_rollback) && + lfp->lf_usecount == 0 && + nfsv4_testlock(&lfp->lf_locallock_lck) == 0) + nfsrv_freenfslockfile(lfp); +} + +/* + * Free stranded open/lock/delegation/layouts. + * (These become stranded if the file has been deleted.) + */ +void +nfsrv_freestrandedstate(struct nfsrvfh *nfp) +{ + struct nfslockfile *lfp; + struct nfsstate *stp, *nstp; + struct nfslayouthash *lhyp; + struct nfslayout *lyp, *nlyp; + fhandle_t *fhp; + + if (nfp->nfsrvfh_len != NFSX_MYFH) + return; + fhp = (fhandle_t *)nfp->nfsrvfh_data; + NFSLOCKSTATE(); + if (nfsrv_getlockfile(0, NULL, &lfp, fhp, 0) < 0) { + NFSUNLOCKSTATE(); + return; + } + lfp->lf_usecount++; /* So nfsrv_freeopen() does not free it. */ + /* Note that nfsrv_freeopen() will also free the byte range locks. */ + LIST_FOREACH_SAFE(stp, &lfp->lf_open, ls_file, nstp) + nfsrv_freeopen(stp, NULL, 0, curthread); + + /* + * Normally, a delegation will have been recalled when the file is + * removed. However, get rid of any that have somehow been + * left stranded. + */ + LIST_FOREACH_SAFE(stp, &lfp->lf_deleg, ls_file, nstp) + nfsrv_freedeleg(stp); + + /* Get rid of the nfslockfile, if no longer in use. */ + lfp->lf_usecount--; + nfsrv_freelockifnotinuse(lfp); + NFSUNLOCKSTATE(); + + /* Free any layouts for the pNFS server case. */ + if (nfsrv_devidcnt == 0) + return; + lhyp = NFSLAYOUTHASH(fhp); + NFSLOCKLAYOUT(lhyp); + TAILQ_FOREACH_SAFE(lyp, &lhyp->list, lay_list, nlyp) { + if (NFSBCMP(&lyp->lay_fh, fhp, sizeof(*fhp)) == 0) + nfsrv_freelayout(&lhyp->list, lyp); + } + NFSUNLOCKLAYOUT(lhyp); +}It seems this commit breaks buildkernel for me: [...] /usr/src/sys/fs/nfsserver/nfs_nfsdstate.c:8900:1: error: no previous prototype for function 'nfsrv_freestrandedstate' [-Werror,-Wmissing-prototypes] 8900 | nfsrv_freestrandedstate(struct nfsrvfh *nfp
+1 Same here
Kind regards, oh
-- Pouria
OpenPGP_signature.asc
Description: OpenPGP digital signature
