mirror of
https://github.com/RhapsodiOS/RhapsodiOS.git
synced 2026-11-03 22:01:40 -07:00
[GH-ISSUE #51] kernel: non-blocking send sleeps waiting for mbufs under exhaustion, wedging the system #43
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#43
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 15, 2026).
Original GitHub issue: https://github.com/RhapsodiOS/RhapsodiOS/issues/51
Symptom
stress-net flood(opt-in level 2) opens ~3500 unix and loopback TCP socket pairs until ENFILE, sets every writer O_NONBLOCK and writes until EWOULDBLOCK/ENOBUFS. The process never gets past the fill phase (the stress watchdog times it out at -t secs), and sshd stops answering (connection accepted by the QEMU forward but no SSH banner) for as long as the process lives; once it hung for more than 10 minutes until the guest was cycled. After the watchdog kill the guest recovers; netstat -m then showed "requests for memory delayed" in the hundreds. The first 6 pairs filled normally (unix 4-8 KB, TCP ~64 KB each), so write itself returns EWOULDBLOCK correctly until the mbuf pool runs dry.Reproduce
On the dev guest (python vm/stress_dev.py up/sync, STRESS_DEV_PORTS=2563,4563,1263), in src/Commands/stress after
make:STRESS_EXHAUST=2 ./net/stress-net -t 90 flood(run it detached; ssh is dead while it runs). Level 1 (STRESS_EXHAUST=1) uses a bounded fill and passes in 1.4 s.Where to look
MGETHDR(m, M_WAIT, ...)(line ~673),MGET(m, M_WAIT, ...)(~678) andMCLGET(m, M_WAIT)(~682) regardless of SS_NBIO; SS_NBIO is only consulted for the sockbuf-space wait (~651, 880), not for mbuf allocation. Also m_get(M_WAIT) at ~802.Impact
Any process, even one using non-blocking sockets, can sleep indefinitely in sosend when the mbuf pool is exhausted, and because the pool is global the whole network stack (sshd, new connections) stalls with it. A non-blocking send should fail with EWOULDBLOCK/ENOBUFS (M_DONTWAIT when SS_NBIO or MSG_DONTWAIT is set) instead of sleeping.
Found by the stress suite (
src/Commands/stress, branchstress-phase3:net flood, phase 3). Related: #43, #45, also found by the stress suite.@raynorpat commented on GitHub (Oct 15, 2026):
Fix
sosend()now asks for mbufs withM_DONTWAITwhen the socket is non-blocking (SS_NBIOorMSG_DONTWAIT), and handles a failed allocation instead of sleeping inm_retry:ENOBUFS. Nothing is sent.EWOULDBLOCK.write()therefore reports a short count that matches exactly what was sent: no lost or duplicated bytes. If nothing was copied, it returns -1/EWOULDBLOCK.soreceive()'sMSG_OOBpath gets the same non-blocking rule and returnsENOBUFS.Blocking sockets are unchanged (
M_WAIT).MCLGETalready falls back to a plain mbuf when no cluster is free, so it needed no change.Commit:
kernel: non-blocking sends fail with EWOULDBLOCK/ENOBUFS instead of sleeping for mbufs (#51), a single-file change tosrc/kernel-7/bsd/kern/uipc_socket.c. It is merged intoffs-perflocally. Neither branch is pushed yet, and it is not on master.Verification
Tested on a
-snapshotguest ofrhap-i386-bootstrapped-v2.img, i386 RELEASE kernel built withrbuild kernel. The stress suite comes fromstress-phase3.STRESS_EXHAUST=2 stress-net -t 90 floodFAIL reason=timeout secs=90PASS secs=8.9(pairs=3496, 13.9 MB), thenPASS secs=8.2Also on the patched kernel:
STRESS_EXHAUST=1flood passes.-xfails as intended (the byte checker works).uipc_socket.c.Not covered: no ppc build was done. The kernel tested was master-based (d03ecae8f). The
uipc_socket.cmerged intoffs-perfis byte-identical, but the ffs-perf kernel as a whole has not been boot-tested with it.Notes for the stress-phase3 branch:
t_floodcomment still say level 2 is expected to wedge current kernels.-x(early-exit) run, ~2800 loopback TCP connections sit in FIN_WAIT_1 holding ~940 clusters, because that path skips theSO_LINGER 0close. This is classic BSD zero-window close behaviour and is unrelated to this fix.@raynorpat commented on GitHub (Oct 16, 2026):
Fixed in
ffa97ff591(Merge branch 'issue-51' into ffs-perf), commita482d9573: non-blocking sends fail with EWOULDBLOCK/ENOBUFS instead of sleeping for mbufs.In
sosend()(bsd/kern/uipc_socket.c):SS_NBIO, or a send withMSG_DONTWAIT, now allocates withM_DONTWAIT. If none is available, the send fails with ENOBUFS for atomic sends and EWOULDBLOCK otherwise.soreceive()'s out-of-bandm_getfollows the same rule.The regression check is the stress suite's
STRESS_EXHAUST=2 net/stress-net flood(on ffs-perf). Before the fix it wedged the guest. After the fix it should either PASS or FAIL cleanly with the system still answering ssh. I haven't run that check against a kernel containing this fix in this session.