From nobody Sat Jul 11 15:51:36 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 4gyCqd4Pmkz6lH5f for ; Sat, 11 Jul 2026 15:51:41 +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 4gyCqd3s1Cz3DlQ for ; Sat, 11 Jul 2026 15:51:41 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1783785101; 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=eS2+UKNgktjdaM5sFJqEMgwYl/0NeuYUOzmwBmLP7iE=; b=ELzcdU91hbLclbVZoRTXOx4DBqnjC0nzrsYQy/uoJMNNUikBo4yZcB+VTi+gHFtgIb8/8g yUPWdV4GUNrgRnINO7MVBo9PgMYOdfipS2qsKvia8wzca2ooAbyPHXxbWMDSdUIblKma7y BV28NZYHRvUccvtlM9JhS2Tic1D2kGwnzTbrGRTZw0jF7us8jCishF+GTwybeRQj6sXDxH Z2BokZ6KFQ9BkDUIzfuX/KwmlLN074DRC1o7/61pqM5B6ybzx5CUdzwYuBsYD3oZtgfPBJ wv9Ec0VUxLJVfMEtOY0hAseaVvG6fX3GKnotElQW/gVxGBAUWfeRWCcjNONOXA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1783785101; a=rsa-sha256; cv=none; b=KnJCS0laKkv+gbe2CYA7XVlIzWOa81I2ZdO/6e7fPxLzQsdD08ffb88JucsWkuxdAWJQdq ArIpL6btU5wAJX8FV+Y1JjVXLu5qnIhqcDljPiE/g73nQm2tItDNDtVwl3IstsdDij2HwJ LqQY4OpF45ztPl5N7n4J+pAERfoqpPFXdCP0yCXFQ/mmtWjMbmQY5y0A4BEMNXKE7WCOZQ Pt8xjFa7bXsh0DT4NIW9350WirAkUyrAf/ewMR3b3s5h02QIWaHy8+MSAJx0HY/YCxgXvI uXasAS/eCHSmHdfZE479IIM1u1Igzo14vo29jchgSKuzRHqAQIy85qRfSgM8RA== 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=1783785101; 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=eS2+UKNgktjdaM5sFJqEMgwYl/0NeuYUOzmwBmLP7iE=; b=FS7cX15S3CyMH/aJSkeM5CQF4lpVMW6plU93k5WTb6iqhS+GmsFEUbgDQeUy2/65S4NQO0 0+WDlsWkYX99Fp4ZROQzG9zLmQJlQlV9zrD5YtfHMr3/KRx/pbcWG7RXFbdioWxE73FMjr gZuNderRMbES7K2EI8YRWyPL00bw2fYBPxOMY+Xy7qJrO8H6FsRNrneihdw4RXs+Xr793y 4MRum/PpfWNif+wKqglJG7hZO8GK88lnB8+YJgp0jccGH1HOiKnWz1OcTb4wnuhEDjmcoS 7LMjRv8HNhx3PTqWtVQWymH2p1JI3nBvZ3KzV81a7w+XbGUUmTGzLsJzroDsfw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gyCqd2xv2znYs for ; Sat, 11 Jul 2026 15:51:41 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 386dd by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 11 Jul 2026 15:51:36 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Rick Macklem Subject: git: c52bcd09c2a6 - main - 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/main X-Git-Reftype: branch X-Git-Commit: c52bcd09c2a6736fe841fd72e3cfb74de5a35b03 Auto-Submitted: auto-generated Date: Sat, 11 Jul 2026 15:51:36 +0000 Message-Id: <6a526688.386dd.2aa996fc@gitrepo.freebsd.org> The branch main has been updated by rmacklem: URL: https://cgit.FreeBSD.org/src/commit/?id=c52bcd09c2a6736fe841fd72e3cfb74de5a35b03 commit c52bcd09c2a6736fe841fd72e3cfb74de5a35b03 Author: Rick Macklem AuthorDate: 2026-07-11 15:49:12 +0000 Commit: Rick Macklem CommitDate: 2026-07-11 15:49:12 +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. Reported by: Richard Purdie Tested by: Michael Halstead 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); +}