From nobody Sat Jul 25 01:21:32 2026 X-Original-To: dev-commits-src-all@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 4h6RsG2wk7z6lbdK for ; Sat, 25 Jul 2026 01:21:38 +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 4h6RsG0H7lz3K0Z for ; Sat, 25 Jul 2026 01:21:38 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1784942498; 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=VaOcgNM54fGo/V8gt6KoiXe7J5/kBO/ON2RfY3RM5c8V0suNSKeH/sR1W5DySaFv9wJI0H RfGBQRlpciTpDAzVXc1DbReweIooN4fRL/F4C1FSqdJ9xmAlrEGPrjg07P5UAFfdU3LUsk xnaXE3bpH5T3FMJOMxyKTu87bqc5JQWZyBpGfdXRrm5zHL/SBg0/9Wo1EkDp+EWgWRYNUr ZIHLLHrJ7T1Zx6xb3yx1sFTcb3ExbqEsiM7jYc+Gv5AIomozcRzm4cMMt3L1aIeMoKsGc/ ufMR04Ln9nF/D36vOqMnsN/NGA7+CAbY2wdRMw6fjLGsFgpElsxoiAekREUkKg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1784942498; a=rsa-sha256; cv=none; b=nJ+7LkYQWTA9OBBb2uMoFWiynMlKY4Iz54mBtX/1yuNT+W7aj8PfhmiWhT+3GzGAwvB5VL 43GSYs42GGrgJX5GB9mMml8VYMalwMCIk9Hf2Q5B9DF86OnXhPMhdFZBKd8G8lwMMM/KVp NtQ/MhN6rhiAh3FCUE7hS931GzSYLhPb2lOHRdQMIr/9mfQIsNxExrqQJ7Tc6skFsoGJzU OJa0L0KVj5MLftu80g/KpiiN98PXT2S5i962VXjb3P559Uv5hCH6mcslGIYboVae9TxqDQ Kw50Ix3LLbBMcFvr0rYNmvhJMIveoKfOhDW5IY5nn0vhhXqPcyxk1nfcwZvbdg== 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=1784942498; 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=LiJm++G5yPv9g9d7w18IlFSbv58I+qzc3dsUJULlqt4KDckwLtlujuKZvNWVf0v9AOQI0o fIP2CcFggXP+Bqt5VTXP6t9wsc24LEm31rvpRglX3FTflnm6Q4ziadayy3+ROtjaFQ1GO1 TvAiTOjb+e3E+fB0P3xR77n9xYyrrv19sra08Kl2SJTRan+5ySB+xiskPRDXFR3Oe1N3sK DXuH0oRMBu2KiAc/o4dsgEBL6MMrPl37XAOnilglj755KIlhYCwbzPW7QY+q8QqBVp3A1M NiS2ywsGhPFOC2IqRv5yYqtYjsvHBGbErBcW4Tqn5M0u+U7l1j4El450yXoGAg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4h6RsF65fKz1jS for ; Sat, 25 Jul 2026 01:21:37 +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: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@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); +}