mirror of
https://github.com/RhapsodiOS/RhapsodiOS.git
synced 2026-11-03 22:01:40 -07:00
[GH-ISSUE #88] nfs: loopback NFS deadlocks when a client vnode is recycled (getnewvnode -> vgonel -> vinvalbuf -> nfs fsync) #79
Labels
No labels
bug
documentation
enhancement
enhancement
good first issue
help wanted
help wanted
pull-request
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
github-starred/RhapsodiOS#79
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @raynorpat on GitHub (Oct 31, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/88
Originally assigned to: @raynorpat on GitHub.
Found while testing #81 (pre-existing; reproduced on the stock kernel).
Symptom
vm/work/issue-81/ditest.con a loopback NFS mount (export and mount on the same guest) writes 4×150 files of 24K, then recycles vnodes (ls -lR,catacross the system). It hangs on both the stock kernel and the #81 kernel, before verification.Thread dumps (
vm/work/issue-81/s1-hang-fw.txt,s1-hang-all.txt,s4-hang-all.txtstock;s3-hang-fw.txtfixed):stat: stuck ingetnewvnode→vgonel→vinvalbuf+0x73, which isVOP_FSYNC: the NFS client buffer flushnfs_fsync/nfs_flush.Likely cause
An nfsd that needs a new vnode to serve a request recycles an NFS client vnode with dirty buffers. Flushing those buffers needs an nfsd to serve the writes, and over loopback that nfsd is this thread or one of its peers, blocked the same way.
This is the vnode-recycling counterpart of the mapfs self-deadlock fixed in #78 (
e9e67317f).Expected
Over loopback NFS, recycling never waits on its own server. Possible fixes:
getnewvnodeskips dirty NFS client vnodes when the caller is an nfsd, or always when the free list offers a clean one;Reproduce
ditestwith NFS on the guest (-d /mnt/nfs, export via NetInfo as invm/stress_fs.sh), 4×150 or more, with recycling. It also cuts the #81 data-integrity test short on NFS, so there are no NFS integrity counts yet.@raynorpat commented on GitHub (Oct 31, 2026):
Fixed by
047687dbe,84b136588and5e1ac8f9a, merged into ffs-perf in550b7c1f1.Deadlock 1 (base kernel,
ditestNFS 4×150; thread dumpsvm/work/issue-88/b1-hang-fw.txt):nfsrv_create/nfsrv_write→ffs_vget→getnewvnode→vgonel→vclean→vinvalbuf→nfs_flush, sleeping "nfsfsync" on a busy buffer (nfs_vnops.c:3066/:3102).nfs_bwriteto an nfsiod, which is innfs_writerpcwaiting for a reply only an nfsd can send.nfsrv_create. All 4 nfsds are blocked.Deadlock 2 (with the first fix alone): an nfsd →
getnewvnode→vm_object_cache_steal→vm_object_terminate→vnode_pageout→nfs_write→nfs_asyncio, sleeping "nfsaio" (nfs_bio.c:1032), with every nfsiod waiting for nfsd replies.Fixes:
047687dbe:getnewvnodeprefers a free vnode with no NFS I/O pending (newvnode_nfs_pending: VT_NFS with dirty buffers,v_numoutput> 0, or a busy clean buffer). An nfsd (newP_NFSD, set aroundnfssvc_nfsd) never recycles a pending vnode. If none is clean it allocates a fresh one. Other callers fall back to the old take-the-head path.84b136588: an nfsd never callsvm_object_cache_stealfromgetnewvnode.5e1ac8f9a, a third bug that showed once the hangs were gone: NFS creates landed in the wrong directory (/mnt1, the export root, even hd0), and open got ENOTDIR.nfs_lookupread the cached vnode'sv_idafterVOP_ACCESS, an RPC that sleeps (nfs_vnops.c:956-962). So a vnode recycled meanwhile, even for a UFS file, passed the capability check. The base code had this too; nfsds now recycle clean client vnodes more often, which made it far more likely.v_idis now taken before the call.No starvation or data loss:
getnewvnode.desiredvnodesare bounded by the free vnodes with I/O pending.nfs_syncfsyncs every dirty vnode on the mount), buffer pressure or a later non-nfsd recycle still write.Verified on the kernel built from
5e1ac8f9a(cksum 3847563290):rp.sh/rp2.shrc=0.check-fs,check-mmap,check-vmpass on UFS and/mnt/nfs, and the plain NFS unmount is clean.--fs nfs, all categories, seed 75, twice: run_exit 0, PASS=98, FAIL=0, UNSUP=9, no unmount error.Left open:
vm_object_cache_trim's synchronous NFS push (not seen to hang).ffs_vgethas no inode-hash recheck after sleeping, the race later BSDs fixed. It isn't needed here.check-mmap -d /mnt/nfsleavesstress.mmap.*.map.*files in the export.