kern/60584: vfs_bio: getnewbuf() can deadlock when fresh allocation is refused and no buffer is reclaimable
"Izumi Tsutsui via gnats" <[email protected]>
| Newsgroups | gmane.os.netbsd.bugs |
|---|---|
| Message-ID | <[email protected]> |
>Number: 60584 >Category: kern >Synopsis: vfs_bio: getnewbuf() can deadlock when fresh allocation is refused and no buffer is reclaimable >Confidential: no >Severity: serious >Priority: medium >Responsible: kern-bug-people >State: open >Class: sw-bug >Submitter-Id: net >Arrival-Date: Fri Aug 14 05:30:01 +0000 2026 >Originator: Izumi Tsutsui >Release: NetBSD 11.99.7 >Organization: >Environment: NetBSD libero 11.99.7 NetBSD 11.99.7 (GENERIC) #24: Thu Aug 13 09:21:55 JST 2026 tsutsui@mirage:/s/cvs/src/sys/arch/news68k/compile/GENERIC news68k Architecture: m68k Machine: news68k, Sony NWS-1750 (MC68030 16MB RAM, 8KB/page) >Description: getnewbuf() can deadlock when buf_lotsfree() decides not to allocate a new buffer and there is no buffer which can actually be reclaimed from BQ_AGE or BQ_LRU. getnewbuf() first uses buf_lotsfree() to decide whether to try a fresh allocation. If fresh allocation is not attempted or fails, it tries to recycle a buffer from BQ_AGE or BQ_LRU. If no suitable buffer is available there, it sleeps on needbuffer_cv. In this case, there is no guarantee that a reclaimable buffer will eventually become available. Buffers may instead be busy or locked, for example by WAPBL. A thread which could release such a buffer may itself need another buffer before it can make progress. Then a buffer allocation policy decision can turn into a deadlock. getnewbuf() waits for a buffer to become available while the operation which could make one available is itself waiting for another buffer. I observed this on NetBSD/news68k on 16MB NWS-1750 under heavy memory pressure with WAPBL enabled. makemandb and the pagedaemon were both sleeping in getnewbuf() on needbuffer_cv, while multiple user processes were waiting for physical memory. As a diagnostic experiment in DDB, I temporarily raised bufmem_lowater so that buf_lotsfree() would permit fresh allocation, woke the needbuffer_cv waiters, and continued execution. After that, the machine immediately resumed normal operation. The pagedaemon started making progress again, free memory increased, and the affected user processes continued running. This appears to demonstrate that getnewbuf() has no forward-progress path when fresh allocation is refused and no buffer can actually be reclaimed. I have also previously observed system hangs after decreasing vm.bufcache at runtime on low-memory machines. The vm.bufcache sysctl handler changes the buffer cache watermarks and attempts to drain the cache, but it can return successfully even if the cache cannot be drained below the new high-water mark. This may provide an easier way to trigger the same problem, but I have not yet verified that the older vm.bufcache hang is caused by this same bug. >How-To-Repeat: The exact workload required to reproduce the problem has not been minimized. The problem can occur when buf_lotsfree() rejects fresh allocation, no suitable buffer is available from BQ_AGE or BQ_LRU, and releasing the buffers needed for forward progress requires another buffer allocation. >Fix: Yes, please. getnewbuf() appears to need a forward-progress mechanism for the case where fresh allocation is refused but no buffer can actually be reclaimed. The appropriate fix might be to reconsider fresh allocation after reclamation fails, change the allocation policy, reserve resources needed for forward progress, or change the handling of locked buffers. I am not sure which approach is appropriate, and would prefer to leave the decision to people more familiar with the buffer cache and UVM. --- Izumi Tsutsui