Re: nfs server issues

Rick Macklem <[email protected]> Wed, 8 Jul 2026 14:37:56 -0700
Newsgroups gmane.os.freebsd.devel.file-systems
Message-ID <CAM5tNy49KrwQ0TM_8PvOhiFQ=sAZUwsw8VHPi9R5GKygugXnzA@mail.gmail.com>
On Wed, Jul 8, 2026 at 7:57 AM Rick Macklem <[email protected]> wrote:
>
> On Wed, Jul 8, 2026 at 2:28 AM Richard Purdie
> <[email protected]> wrote:
> >
> > On Tue, 2026-07-07 at 15:54 -0700, Michael Halstead wrote:
> > > On Tue, Jul 7, 2026 at 1:52 AM Richard Purdie <[email protected]> wrote:
> > > > On Sat, 2026-07-04 at 12:41 -0700, Rick Macklem wrote:
> > > > > On Sat, Jul 4, 2026 at 1:05 AM Richard Purdie
> > > > > <[email protected]> wrote:
> > > > > >
> > > > > > nfsstat -E -s
> > > > > > Server Info:
> > > > > >       Getattr      Setattr       Lookup     Readlink         Read        Write
> > > > > >   17354100116   1674996998   5136769057        40544  18429670397    986443058
> > > > > >        Create       Remove       Rename         Link      Symlink        Mkdir
> > > > > >             0     98646613    119865835     17071853            0            0
> > > > > >         Rmdir      Readdir     RdirPlus       Access        Mknod       Fsstat
> > > > > >             0   1655544711            0   4129535844            0            0
> > > > > >        FSinfo     pathConf       Commit      LookupP      SetClId    SetClIdCf
> > > > > >             0            0      8723436            0            0            0
> > > > > One more thing to note. The # of Commits is much smaller than the # of Writes
> > > > > on the top line. This "hints" that a lot of the Writes are being done File_sync.
> > > > > (To check that, you'd need to capture packets and look at them in wireshark.)
> > >
> > >
> > > I've captured data
> > > at https://valkyrie.yocto.io/pub/non-release/mtest/nfs_capture2.pcap.
> > > zst from a fresh reboot running the testlock.py for about 100MB of
> > > data and umounting the filesystems. Please let me know if this
> > > reveals the issue or what else I can offer.
> >
> > Thanks Michael. I think that didn't have enough parallel test scripts
> > running. This is one I just captured with more parallelism (4 clients),
> > just tracing one of them:
> >
> > https://valkyrie.yocto.io/pub/non-release/mtest/rp_capture.pcap.zst
> >
> > This one has a lot of NFS4ERR_STALE in it which I suspect is what we
> > were looking for...
> Yep. This one definitely shows RPCs that would do Close failing when
> PutFH replies NFS4ERR_STALE. (I looked at the previous one and couldn't
> see any problems in it, although the previous one does have the weird case
> where the same Lock is acquired twice. Both succeed because it is the
> same client and lock_owner.  I am going to check to see what the RFCs
> say w.r.t. duplicate lock operations. Like Open, the Lock ops were modelled
> after what Windows wants, but were intended to handle POSIX lock semantics
> as well. Of course, no one implements mandatory locking and the protocol
> doesn't even allow the client to figure out which the server is doing, but
> that's another story.;-)
>
> I do think I can come up with a simple solution for this:
> - Since the operations that get rid of state all require a CFH, which
>   must be set by PutFH.. I cannot see any reason to keep state once
>   the PutFH is returning NFS4ERR_STALE for the file.
>   --> So, I think I can just free all the state for that FH at that time.
>       (The state is keyed on FH and not a vnode, so the PutFH has
>        the required FH.)
> --> I'll post a patch here later to-day. Hopefully you guys can run a
>       kernel from patched sources to test it.
I've attached a patch that I think fixes this (minimally tested).
If you can test this by applying it to your kernel sources and building a
kernel from sources, please do so and let us know how it goes.

If you've never done this, the following should work (assuming the
source tree is /usr/src).
# cd /usr/src
# patch -p0 < freefh.patch
# make buildkernel
# make installkernel
- reboot

rick

>
> Thanks for the packet trace and help tracking this down, rick
>
> >
> > Cheers,
> >
> > Richard
freefh.patch (application/octet-stream, 3.7 KB)
--- sys/fs/nfs/nfs_var.h.freefh	2026-07-08 13:44:13.102029000 -0700
+++ sys/fs/nfs/nfs_var.h	2026-07-08 13:59:13.162426000 -0700
@@ -169,6 +169,7 @@ void nfsrv_removedeleg(fhandle_t *, struct nfsrv_descr
     struct nfsdevice **);
 void nfsrv_marknospc(char *, bool);
 void nfsrv_removedeleg(fhandle_t *, struct nfsrv_descript *, NFSPROC_T *);
+void nfsrv_freestrandedstate(struct nfsrvfh *);
 
 /* nfs_nfsdserv.c */
 int nfsrvd_access(struct nfsrv_descript *, int,
--- sys/fs/nfsserver/nfs_nfsdstate.c.freefh	2026-07-08 08:52:44.075026000 -0700
+++ sys/fs/nfsserver/nfs_nfsdstate.c	2026-07-08 14:15:45.938151000 -0700
@@ -253,6 +253,7 @@ static void nfsrv_clientunlock(bool mlocked);
     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.
@@ -8878,6 +8879,79 @@ nfsrv_removedeleg(fhandle_t *fhp, struct nfsrv_descrip
 	if (error == 0) {
 		LIST_FOREACH_SAFE(stp, &lfp->lf_deleg, ls_file, nstp)
 			nfsrv_freedeleg(stp);
+	}
+	NFSUNLOCKSTATE();
+}
+
+/*
+ * Free the nfslockfile structure if not in use.
+ */
+static void
+nfsrv_freelockifnotinuse(struct nfslockfile *lfp)
+{
+
+	mtx_assert(NFSSTATEMUTEXPTR, MA_OWNED);
+	/*
+	 * 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);
 }
--- sys/fs/nfsserver/nfs_nfsdsocket.c.freefh	2026-07-08 13:45:25.897531000 -0700
+++ sys/fs/nfsserver/nfs_nfsdsocket.c	2026-07-08 13:58:42.960529000 -0700
@@ -1031,7 +1031,8 @@ nfsrvd_compound(struct nfsrv_descript *nd, int isdgram
 				cur_fsid = vp->v_mount->mnt_stat.f_fsid;
 				NFSVOPUNLOCK(vp);
 				vpnes = nes;
-			}
+			} else if (nd->nd_repstat == ESTALE)
+				nfsrv_freestrandedstate(&fh);
 			break;
 		case NFSV4OP_PUTPUBFH:
 			if (nfs_pubfhset) {