[GH-ISSUE #54] msdosfs: concurrent rename deadlocks on denode locks; the hang spreads to the root fs #47

Closed
opened 2026-11-03 22:03:11 -07:00 by gitea-mirror · 1 comment
Owner

Originally created by @raynorpat on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/54

Symptom

stress-fs -n 4 -s 1 -d /mnt/m rw append tree fsync rename link on a FAT16
volume hangs in the rename test (rw/append/tree/fsync had passed). All workers
sleep in state U on denode locks. Other processes that stat a path through
/mnt then block on the root UFS inode locks of /mnt and /, so ssh logins
hang too. No panic. Kernel mach_kernel.issue43 (cksum 1562297808, built Oct 15),
which contains the #45 msdosfs rename changes (89e39d0b2, b2381210b, 0f1627e08).

QEMU gdbstub dump (msdos-1-hang-threads.txt, msdos-1-hang-locks.txt):

pid waits in lock held by
1747 rename -> msdosfs_rename (static) -> relookup -> vn_lock 20241884 1749
1749 stat -> lookup -> msdosfs_lookup -> deget -> vget -> vn_lock 20241204 1747
1751 open -> lookup -> msdosfs_lookup 20241204 1747
1748 rename -> namei -> msdosfs_lookup 20237584 1751
1808 stat -> lookup -> deget 201fd404 1748
1809, 1810, 277 + 3 more stat -> ufs_lookup (root fs) UFS inodes 1808 -> 1809 -> 1810

1747 and 1749 are a two-process lock-order cycle. 1747, the renamer, holds
denode lock 20241204 and waits in relookup for 20241884. 1749, in an
ordinary lookup, holds 20241884 and waits in deget for 20241204. The
other waiters queue behind them, and a lookup holding a root-fs vnode lock
spreads the hang to the root fs.

A second hang came from stress-fs -n 4 -s 3 -d /mnt/m rename run on its
own (msdos-3-hang-*.txt). This time two renamers deadlocked each other.
Both were in rename -> msdosfs_rename -> relookup -> vn_lock: 1348
waited for 2028a604, held by 1349, and 1349 waited for 2028a804, held by
1348. Overall, rename hung 2 of 5 runs (seeds 1 and 3; seeds 1, 2 and 2
passed in their other runs).

After a hard stop, fsck_msdos -n reported the volume clean the first time
(106 leftover files) and 6 lost 2-cluster chains the second time.

Reproduce

i386 QEMU guest. Partition a of a 512-byte-sector NeXT-labelled disk
(vm/ext2_guest.py wrap_volume layout), formatted on the guest with
newfs_msdos -s 524288 /dev/rhd2a (FAT16, 4 KiB clusters) and mounted with
mount_msdos /dev/hd2a /mnt/m. Tools mount_msdos/newfs_msdos/fsck_msdos
from vm/work/fs-races/msdos/tools.tar. Then:

stress-fs -n 4 -s 1 -d /mnt/m rename      # 4 procs renaming files and directories in one shared dir set

(It hung within the first few minutes of rename. The run used the full
rw append tree fsync rename link list, so rename was the fifth test.)

Artifacts in vm/work/fsrepro/: msdos-{1,3}-hang.{ppm,png,log},
msdos-{1,3}-hang-threads.txt, msdos-{1,3}-hang-locks.txt, res-m3-part1.log,
res-m5-final.log.

Where to look

bsd/msdosfs/msdosfs_vnops.c msdosfs_rename(), the relookup() calls added or
moved by 89e39d0b2: relookup(fdvp, &svp, fcnp) (source re-check),
relookup_startdir(tdvp, ...), and the final relookup(fdvp, &fvp, fcnp).
When newparent is set, the last one runs with tdvp still locked, so the
rename locks the target directory and then the source directory. A plain
lookup (msdosfs_lookup -> deget -> vget) that holds one of these
directories and wants the other closes the cycle. Check every relookup in
the function for a vnode left locked across it, and compare the lock order
with ufs_rename in this tree, which passes the same rename load.

Found by the stress suite (src/Commands/stress, branch stress-phase3) during the phase 3 filesystem run. Related: #45 (rename races, fixed on fs-races).

Originally created by @raynorpat on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/54 ## Symptom `stress-fs -n 4 -s 1 -d /mnt/m rw append tree fsync rename link` on a FAT16 volume hangs in the `rename` test (rw/append/tree/fsync had passed). All workers sleep in state `U` on `denode` locks. Other processes that `stat` a path through `/mnt` then block on the root UFS `inode` locks of `/mnt` and `/`, so ssh logins hang too. No panic. Kernel `mach_kernel.issue43` (cksum 1562297808, built Oct 15), which contains the #45 msdosfs rename changes (89e39d0b2, b2381210b, 0f1627e08). QEMU gdbstub dump (`msdos-1-hang-threads.txt`, `msdos-1-hang-locks.txt`): | pid | waits in | lock | held by | |------|----------|------|---------| | 1747 | `rename` -> msdosfs_rename (static) -> `relookup` -> `vn_lock` | 20241884 | 1749 | | 1749 | `stat` -> `lookup` -> msdosfs_lookup -> `deget` -> `vget` -> `vn_lock` | 20241204 | 1747 | | 1751 | `open` -> `lookup` -> msdosfs_lookup | 20241204 | 1747 | | 1748 | `rename` -> `namei` -> msdosfs_lookup | 20237584 | 1751 | | 1808 | `stat` -> `lookup` -> `deget` | 201fd404 | 1748 | | 1809, 1810, 277 + 3 more | `stat` -> `ufs_lookup` (root fs) | UFS inodes | 1808 -> 1809 -> 1810 | 1747 and 1749 are a two-process lock-order cycle. 1747, the renamer, holds denode lock 20241204 and waits in `relookup` for 20241884. 1749, in an ordinary lookup, holds 20241884 and waits in `deget` for 20241204. The other waiters queue behind them, and a lookup holding a root-fs vnode lock spreads the hang to the root fs. A second hang came from `stress-fs -n 4 -s 3 -d /mnt/m rename` run on its own (`msdos-3-hang-*.txt`). This time two renamers deadlocked each other. Both were in `rename` -> msdosfs_rename -> `relookup` -> `vn_lock`: 1348 waited for 2028a604, held by 1349, and 1349 waited for 2028a804, held by 1348. Overall, `rename` hung 2 of 5 runs (seeds 1 and 3; seeds 1, 2 and 2 passed in their other runs). After a hard stop, `fsck_msdos -n` reported the volume clean the first time (106 leftover files) and 6 lost 2-cluster chains the second time. ## Reproduce i386 QEMU guest. Partition a of a 512-byte-sector NeXT-labelled disk (`vm/ext2_guest.py wrap_volume` layout), formatted on the guest with `newfs_msdos -s 524288 /dev/rhd2a` (FAT16, 4 KiB clusters) and mounted with `mount_msdos /dev/hd2a /mnt/m`. Tools `mount_msdos`/`newfs_msdos`/`fsck_msdos` from `vm/work/fs-races/msdos/tools.tar`. Then: ``` stress-fs -n 4 -s 1 -d /mnt/m rename # 4 procs renaming files and directories in one shared dir set ``` (It hung within the first few minutes of `rename`. The run used the full `rw append tree fsync rename link` list, so `rename` was the fifth test.) Artifacts in `vm/work/fsrepro/`: `msdos-{1,3}-hang.{ppm,png,log}`, `msdos-{1,3}-hang-threads.txt`, `msdos-{1,3}-hang-locks.txt`, `res-m3-part1.log`, `res-m5-final.log`. ## Where to look `bsd/msdosfs/msdosfs_vnops.c` `msdosfs_rename()`, the `relookup()` calls added or moved by 89e39d0b2: `relookup(fdvp, &svp, fcnp)` (source re-check), `relookup_startdir(tdvp, ...)`, and the final `relookup(fdvp, &fvp, fcnp)`. When `newparent` is set, the last one runs with `tdvp` still locked, so the rename locks the target directory and then the source directory. A plain lookup (`msdosfs_lookup` -> `deget` -> `vget`) that holds one of these directories and wants the other closes the cycle. Check every `relookup` in the function for a vnode left locked across it, and compare the lock order with `ufs_rename` in this tree, which passes the same `rename` load. Found by the stress suite (`src/Commands/stress`, branch `stress-phase3`) during the phase 3 filesystem run. Related: #45 (rename races, fixed on fs-races).
Author
Owner

@raynorpat commented on GitHub (Oct 16, 2026):

Fixed in 260033acbb (kernel: Merge filesystem repairs for issues 52-55), on ffs-perf; the rename locking is reworked in msdosfs_vnops.c and msdosfs_lookup.c.

Verified on the repair kernel:

  • The new focused test vm/msdosfs_rename_test.c passes: 4 workers × 4,000 opposing file and directory moves, with lookup checks and source preservation on refused renames.
  • FAT16 stress-fs tree and rename pass with 4 workers, 500 iterations, seeds 1, 3 and 10, including the two seeds that hung before.
  • fsck_msdos -n is clean afterwards.

Test notes and focused regressions: vm/filesystem-issues-52-55.md in that commit. Repair kernel cksum 3500498149 (i386, QEMU snapshot); PowerPC not tested.

<!-- gh-comment-id:6206909486 --> @raynorpat commented on GitHub (Oct 16, 2026): Fixed in 260033acbb4c29cf90781f9b16cc028a79785e69 (kernel: Merge filesystem repairs for issues 52-55), on ffs-perf; the rename locking is reworked in `msdosfs_vnops.c` and `msdosfs_lookup.c`. Verified on the repair kernel: - The new focused test `vm/msdosfs_rename_test.c` passes: 4 workers × 4,000 opposing file and directory moves, with lookup checks and source preservation on refused renames. - FAT16 `stress-fs tree` and `rename` pass with 4 workers, 500 iterations, seeds 1, 3 and 10, including the two seeds that hung before. - `fsck_msdos -n` is clean afterwards. Test notes and focused regressions: `vm/filesystem-issues-52-55.md` in that commit. Repair kernel cksum 3500498149 (i386, QEMU snapshot); PowerPC not tested.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
github-starred/RhapsodiOS#47
No description provided.