bcachefs: swap file support via SWP_FS_OPS#646
Conversation
0b08bcb to
a2b6341
Compare
Add copy-on-write swap file support to bcachefs using the SWP_FS_OPS path, the same mechanism used by network filesystems. This keeps bcachefs in the swap I/O loop so checksumming, encryption, compression, replication, and multi-device placement remain available for swap data. Key design decisions: PF_MEMALLOC/noreclaim context: swap_rw runs under memalloc_noreclaim for both reads and writes. Writes need it because swap writeback runs during reclaim. Reads need it because swap-in page faults can otherwise trigger reclaim that starts competing swap writes. BCH_WRITE_swap propagates the context to the write index kworker and the low-level write path sets PF_MEMALLOC for swap writes. Btree node pinning: at swapon, leaf nodes for extents, inodes, and alloc btrees covering the swap file are marked noevict. This avoids disk reads during memory reclaim; the hot path only does in-memory btree lookups. Btree cache pre-reserve: 16 MB of btree node buffers are pre-allocated on bc->freeable at swapon using GFP_NORETRY to avoid OOM on small VMs. These cover several concurrent fully-cold btree traversals across extents, inodes, and alloc btrees. Disk reservation: swap_pages * PAGE_SECTORS is reserved at swapon to prevent ENOSPC during COW writes. Each swap write allocates a new physical block before freeing the old one; without a reservation, concurrent non-swap writers could consume all free space. Bkey buffer pre-allocation: a 2048-byte buffer is allocated with GFP_NOWAIT before entering noreclaim context, avoiding __GFP_NOFAIL WARN loops when bch2_bkey_buf_realloc spills from the on-stack buffer. IS_SWAPFILE bypass: swap files skip pagecache locking in the direct I/O path because the kernel manages swap file pages directly. This port drops the old review-only debug toggles, the debug-build BUG path, the extents-leaf fill logging, and broad write-buffer PF_MEMALLOC changes from the original draft. A companion ktest scenario is posted as koverstreet/ktest#63. Originally tested at 128-512M RAM with parallel ablation matrix covering pinning on/off, PF_MEMALLOC on/off, btree cache thrashing, drop_caches, multi-device tiered storage, lz4/zstd compression, sustained pressure, concurrent file I/O workload, and disk-throttled I/O. Signed-off-by: Matthias Schorer <matthias@schorer.dev> Signed-off-by: Darafei Praliaskouski <me@komzpa.net> (cherry picked from commit eb6062c357d0728da2993375394a6cf6690ec3c5)
a2b6341 to
cb4aee8
Compare
682b66e to
073b841
Compare
|
Thanks for the port — it's a conscientious one (authorship preserved, the debug scaffolding from the old draft dropped, and the companion ktest coverage is appreciated). We went through it in detail. The short version: the core swap plumbing is right, but most of the supporting machinery is solving problems the filesystem already solves, and can be deleted outright. That's good news — this PR can shrink to something like 150 lines. The memory-reserve machinery isn't needed. That means: the btree node pinning walk, The reasoning: the write path already runs under memory reclaim today. Ordinary dirty page writeback from kswapd goes through the exact same COW machinery — extent updates, journal, btree node splits — with no swap-specific reserves. If bcachefs needed pre-pinned btree nodes or pre-allocated buffers to complete a write under memory pressure, plain writeback would deadlock without any swapfile involved. The forward-progress guarantee is the one the write path already provides: mempools sized so one I/O can always complete, plus the existing btree cache reserve for interior updates. Swap writeback isn't more constrained than page writeback — it's just louder when it fails. On the specifics:
What stays: The one open design question with real substance is ENOSPC. COW means even overwrites allocate, so a full filesystem can fail swap writes and turn memory pressure into an OOM spiral. The full-file reservation at swapon is one answer, but it needs the reflink/snapshot interaction closed: nothing currently prevents the swapfile being reflinked, or its subvol snapshotted, while active — which invalidates the reservation math exactly when it matters. The other answer is requiring swapfiles to be nocow, like other filesystems require them to be fully allocated: writes go in place, nothing allocates in the swap path, and the problem disappears (at the cost of checksummed/replicated swap). We need to settle that before the next revision — hold off on reworking until we do. (Review by Proof of Concept, discussed with Kent.) |
nocow doesn't inherently address the snapshot and reflink issue, though, right? Per the PoO, “snapshots and reflink still trigger COW semantics even for nocow files.” If so, that will need to addressed orthogonality to whether swap files are nocow. (Perhaps by reserving enough space at the time of snapshot / reflink to guarantee the swapfile can been fully cow'd, or disallowing those operations?) Nocow disables encryption, which seems undesirable for potentially highly-sensitive swap contents. |
|
You guys are waaaaaaaaaaay overthinking this. The IO paths are already memory reclaim safe, there's no need for btree cache pinning or anything like that, it shouldn't be touching nocow. The only thing that needs to be done for swapfile support is whatever the VFS interfaces need so it can be flipped on. |
|
Just rebased this on master to test it out, immediately got a panic when running swapon. I haven't been running my own rebase of the original patchset for a while but I haven't had that happen with that one on the same FS. "trans->restart_count 11, should be 8, last restarted by bch2_swap_rw+0x306/0x4b0 [bcachefs]". drm-panic link |
3bd925d to
492be5d
Compare
References koverstreet/bcachefs#368, duplicate report koverstreet/bcachefs#923, and the earlier draft koverstreet/bcachefs#1072. Companion ktest coverage is in koverstreet/ktest#63.
This ports Matthias Goergens' SWP_FS_OPS swapfile support to current
bcachefs-tools/testing, preserving the original author and keeping the branch focused on the swap implementation.Compared with the old draft, this version deliberately drops:
BUG_ONpath in swap I/Obch2_swap_noreclaim_enabledboot toggle and hot-path global checkPF_MEMALLOCchanges outside swapThis revision also narrows alloc-btree pinning: instead of pinning
BTREE_ID_allocfromPOS_MINtoSPOS_MAX, swap activation now walks the swap file extents and pins the alloc-btree bucket ranges reached by direct extent pointers. Non-direct extent types are rejected during activation for now rather than silently under-pinning them; reflink/indirect support can be added once the desired policy is clear.The latest update tightens
swaponcleanup paths: failed disk reservation now unpins btree nodes, failedadd_swap_extent()unwinds the actual btree reserve and disk reservation, and swapoff removes the actual preallocated btree reserve rather than the requested count.Local validation:
git diff --checkmake -j32 fs/vfs/swap.o fs/vfs/direct.o fs/vfs/fs.o fs/data/write.o fs/btree/cache.o(userspace/FUSE object path; does not compile the kernel-only swap body)make -C /usr/src/linux-headers-7.1-amd64 M=$PWD/tmp/bcachefs-swapfile-dkms-build modules -j32after staging the DKMS tree undertmp/; this compiledsrc/fs/bcachefs/vfs/swap.oand linkedsrc/fs/bcachefs/bcachefs.kosuccessfully. The temporary build tree was removed after validation.build-and-format).