From nobody Sat Jul 25 01:21:32 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4h6Rs92QdMz6lb9M for ; Sat, 25 Jul 2026 01:21:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4h6Rs873ZGz3KGf for ; Sat, 25 Jul 2026 01:21:32 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784942493; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=OS+ZcPcYFWxRI+yjQaJD/latzoI9bjKKkVgLRMwj8RY=; b=tb/gLCtLesOoQw7wzCFZ2k18NjS/EXWZM85QvvoNHxuJrEse74ONf3VldN8wtdcCkSVNMp H43RV8JVlhZ/wd8NRb5zeE3y5NG/KWVciTtrzv8poQSyYTP6jbZYy1VJ/CD06VnBq5TMQQ 9cEEIY7yQXTQwkYB8qYUWKj9T+UbCKmqeZaTPO/dXzDfGkhE2GYzQrttVM2ONu5fjN25WD XW0wz9d94zPEZ/RARfdM4xqg4ha6S9Unf4BLPRLp13Xo2w22SeEQGYgDU9dSvJhJJllOx3 tiGEbvFcPKuObQhrMYkaiGg4Iaz1CjnLgBzqzvvGk3FazvvNIXDlx2BhLZ0+bg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1784942493; a=rsa-sha256; cv=none; b=H9zActzYB46PaQupyz+iddS1uINlM/TN004YTczMQ5mOhKyCZwBHUnDH2HcWmIFpWThBix vT5CgNwRf9BKd8/uj0wls3SESGHoloP0gJpcuqvr6sPYlqtE7GQiTycc2lOk9crOHXAUzj 8OuGRz6kJkpM1Be+mUxX+9lQOqNAldMq77gaqyP8d9RsCY2PWVryKn2jIb2Oc1mVnADqgw 0RapNGHgjEoo73moylCgtNy52E7jSrJQ3sfgXP8uIY0zdOm/6Ynf6YwKrH4veeJ5jm94GS CfNneVC/EBwzkK6q08E0zwNVCDnI9AAwGJsdPI8qIjcorvyK0mlBuFzvM+ozQA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784942493; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=OS+ZcPcYFWxRI+yjQaJD/latzoI9bjKKkVgLRMwj8RY=; b=YSAamQXRk8rlY8lyrzwrVv7yF/2Q12PG7/eMh2q404KcK9ohDiVEnIcOTlrX+c2OIQriVD X5tXHvrfvNE0rrNrDW4Cn+wXLURjnNB0LmVOes1WB4Qk5UJcxJ4vvmsJJMZVEnsE2EhyT/ bST82tn7Aa6q8qBg0a5lnOJwergwtkRLRZzP2R2iPdvNQOUleD/N4a+MR0eVSJGTCV0j8P UxYUAWMI8EWYtHKQZ+iKPz9gjNffHsKhlbLbi/3r0NbYYU5T8cHnlkQ7AtotwLAcrReqrw q5jrRxorzG57HZN1TBs+ziEpG/oGFZFCkaptx+nI09jm9lC19o3t7Bpwd5KnYg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4h6Rs863ynzxR for ; Sat, 25 Jul 2026 01:21:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 323e3 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 25 Jul 2026 01:21:32 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Rick Macklem Subject: git: 3ad71ccbc58f - stable/15 - nfsd: Garbage collect stray NFSv4 state List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: rmacklem X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 3ad71ccbc58fe3d99e8af7f4246f4a85a0ef370d Auto-Submitted: auto-generated Date: Sat, 25 Jul 2026 01:21:32 +0000 Message-Id: <6a640f9c.323e3.d377a0b@gitrepo.freebsd.org> The branch stable/15 has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=3ad71ccbc58fe3d99e8af7f4246f4a85a0ef370d commit 3ad71ccbc58fe3d99e8af7f4246f4a85a0ef370d Author: Rick Macklem AuthorDate: 2026-07-11 15:49:12 +0000 Commit: Rick Macklem CommitDate: 2026-07-25 01:19:03 +0000 nfsd: Garbage collect stray NFSv4 state When 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'ng of the nfslockfile structure and replaces the two places where nearly identical code does this. (cherry picked from commit c52bcd09c2a6736fe841fd72e3cfb74de5a35b03) --- 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 22e702b001c0..c14dbecd72b2 100644 --- a/sys/fs/nfsserver/nfs_nfsdstate.c +++ b/sys/fs/nfsserver/nfs_nfsdstate.c @@ -251,6 +251,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. @@ -1535,20 +1536,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--; @@ -1630,12 +1624,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--; @@ -8859,3 +8848,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); +}