No swap partition, no dump partition, working crash dumps, and ZFS — all four at once, with one mechanism. The design is not novel: Apple and illumos arrived at it independently. Pin the object's blocks, extract a physical extent map once, and at I/O time bypass the filesystem entirely, writing raw LBAs. Neither writes through a copy-on-write filesystem; both make COW irrelevant by never dirtying the object through the FS after pinning. ZFS shipped this in production — illumos, 2008 to today. OpenZFS stripped it; FreeBSD never compiled it (FreeBSD 12's in-tree ZFS still carried the whole thing under #ifdef illumos). Restoring it is ~2–4k LOC and every hard primitive survives. The UFS half is much cheaper — ~500–800 LOC — and is the same device path swap partitions already use. This also corrects freebsd-apple-userland-cmds §7: modern dynamic_pager is a 114-line shim that sets vm.swapfileprefix, macx_swapon returns ENOTSUP, and the Mach default pager was removed in 10.12 — so the port needs no Mach traps and buys almost nothing. Companion to EPIC E14.
mediaoffset = 0, mediasize = 0 and ignores offsets entirely. A file wrapper splits writes at extent boundaries and calls the leaf d_dump.zvol_dumpify is a complete shipped precedent. Every primitive OpenZFS needs still exists and is live code: dmu_buf_will_not_fill() (dbuf.c:2973, called internally at 3153 and 3194), the DB_NOFILL write path asserting checksum == OFF || NOPARITY (5500), ZIO_CHECKSUM_NOPARITY, traverse_dataset(), and the multi_vdev_crash_dump feature still registered (zfeature_common.c:383).zvol_strategy() routes all I/O to a dumpified zvol through zvol_dumpio — no DMU, no tx, no ARC, no ZIL, no allocation. Removing allocation from the write path is exactly what kills the swap deadlock.dumpon/savecore changes), but a no-partition design needs ZFS swapfiles anyway — and once swapify exists, dumps come nearly free on the same object. Zvol-only means building two mechanisms.NOMEMWAIT(), KM_PUSHPAGE, a ZFS-sized pageout_reserve, the ARC throttle — and still ships pageout_deadman, which panics after 90 seconds of stuck pageout. OpenZFS #7734 is still open.KM_PUSHPAGE is #defined to plain M_WAITOK (kmem.h:49) and arc_memory_throttle() is return (0); (arc_os.c:124).build.sh calls /cow “swap-backed”, and a swap entry in fstab would be inert anyway because launchd runs no swapon -a (#171).| Source | Says | Reality |
|---|---|---|
nextbsd/nextbsd build.sh 801, 953 | /cow is “tmpfs: dynamic, swap-backed” | false no swap configured anywhere |
| image-strategy survey | live writes “go to RAM (tmpfs/swap)” | false RAM only |
| srclist build plan | swapon DEFER, dumpon DEFER, savecore DROP | accurate |
Measured consequence, #326: /cow at 97 % of 7 GB, 7032M Laundry, swapctl -l empty, OOM-killer taking daemons.
swapon -a or dumpon, so any fstab entry is dead text — the same failure as closed issue #358, where the installer wrote /etc/myname and nothing read it. The activation path must land before or with anything that creates swap.
swap pager / dump wrapper
|
+--------+--------+
| physical extent | built once, at swapon / pin time
| map | never consulted through the FS
+--------+--------+
|
raw bio / d_dump ---> GEOM provider
|
+-------------------+-------------------+
| |
UFS: ufs_bmaparray / VOP_BMAP ZFS: swapified object's DVA list
~500-800 LOC, low risk ~2-4k LOC, moderate risk
(same path swap partitions use) (illumos dumpify port)
After swapon the filesystem is not on the I/O path. That is the whole safety argument, and it is the same one Linux relies on (swap_activate → extent map → submit_bio straight to sis->bdev).
FreeBSD refuses local swapfiles today. sys/vm/swap_pager.c:2680 on releng/15.1, the NextBSD kernel base (2679 on main):
} else if (vp->v_type == VREG &&
(vp->v_mount->mnt_vfc->vfc_flags & VFCF_NETWORK) != 0 &&
(error = VOP_GETATTR(vp, &attr, td->td_ucred)) == 0) {
/*
* Allow direct swapping to NFS regular files in the same
* way that nfs_mountroot() sets up diskless swapping.
So: relax that test for filesystems that can return a full, hole-free block map; walk VOP_BMAP at swapon as Linux's generic_swapfile_activate() does; store the extents in struct swdevt; add a VV_SWAP vnode flag that makes ffs_write, ffs_truncate and ufs_remove refuse with ETXTBSY, mirroring xnu's VSWAP and Linux's S_SWAPFILE; and teach swapon(8) to stop creating an md(4) device for local files.
Preallocation on UFS is already correct if slow: FFS has no vop_allocate of its own, so vop_stdallocate() writes real zero blocks — no holes, which is exactly what the extent walk requires. Note also that ffs_reallocblks only ever relocates dirty, not-yet-written clusters, so a synced file does not move.
Everything below happens with no partition. The files live at the canonical Darwin path /private/var/vm — NextBSD ships /var -> private/var, matching classic Mac OS X's /private/var/vm/swapfile0. On ZFS it is the zroot/private/var/vm dataset, which inherits that mountpoint with no override (§5.1). (NextBSD has no /System/Volumes, so it models the pre-Big Sur layout, not Apple's separate VM volume.)
installer userland (swapd, launchd) kernel
This is the whole design in one picture. Both paths share everything down to sw_strategy; they differ only in what that pointer calls.
| Phase | Steps | Owner |
|---|---|---|
| 0 Install | Create /private/var/vm. On ZFS, create zroot/private/var/vm (inheriting that mountpoint) with checksum=off compression=off dedup=off copies=1 recordsize=128K, no snapshots, no encryption. Do it early, while the pool is clean — a preallocation on a fragmented pool can gang, and gang blocks are fatal to the extent walk (EFRAGS). No partition. No size decision. | installer |
| 1 Boot | launchd starts swapd. Per file: open(O_CREAT, 0600) → preallocate (UFS vop_stdallocate writes real zeros; ZFS dmu_prealloc allocates DVAs with DB_NOFILL) → swapon(2) on the path → kernel walks VOP_BMAP / traverse_dataset into struct swdevt, rejecting holes and gang blocks → sets VV_SWAP → registers swapfile_strategy on a GEOM consumer of the underlying provider → blist_create, swapon_check_swzone. The corefile is pinned the same way and registered with dumper_insert. Rebuilt every boot; never cached. | swapd + kernel |
| 2 Pageout | Free count drops below vmd_free_target → pagedaemon_wakeup → inactive scan moves dirty anon pages to laundry → vm_pageout_laundry_worker (in pageproc) → vm_pageout_flush → swap_pager_putpages: bitmap block allocation, preallocated pbuf, M_NOWAIT metadata → swapfile_strategy → extent lookup → g_new_bio → g_io_request → disk. Completion frees the pages. | kernel |
| 3 Grow | swp_sizecheck sees swap_pager_avail < nswap_lowat (128 pages) → EVENTHANDLER → devctl_notify("VM","swap","LOWSPACE") → swapd creates the next file on xnu's ladder, then runs Phase 1 for it. Back off 15 s on ENOSPC. Refuse past vm.swap_maxpages/2 (4× RAM) unless kern.maxswzone was raised. | swapd |
| 4 Reclaim | Free swap above a high-water mark for N minutes → swapoff(2) the newest file → swap_pager_swapoff pages everything on it back in, synchronously → clear VV_SWAP → unlink. Expensive, and it can fail with ENOMEM if vm_free_count + swap_pager_avail < nblks + nswap_lowat; never default to SWAPOFF_FORCE. | swapd |
| 5 Panic | doadump → dumpsys → wrapper dumper → locate extent, split at extent edges (arm64 minidump writes are unaligned) → leaf d_dump (e.g. ndadump) polls — no interrupts, no allocation → (NULL,0,0) flush fans out. Next boot, savecore in file mode fstat()s the corefile and extracts to /private/var/crash. | kernel |
swapd must open and preallocate a file at exactly the moment memory is short; if it is paged out or blocks, growth stalls. Mitigate by mlocking the daemon and always keeping one pre-created file of headroom so growth is never on the critical path. xnu's real answer is to create files in the kernel (vm_swapfile_create_thread) — that is Increment 5's territory.The dumper contract is just “a callback taking an absolute byte offset, block-aligned, plus a (NULL, 0, 0) flush”. mediaoffset/mediasize are used only for bounds checking and header placement — and netdump already ignores them, in two places (sys/netinet/netdump/netdump_client.c:584 and :745):
dumper.mediaoffset = 0;
dumper.mediasize = 0;
A file-backed wrapper therefore sets mediaoffset = 0, mediasize = file length, blocksize = 4096, maxiosize = min leaf d_maxsize, and on each write locates the extent, splits at extent boundaries, and calls the leaf d_dump. EKCD encryption and zstd compression layer above dump_write and need nothing. Splitting is mandatory, not optional: arm64 minidump_machdep.c emits writes that are not extent-aligned.
Userland is small: dumpon(8) gains an S_ISREG path routing DIOCSKERNELDUMP through VOP_IOCTL (~100 LOC); savecore(8) gains a file mode using fstat() in place of DIOCGMEDIASIZE (~150 LOC).
Apple's reference implementation is kern_open_file_for_direct_io() (bsd/kern/kern_symfile.c), run at boot: vnode_setsize(IO_NOZEROFILL), FSCTL_FREEZE_EXTENTS (fsctl.h: “Mark file's extents as 'frozen' because someone has references to physical address”), DKIOCLOCKPHYSICALEXTENTS, VNOP_BLOCKMAP(VNODE_WRITE), DKIOCGETPHYSICALEXTENT. The same map backs hibernation.
| Driver | Dump | Note |
|---|---|---|
NVMe (nda) | works | ndadump() nvme_da.c:583, d_dump at 1011; polls via cam_periph_runccb when dumping. The Pi 500+ boots from NVMe. |
| virtio-blk | works | vtblk_dump — test the whole design in a VM first |
| SDHCI | works | sdhci_generic_request spins when dumping |
dwmmc | ~30 LOC | no dumping path; would hang |
USB (umass) | fragile | works in principle; historically least reliable at panic |
illumos does not make ZFS COW-safe. It makes COW irrelevant: once ZVOL_DUMPIFIED is set, every read and write bypasses the DMU, so nothing ever dirties the object again, so no block pointer is rewritten, so the captured DVAs stay valid.
| Step | illumos |
|---|---|
| 1. Discard | dmu_free_long_range + txg_wait_synced |
| 2. Freeze properties | compression=off checksum=off dedup=off refreservation=0, 128 K fixed blocks |
| 3. Preallocate | zvol_prealloc() → dmu_prealloc() → dmu_buf_will_not_fill() per block — real DVAs, no data written (DB_NOFILL) |
| 4. Map | zvol_get_lbas() → traverse_dataset(), coalesce adjacent DVAs, reject gang blocks (EFRAGS — dumpadm's “too fragmented”) |
| 5. I/O | zvol_dumpio() → vdev_lookup_top → vdev_op_dumpio — no DMU, no tx, no ARC, no ZIL, no allocation |
Step 5 is why this solves swap and not only dumps. The deadlock is allocation-under-pressure; removing allocation from the write path removes the deadlock.
The one difference from Apple: APFS does not checksum file data, so raw overwrites read back normally. ZFS does — hence checksum=off, which illumos already handled, and which the surviving DB_NOFILL assert still enforces.
Each needs a refusal check at pin time, and these are the substance of the ZFS work beyond the port itself:
| device removal / indirect vdevs | DVAs get remapped — refuse, or re-walk |
| raidz expansion | relocates data — refuse while pinned |
| block cloning / BRT | FICLONE on a pinned file must be refused |
| dRAID | no dumpio precedent — unsupported |
| encrypted datasets | illumos already refuses; encrypt in the pager instead, as xnu does |
| snapshots / clones | a bypass write mutates the snapshot's view — refuse on the dump/swap dataset |
| resilver | copies to the same offset; needs ordering against concurrent bypass writes unverified |
NextBSD has no ZFS dataset hierarchy yet — the only name in use anywhere is the boot environment, zroot/ROOT/default (image-strategy survey). So this plan sets the precedent, and it should follow FreeBSD's own bsdinstall convention translated to the Darwin /private tree:
zroot/ROOT/default mountpoint=/ <- boot environment
zroot/private mountpoint=/private canmount=off
zroot/private/var canmount=off (inherits /private/var)
zroot/private/var/vm real dataset (inherits /private/var/vm)
zroot/private/var/crash real dataset (inherits /private/var/crash)
The dataset name matches the real path, so one explicit mountpoint on zroot/private places everything beneath it. zroot/var/vm would name the /var symlink rather than the directory — the same mistake as writing /var/vm.
canmount=off parents keep the boot environment intact. They exist to provide the naming and mountpoint hierarchy without mounting anything, so /private/etc and the bulk of /private/var stay inside zroot/ROOT/default and roll back with it; only vm and crash are split out. This is exactly bsdinstall's zroot/var + zroot/var/crash/log pattern. The pinned dataset must live outside ROOT in any case — bectl snapshots the boot environment, and a pinned dataset under it would be hit on every BE create.
vm and crash are deliberately separate. The pinned corefile lives in vm with checksum=off; savecore extracts it into crash as an ordinary file that should be checksummed, and that takes bsdinstall's exec=off setuid=off.
Properties on zroot/private/var/vm: checksum=off compression=off dedup=off copies=1 recordsize=128K, no encryption, and com.sun:auto-snapshot=false (which the OpenZFS FAQ already recommends for swap).
zfs snapshot -r zroot/private@backup is all-or-nothing, so if Z4 simply refused a snapshot of the pinned child, the entire recursive snapshot would fail — silently breaking backup scripts, sanoid and zfs-auto-snapshot. Z4 must instead skip pinned datasets during recursive snapshot and send -R, and refuse only a snapshot aimed at the pinned dataset directly. This is the real reason Apple keeps VM on a separate volume: it is excluded from Data's snapshots by construction. Nesting it gives the right name and path, at the cost of handling that exclusion explicitly.It is the obvious idea, it is what xnu appears to do, and it is not the safety argument.
xnu's TH_OPT_VMPRIV is real — an exclusive page reserve that only privileged threads may drain, with privileged waiters woken first. But it is only sufficient because preallocation and VSWAP remove allocation from the pageout path in the first place. The privilege cannot help if the swap thread blocks on a lock held by an unprivileged thread parked in vm_page_wait().
illumos built the full reserve design for ZFS — NOMEMWAIT(), KM_PUSHPAGE, pageout_reserve sized “at least 4MB for use by ZFS”, the arc_memory_throttle pageout branch — and still ships pageout_deadman, which panics after 90 seconds of stuck pageout. That is what “narrows but does not close” looks like in production.
On FreeBSD it is weaker again:
/* include/os/freebsd/spl/sys/kmem.h */
#define KM_SLEEP M_WAITOK
#define KM_PUSHPAGE M_WAITOK /* the illumos reserve semantic is gone */
/* module/os/freebsd/zfs/arc_os.c:124 */
arc_memory_throttle(spa_t *spa, uint64_t reserve, uint64_t txg)
{
return (0);
}
And a structural point no privilege can fix: with any DMU write the data stays dirty in ARC until txg sync, so a swap write through the DMU cannot free memory faster than txg sync.
TDP_VMPRIV per-thread flag (~300–500 LOC in sys/vm) to replace the blunt curproc == pageproc identity check, synchronous dispatch for swap objects, and the arc_memory_throttle pageout branch. Do not let any of it carry the correctness argument. Tracked as K8.zfs_putpages takes a rangelock and then calls dmu_tx_assign(tx, DMU_TX_WAIT) in pageout context for dirty mmap'd pages — and zfs_vnops_os.c:131–155 documents that precise pattern as the deadlock shape in its own header comment. Every NextBSD system on ZFS root has this today. Worth its own ticket upstream.| Increment | Effort | Buys | |
|---|---|---|---|
| 1 | Extent-map swap pager + native file swapon on UFS; VV_SWAP guard; swapon(8) drops the md detour | ~500–800 LOC | safe swapfiles on UFS, no partition, same device path partitions already use |
| 2 | File-backed dumper wrapper + dumpon/savecore file mode | ~400 kernel ~250 userland | crash dumps with no dump device; reuses Increment 1's map |
| 3 | ZFS swapify: vdev_op_dumpio for geom/mirror/raidz, reinstate dmu_prealloc, extent walk, DSL guards | ~2–4k LOC | the same two features on ZFS root |
| 4 | Userland swapd: grow/reclaim files on pressure, xnu size ladder, 15 s backoff, GC | 1–2 wk | dynamic sizing; the dynamic_pager behaviour, minus Mach |
| 5 | Decouple reclaim from swap-I/O completion (compressor-style staging) | research | a stalled swap write slows the system instead of wedging the pagedaemon |
| skip | Full Mach external pager | months | nothing — xnu abandoned it in 10.12 |
It mostly dissolves. With files grown on demand there is no install-time number to get wrong — which is the real lesson of the Debian incident where 384 GB of RAM on a 512 GB SSD produced a 400 GB swap partition. The installer creates a dataset or directory, not a sized slice.
Two bounds remain, and swapd must respect both:
swap_maxpages = 8 × physical pages by default, and swapon_check_swzone() warns above half that — i.e. more than 4 × RAM of configured swap. Raise kern.maxswzone in loader.conf if more is wanted; the zone is KVA-reserved, so it is cheap on arm64.free_space_to_leave), and back off 15 s after ENOSPC.For reference, FreeBSD's own installer never used a RAM multiple: part_wizard.c:54 is MIN(available/20, 4 GB), disk only. The Handbook's “double the size of physical memory” and tuning(7)'s page-scanner claim are contradicted by that code and cite no mechanism.
checksum=off is mandatory for bypass writes. Acceptable for swap and dumps; must be documented, and confined to a dedicated dataset so nothing else inherits it.zvol_get_lbas on DKIOCDUMPINIT. Never cache it across boots.checksum=noparity exists. Port carefully, or restrict to single/mirror top-levels first; NextBSD's realistic targets are single NVMe or a mirror.swapon and dumpon. That work is not in this plan's tracks; it is #171's.virt-10-0 mounts its root with soft-updates, so this had to be settled before K1. On releng/15.1, soft-updates' write hook (buf_start on a buffer's b_dep) and UFS snapshot copy-on-write (ffs_copyonwrite, called only at ffs_vfsops.c:2366 and :2378) both live in ffs_geom_strategy (ffs_vfsops.c:2334), so they run only for writes through the buffer cache; a bypass bio has no buffer and no dependency list. fsync(MNT_WAIT) flushes every inode dependency, every dirty buffer and then softdep_fsync, looping until nothing is dirty or in flight (ffs_vnops.c:226-258), and SU+J takes the same path (DOINGSOFTDEP). Two requirements follow for pin time: fsync(MNT_WAIT) and then vinvalbuf(), which drops the file's cached buffers and pages (bufobj_invalbuf calls vm_object_page_remove). Bypass writes also skip snapshot copy-on-write, so a UFS snapshot's image of the swapfile holds live swap contents — harmless, since nothing that uses snapshots interprets swap or dump data./private/var/vm. NextBSD ships /var -> private/var (verified on virt-10-0) and has no /System/Volumes, so it follows the classic Mac OS X layout (/private/var/vm/swapfile0) rather than Big Sur's separate VM volume. Swapfiles and the corefile share it; on ZFS it is the zroot/private/var/vm dataset (§5.1). Stored paths (vm.swapfileprefix, fstab) use the canonical form, not the symlink.zroot/private, zroot/private/var, vm and crash; whether log, tmp, audit and /private/tmp get datasets is an installer decision beyond this plan, but it should be made once, with this layout as the template.nextbsd-kernel (at ddad2c1) touch sys/vm/, sys/dev/md/, UFS/FFS, the VFS layer, GEOM, kern_shutdown.c or the bundled OpenZFS, and src-overlay/ only adds new files. The kernel builds from releng/15.1, not stable/15 as first assumed. K1 is written against stock code.zfs_putpages exposure worth an upstream OpenZFS report before any of this lands?zfs_prop.c still accept noparity as a checksum index? Only matters if raidz is supported.EPIC E14 — VM, paging & filesystems, label area:vm (new, in four repos). Full bodies for every ticket are on the E14 ticket drafts page, which is generated from the same source as this table. All of them were filed on 2026-09-20 under epic nextbsd#468.
| # | Ticket | Repo | Depends on | Issue |
|---|---|---|---|---|
| K1 | Extent-map swap pager: accept local VREG swapon on filesystems with a real VOP_BMAP | nextbsd-kernel | — | nextbsd-kernel#214 |
| K2 | VV_SWAP: refuse write, truncate and unlink on a pinned swap or dump file | nextbsd-kernel | K1 | nextbsd-kernel#215 |
| K3 | swapon(8): stop creating an md(4) device for local swapfiles | nextbsd-freebsd-compat | K1 | nextbsd-freebsd-compat#51 |
| K4 | File-backed kernel dumper: write crash dumps through a pinned file's extent map | nextbsd-kernel | K1 | nextbsd-kernel#216 |
| K5 | dumpon(8)/savecore(8): regular-file mode | nextbsd-freebsd-compat | K4 | nextbsd-freebsd-compat#52 |
| K6 | g_part: allow GEOM::kerneldump on freebsd-ufs/freebsd-zfs partitions for the file dumper | nextbsd-kernel | K4 | nextbsd-kernel#217 |
| K7 | Swap low-space event: surface swp_sizecheck transitions to userland via devctl | nextbsd-kernel | — | nextbsd-kernel#218 |
| K8 | Defence-in-depth VM privileges: TDP_VMPRIV, sync dispatch for swap objects, ARC pageout throttle | nextbsd-kernel | K1 | nextbsd-kernel#219 |
| Z0 | kernel: compile ZFS into NEXTBSD (options ZFS, no module tree) and ship the ZFS userland | nextbsd-kernel | — | nextbsd-kernel#230 |
| Z1 | vdev_op_dumpio for vdev_geom, mirror and raidz | nextbsd-kernel | K6 | nextbsd-kernel#220 |
| Z2 | Reinstate dmu_prealloc() | nextbsd-kernel | — | nextbsd-kernel#221 |
| Z3 | Swapify: pin a ZFS file object -- property freeze, preallocation, DVA extent walk, DMU bypass | nextbsd-kernel | Z1, Z2 | nextbsd-kernel#222 |
| Z4 | DSL guards for pinned objects -- and skip, not fail, them in recursive snapshot/send | nextbsd-kernel | Z3 | nextbsd-kernel#223 |
| Z5 | Pager-level swap encryption (geli cannot cover a sub-range of a provider) | nextbsd-kernel | K1 | nextbsd-kernel#224 |
| P1 | rpi5: verify NVMe polled crash dump on the Pi 500+ | nextbsd-kernel | K4 | nextbsd-kernel#225 |
| P2 | dwmmc: add a dumping path so d_dump does not hang | nextbsd-kernel | — | nextbsd-kernel#226 |
| P3 | CI: virtio-blk crash-dump harness so the dump path is tested before hardware | nextbsd | K4 | nextbsd#469 |
| U1 | swapd: grow and reclaim swapfiles under memory pressure | nextbsd-userland | K1, K7 | nextbsd-userland#178 |
| U2 | installer: create /private/var/vm (ZFS: zroot/private/var/vm) -- no partition, no sizing | nextbsd-userland | U1 | nextbsd-userland#179 |
| U3 | vm_stat / pagesize report real numbers from vm.stats instead of the 'feature not available' stub | nextbsd-userland | — | nextbsd-userland#180 |
| U4 | installer: ZFS install path -- F8 on the disk picker, pool layout, options and review (preliminary design) | nextbsd-userland | Z0, U2 | nextbsd-userland#201 |
| S1 | Spike: decouple page reclaim from swap-I/O completion (compressor-style staging) | nextbsd-kernel | K1 | nextbsd-kernel#227 |
| X1 | Report upstream: zfs_putpages calls dmu_tx_assign(DMU_TX_WAIT) in pageout context | nextbsd | — | nextbsd#470 |
Order: K1 -> K2 / K3 / K4 / K7 -> K5 / K6 -> U1 -> U2. Z0 first for everything ZFS, then Z2 and Z1 (after K6) in parallel, then Z3 -> Z4. K8, Z5, S1 follow K1. U4 after Z0 and U2, once its design is refined. P2, U3 and X1 are independent.
#393 (wired memory) was moved here from E11 on 2026-09-20, and is labelled area:vm. Linked, not sub-issues: nextbsd#326, nextbsd#329, nextbsd-userland#171, nextbsd-userland#54, nextbsd#410, nextbsd#428, nextbsd#66, nextbsd#350.
FreeBSD from freebsd/freebsd-src main, with the swap-pager lines re-checked on releng/15.1 (the NextBSD kernel base, nextbsd-kernel/.github/workflows/build.yml); ZFS from openzfs/zfs master; illumos from illumos/illumos-gate; xnu from apple-oss-distributions/xnu.
Verified directly from source while writing this: swap_pager.c:2680 on releng/15.1 (VFCF_NETWORK), netdump_client.c:584,745 (mediaoffset/mediasize zero), nvme_da.c:583,1011 (ndadump), dbuf.c:2973,5500 (dmu_buf_will_not_fill + NOPARITY assert), zfeature_common.c:383 (multi_vdev_crash_dump), kmem.h:49 (KM_PUSHPAGE), arc_os.c:124 (throttle stub), zfs_vnops_os.c:131–155 (the documented deadlock pattern), part_wizard.c:54, rc.d/growfs:66, RPI.conf.
Observed on hardware: macOS 27.0 — vm.swapfileprefix = /System/Volumes/VM/swapfile, VM is APFS volume disk3s6 (noexec,noatime,nobrowse), vm.swapusage 0.00M (encrypted) with vm.compressor_bytes_used 4.07 GB, dynamic_pager usage [-F filename]. virt-10-0 — NextBSD arm64, 4 GB, 62 GB UFS root, swapinfo empty, no dynamic_pager.
Unverified — do not rely on without checking: APFS internals (closed source; FSCTL_FREEZE_EXTENTS semantics inferred from the XNU call sequence and fsctl.h, and HFS does not implement it); how illumos orders resilver against concurrent dumpio writes; illumos mirror/raidz vdev_op_dumpio details; whether OpenZFS' zfs_prop.c still accepts noparity; whether the mmc bus layer is dump-safe on all arm64 hosts; FreeBSD wiki and Bugzilla (PR 181565, 254721) are behind anti-bot protection and were read only via search snippets.