Post

Replies

Boosts

Views

Activity

Reply to OpenZFS on FSKit — Proof of Concept
Kevin, thank you for the detailed responses — this is exactly the kind of clarity we were hoping for. Let me work through your points. On app-sandbox=true The reframe is helpful. We were reading "sandbox" as "locked down" rather than "opt-in capability declaration." That makes much more sense architecturally. The practical follow-on question: what entitlements exist today for block device ioctls (DKIOCGETBLOCKSIZE, DKIOCGETBLOCKCOUNT)? We currently work around their absence with a path→size registry, but if we can simply declare the entitlement, that's the right fix. Similarly for any IPC socket path we'd use for the management layer. On the management plane Good news: we already implemented a UNIX socket approach — a zfsd management daemon that receives ZFS_IOC_* requests over a socket, paired with a libzfs_core transport that connects to it instead of opening /dev/zfs. The open question is whether the sandboxed extension can create and bind a socket at a path reachable by privileged tools outside the sandbox (e.g., /var/run/zfs/zfsd.sock). If that's permitted without a special entitlement, this blocker is resolved for us. Your third option — using the filesystem itself as a control channel — is interesting for in-band dataset operations, but circular for pool-level work (create, import, destroy) that must happen before any dataset is mounted. On zvols The hdiutil/CRawDiskImage suggestion could work for VM disk image consumers (Parallels, VMware) if the extension can present zvol I/O as a file-like object. Two follow-up questions: Is IOUserBlockStorageDevice in DriverKit a viable path for dynamically publishing block devices from userspace? Could an FSKit extension coordinate with a companion DriverKit extension to publish zvols when a pool is imported? Is the hdiutil path accessible from within the extension's sandbox, or does it require coordination with a process outside? On N:M The APFS observation is the most useful validation we could have asked for. Apple knows about it. On ARC memory limits Fair point — we were speculating. We haven't actually hit a limit yet. The concern was that ARC on a 64 GB system typically uses 20–30 GB, and we were anticipating that a sandboxed process holding that much memory would attract attention from the system. The metadataRead suggestion is interesting. We're currently using the standard read path. If metadata reads are accounted to the kernel rather than the process, that could meaningfully reduce ARC's apparent footprint. We'll revisit with actual numbers when we get there. Is there documentation on what memory limits a sandboxed FSKit extension actually operates under? On background operations We were speculating here too — haven't tried it. Good to know threads are unrestricted while mounted. We'll come back if we actually hit a problem. On UBC / buffer cache Thank you for the correction — that's a much clearer framing. The issue isn't UBC access; it's that ZFS needs to sit below the UBC as well as above it, and FSKit currently only provides the top layer. The two-layer architecture you described (IOMedia below UBC for transformation + VFS above for the POSIX layer) is exactly what the kext implements today. You mentioned SCSIControllerDriverKit with UserGetDataBuffer/UserProcessBundledParallelTasks as a potential path for a virtual I/O transformation layer. Is that a supported and documented route for this use case, even if difficult? Or is it more theoretical? If it's genuinely viable, we'd rather know that now than assume the door is closed. Action items We'll file separate bugs for the three hard blockers (management plane, zvols, N:M multi-device/multi-dataset) and post the numbers back here. But at the end of the day, we wanted to test out the fit with FSKit and it is rather pleasing we got it to do "something at all", which is quite hopeful if'en Apple does decide to remove KEXT support. Shiny.
Topic: App & System Services SubTopic: Core OS Tags:
Jun ’26
Reply to OpenZFS on FSKit — Proof of Concept
Technical Findings — Things That Weren't Obvious These cost significant debugging time and aren't documented anywhere: startCheckWithTask: must complete asynchronously Calling [task didCompleteWithError:nil] synchronously inside startCheckWithTask: causes fskitd to receive "task completed" before "task started" over XPC. fskitd rejects this with FSKitErrorDomain Code=27503 "Task didn't start yet" and never spawns the activate instance. The fix is to dispatch the completion with even a 1ms delay so FSKit can send the "started" notification first. app-sandbox=true is required in entitlements ExtensionKit rejects the extension entirely without com.apple.security.app-sandbox=true, even though sandboxing a filesystem extension feels counterintuitive. This needs to be paired with com.apple.developer.fskit.fsmodule=true. Container identifier must equal volume identifier for FSUnaryFileSystem FSContainer.h documents this but it's easy to miss: for unary file systems, the container identifier passed in probeResource: must exactly match the volume identifier used when constructing the FSVolume. A mismatch causes loadResource: to fail with EAGAIN "unexpected container state." What's Missing for Production We see three hard blockers and several important-but-workable gaps. Blocker 1: No management plane Every ZFS management tool — zpool, zfs, zdb, zed, zinject — communicates with the ZFS engine via /dev/zfs ioctls (ZFS_IOC_*). Without an equivalent: No pool create, destroy, import, export, scrub, resilver, or status No snapshots, clones, send/receive, or bookmark management No dataset property management No ZFS event daemon (zed) for fault handling and auto-replacement The need isn't specifically "ioctls" — it's a defined IPC contract between the FSKit-hosted ZFS engine and management tools. Whether that's a character device DEXT, an XPC service exposed by the extension, or a new FSKit API, the mechanism needs to exist. The management tools would be adapted to whatever is provided. Question: Is there a recommended pattern for a filesystem extension to expose a management interface to non-sandboxed privileged tools? Blocker 2: No virtual block device publication ZFS Volumes (zvols) are block devices backed by the ZFS storage pool — used for VM disks, iSCSI targets, swap, and more. The current kext implementation (IOBlockStorageDevice subclass) works because it creates a kernel service that IOKit matches into IOMedia → /dev/diskN. There is no userspace equivalent. DriverKit's IOUserBlockStorageDevice is the closest analog, but IOKit matching is hardware-triggered. There's no mechanism for a filesystem extension discovering a zvol inside a pool to say "please also publish a block device for this." A static DEXT can't know what zvols exist until the pool is imported. Question: What is the intended path for a filesystem to publish virtual block devices — for example, a software RAID layer or a volume manager that needs to create block device nodes dynamically at runtime? Blocker 3: N:M — multiple devices per pool, multiple datasets per mount ZFS has a fundamental mismatch with FSKit's current resource model on two axes: N devices → 1 pool. ZFS pools span multiple block devices: a 3-disk RAIDZ, a 4-disk mirror, etc. FSKit's probe/activate model is one FSBlockDeviceResource per activation. For a RAIDZ pool, all member device fds need to be available at spa_import time. There's no multi-resource activation concept, and no way for probing one member disk to "claim" the others. (Our PoC works only because we used a single-vdev file pool.) 1 pool → M mounts. A pool typically contains many datasets, each with its own mountpoint (tank, tank/home, tank/data, tank/vm). These should each appear as separately mounted volumes. FSUnaryFileSystem is explicitly one volume per activation — there's no mechanism for one pool import to produce multiple mounted volumes. These likely need separate primitives: something like a multi-resource pool probe that coalesces member devices, and a dataset iterator that produces multiple FSVolume instances per pool activation. Question: Is multi-resource activation (multiple FSBlockDeviceResource objects for one filesystem instance) on the roadmap? Is there a pattern for one extension activation to produce multiple mount points? Secondary Gaps (important but not immediate blockers) ARC memory limits. ZFS's Adaptive Replacement Cache is designed to use a significant portion of system RAM. Sandbox memory limits constrain it. A declared "buffer cache" process role with higher memory entitlements would meaningfully improve performance. Background operations. Pool scrub and vdev resilver (RAID rebuild) are long-running background I/O tasks essential for data integrity. There's no mechanism for an FSKit extension to run sustained background work while mounted. Resilver after a disk failure can't wait for user interaction. No unified buffer cache integration. All reads go through the extension process with no kernel page cache sharing. mmap either goes through readFromFile: per page fault or doesn't work at all. This is significant for database and VM image workloads. NFS/SMB re-export. ZFS is widely used as a NAS backend. Correctness of persistent file IDs, fsid stability, and server-side locking semantics under FSKit needs validation. The code is open source at https://github.com/openzfsonosx/openzfs-fork/tree/FSKit Jorgen Lundman Co-Authored-By: Claude Sonnet 4.6
Topic: App & System Services SubTopic: Core OS Tags:
May ’26
Reply to Symbolicating kernel backtraces on Apple Silicon
I am unsure if there has been any progress in this area. But I needed to resolve a recent Tahoe panic, so chatgpt cobbled together this: https://gist.github.com/lundman/54e633a850e7623aae5adab38a39f464 If we are allowed to share? Either way, output was: ./symbolicate_panic.py -p ~/ZFS.2.3.1rc1.kernel.panic.-.Tahoe.M4.Pro.Mac.Mini.txt -k /Library/Extensions/zfs.kext/Contents/MacOS/zfs --kernel --kdk-nearest --accept-mismatch === KEXT mapping === bundle: org.openzfsonosx.zfs arch: arm64e base@: 0xfffffe004400d840 file __TEXT vmaddr: 0x0 file __TEXT_EXEC vmaddr: 0x70000 delta=0x70000 chosen TEXT_LOAD: 0xfffffe0043f9d840 === Kernel mapping (via KDK) === checking for KDK build 25A354 ... nearest: /Library/Developer/KDKs/KDK_26.0_25A353.kdk build: 25A354 SoC: t6041 kernel: /Library/Developer/KDKs/KDK_26.0_25A353.kdk/System/Library/Kernels/kernel.release.t6041 panic Kernel UUID: E67CAF31-8F84-389C-BB27-7FAEC762FA14 KDK Kernel UUID: E67CAF31-8F84-389C-BB27-7FAEC762FA14 file __TEXT vmaddr: 0xfffffe0007004000 file __TEXT_EXEC vmaddr: 0xfffffe00072cc000 delta=0x2c8000 panic Kernel text exec base: 0xfffffe003e2c4000 chosen TEXT_LOAD: 0xfffffe003dffc000 ESR: 0x96000005 -> Data Abort, same EL; Translation fault, level 1 FAR: 0x0000000000000084 === Panicked thread (merged, ordered) === 0xfffffe004401d934 [zfs] taskq_dispatch (in zfs) (spl-taskq.c:1457) 0xfffffe003e3164c0 [kernel] handle_debugger_trap (in kernel.release.t6041) (debug.c:1863) 0xfffffe003e48bc54 [kernel] handle_uncategorized (in kernel.release.t6041) (sleh.c:1818) 0xfffffe003e489e8c [kernel] sleh_synchronous (in kernel.release.t6041) (sleh.c:0) 0xfffffe003e2c7d48 [kernel] fleh_synchronous (in kernel.release.t6041) + 72 0xfffffe003e3167d0 [kernel] DebuggerTrapWithState (in kernel.release.t6041) (debug.c:830) 0xfffffe003ec199c8 [kernel] Assert (in kernel.release.t6041) (debug.c:841) 0xfffffe003ec24c74 [kernel] sleh_synchronous_sp1 (in kernel.release.t6041) (sleh.c:1191) 0xfffffe003e48bac4 [kernel] handle_kernel_abort (in kernel.release.t6041) (sleh.c:3960) 0xfffffe003e489ed0 [kernel] sleh_synchronous (in kernel.release.t6041) (sleh.c:1544) 0xfffffe0044155ab4 [zfs] vdev_disk_io_start (in zfs) (vdev_disk.c:750) 0xfffffe0044140c54 [zfs] zio_vdev_io_start (in zfs) (zio.c:0) 0xfffffe004413bdd8 [zfs] zio_nowait (in zfs) (zio.c:2580) 0xfffffe00440dbb98 [zfs] vdev_probe (in zfs) (vdev.c:1840) 0xfffffe00440dc92c [zfs] vdev_open (in zfs) (vdev.c:2273) 0xfffffe00440e4d90 [zfs] vdev_open_child (in zfs) (vdev.c:1868) 0xfffffe004401ffe4 [zfs] taskq_thread (in zfs) (spl-taskq.c:2144) 0xfffffe0044020574 [zfs] spl_thread_setup (in zfs) (spl-thread.c:128) 0xfffffe003e2c87cc [kernel] Call_continuation (in kernel.release.t6041) + 204 Maybe it will help someone, or, if there is an official way now, please let me know.
Topic: App & System Services SubTopic: Core OS Tags:
Sep ’25
Reply to Looking for prebuilt notary tool for macOS 10.14
Same here, 10.14 and 10.15 both need to be notarized. With 10.15 I can copy the notarytool from Monterey's xcode (13.1), and it runs fine. With 10.14 it does not run due to: dyld: Library not loaded: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit If I copy over a local copy of CryptoKit, I can not set DYLD_LIBRARY_PATH anymore, sad-face. Possibly I could strip it of the codesign certificate and add "com.apple.security.cs.allow-dyld-environment-variables" but that feels like I'm close to yak shaving. 10.13 does not need notarizing, so it is odd Apple left one OS out in the cold. Has anyone come up with a solution that doesn't require me to copy it to a 2nd VM running newer macOS?
Dec ’23
Reply to Has anyone found a way to get cpu_number() on aarch64 ?
To be complete, this is what we went with: #if defined(__aarch64__) uint64_t mpidr_el1; asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr_el1)); /* * To save us looking up number of eCores and pCores, we * just wrap eCores backwards from max_ncpu. * 0: [P0 P1 P2 ... Px Ex .. E2 E1 E0] : max_ncpu * * XNU: Aff2: "1" - PCORE, "0" - ECORE */ #define PCORE_BIT (1ULL << 16) if (mpidr_el1 & PCORE_BIT) return ((uint32_t)mpidr_el1 & 0xff); else return ((max_ncpus -1) - (uint32_t)(mpidr_el1 & 0xff)); #else return ((uint32_t)cpu_number()); #endif }
Topic: App & System Services SubTopic: Core OS Tags:
Mar ’23
Reply to Has anyone found a way to get cpu_number() on aarch64 ?
uint32_t getcpuid(void) { #if defined(__aarch64__) uint64_t mpidr_el1; asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr_el1)); return (uint8_t)(mpidr_el1 & 0xff); #else return ((uint32_t)cpu_number()); #endif } Is a start, on my 8core M2, it returns values 0x00 to 0x03 from Aff0 bits. Generally the 4 fast cores. If you idle for a bit, you can probably look at the Aff1 (cluster) values, returning values like 0x0102 - which probably means the slower cores. But for the sake of reducing contention by spreading out locks over cores, this is a good start.
Topic: App & System Services SubTopic: Core OS Tags:
Mar ’23
Reply to Mac Studio Ventura panic / boot loop with kext
With exhaustive printf debugging - since this is now the best available, we found out the old line: static char initial_default_block[16ULL*1024ULL*1024ULL] __attribute__((aligned(16384))) = { 0 }; Used to bootstrap our memory allocator, would not work on Ventura+M1+more-than-16GB ram. Ie, MacStudio 128G. Monterey on same hardware (As well as all lower M1s, and x86_64) are fine. We replaced it with: initial_default_block = IOMallocAligned(INITIAL_BLOCK_SIZE, 16384); memset(initial_default_block, 0, INITIAL_BLOCK_SIZE); Unlikely anyone else would bump into this issue, but it feels more complete to post the answer.
Topic: App & System Services SubTopic: Core OS Tags:
Nov ’22
Reply to How exactly did 32 bit support get removed in OSX? (read carefully please)
Maybe you could write a program that will dlopen() and 32bit app, the intercept any interlibrary calls, libc, syscall, with glue to translate 32bit to 64bit and back. It would be a massive undertaking, with no guarantee that you wouldn't hit a brick wall at somepoint.. .. when all "they'd" have to do is compile it for 64 bit :) Might even be easier to go translate the 32bit assembler to 64bit in each segment. But that does nothing to help you with the "old" API calls, you'd still need to interpose them with glue.
Topic: App & System Services SubTopic: Core OS Tags:
Aug ’22
Reply to NFS on VFS/ZFS with open(..., O_EXCL) ?
OK, dtracing the debug kernel helped here, as certain functions were not inlined: dtrace -n 'mac*:entry / execname == "nfsd" / {} ' -n 'mac*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'nfs*:entry / execname == "nfsd" / {} ' -n 'nfs*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'vnode*:entry / execname == "nfsd" / {} ' -n 'vnode*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'vn*:entry / execname == "nfsd" / {} ' -n 'vn*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'nfsrv_rephead:entry { nd = arg0 + 0x88; tracemem(nd, 4); printf("rephead %d", arg0);}' Yeah, the whole thing. Anyway, it produced this hint: 1 269114 mac_vnode_check_open:entry 1 269042 mac_cred_check_enforce:entry 1 269043 mac_cred_check_enforce:return 1 nfsd 1 232364 vn_getpath_ext_with_mntlen:entry 1 231850 build_path_with_parent:return 2 nfsd 1 232365 vn_getpath_ext_with_mntlen:return 2 nfsd 1 232364 vn_getpath_ext_with_mntlen:entry 1 231850 build_path_with_parent:return 2 nfsd 1 232365 vn_getpath_ext_with_mntlen:return 2 nfsd 1 268848 mac_error_select:entry 1 268849 mac_error_select:return 2 nfsd 1 269115 mac_vnode_check_open:return 2 nfsd ie, it's not that mac_vnode_check_open() but rather that vn_getpath_ext_with_mntlen() fails. (Technically, build_path_with_parent()). At the end of my zfs_vnop_create() - if I add the call: vnode_update_identity(*ap->a_vpp, NULL, (const char *)ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen, 0, VNODE_UPDATE_NAME); I get a successful test run using O_EXCL over nfs, finally. I am not entirely sure why this is required, only place I call vnode_update_identity() is in vfs_vget(). Otherwise, I've "been getting away" with not calling it all this time. I assume it doesn't hurt to call it.
Topic: App & System Services SubTopic: Core OS Tags:
Jan ’22
Reply to OpenZFS on FSKit — Proof of Concept
Kevin, thank you for the detailed responses — this is exactly the kind of clarity we were hoping for. Let me work through your points. On app-sandbox=true The reframe is helpful. We were reading "sandbox" as "locked down" rather than "opt-in capability declaration." That makes much more sense architecturally. The practical follow-on question: what entitlements exist today for block device ioctls (DKIOCGETBLOCKSIZE, DKIOCGETBLOCKCOUNT)? We currently work around their absence with a path→size registry, but if we can simply declare the entitlement, that's the right fix. Similarly for any IPC socket path we'd use for the management layer. On the management plane Good news: we already implemented a UNIX socket approach — a zfsd management daemon that receives ZFS_IOC_* requests over a socket, paired with a libzfs_core transport that connects to it instead of opening /dev/zfs. The open question is whether the sandboxed extension can create and bind a socket at a path reachable by privileged tools outside the sandbox (e.g., /var/run/zfs/zfsd.sock). If that's permitted without a special entitlement, this blocker is resolved for us. Your third option — using the filesystem itself as a control channel — is interesting for in-band dataset operations, but circular for pool-level work (create, import, destroy) that must happen before any dataset is mounted. On zvols The hdiutil/CRawDiskImage suggestion could work for VM disk image consumers (Parallels, VMware) if the extension can present zvol I/O as a file-like object. Two follow-up questions: Is IOUserBlockStorageDevice in DriverKit a viable path for dynamically publishing block devices from userspace? Could an FSKit extension coordinate with a companion DriverKit extension to publish zvols when a pool is imported? Is the hdiutil path accessible from within the extension's sandbox, or does it require coordination with a process outside? On N:M The APFS observation is the most useful validation we could have asked for. Apple knows about it. On ARC memory limits Fair point — we were speculating. We haven't actually hit a limit yet. The concern was that ARC on a 64 GB system typically uses 20–30 GB, and we were anticipating that a sandboxed process holding that much memory would attract attention from the system. The metadataRead suggestion is interesting. We're currently using the standard read path. If metadata reads are accounted to the kernel rather than the process, that could meaningfully reduce ARC's apparent footprint. We'll revisit with actual numbers when we get there. Is there documentation on what memory limits a sandboxed FSKit extension actually operates under? On background operations We were speculating here too — haven't tried it. Good to know threads are unrestricted while mounted. We'll come back if we actually hit a problem. On UBC / buffer cache Thank you for the correction — that's a much clearer framing. The issue isn't UBC access; it's that ZFS needs to sit below the UBC as well as above it, and FSKit currently only provides the top layer. The two-layer architecture you described (IOMedia below UBC for transformation + VFS above for the POSIX layer) is exactly what the kext implements today. You mentioned SCSIControllerDriverKit with UserGetDataBuffer/UserProcessBundledParallelTasks as a potential path for a virtual I/O transformation layer. Is that a supported and documented route for this use case, even if difficult? Or is it more theoretical? If it's genuinely viable, we'd rather know that now than assume the door is closed. Action items We'll file separate bugs for the three hard blockers (management plane, zvols, N:M multi-device/multi-dataset) and post the numbers back here. But at the end of the day, we wanted to test out the fit with FSKit and it is rather pleasing we got it to do "something at all", which is quite hopeful if'en Apple does decide to remove KEXT support. Shiny.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jun ’26
Reply to OpenZFS on FSKit — Proof of Concept
Technical Findings — Things That Weren't Obvious These cost significant debugging time and aren't documented anywhere: startCheckWithTask: must complete asynchronously Calling [task didCompleteWithError:nil] synchronously inside startCheckWithTask: causes fskitd to receive "task completed" before "task started" over XPC. fskitd rejects this with FSKitErrorDomain Code=27503 "Task didn't start yet" and never spawns the activate instance. The fix is to dispatch the completion with even a 1ms delay so FSKit can send the "started" notification first. app-sandbox=true is required in entitlements ExtensionKit rejects the extension entirely without com.apple.security.app-sandbox=true, even though sandboxing a filesystem extension feels counterintuitive. This needs to be paired with com.apple.developer.fskit.fsmodule=true. Container identifier must equal volume identifier for FSUnaryFileSystem FSContainer.h documents this but it's easy to miss: for unary file systems, the container identifier passed in probeResource: must exactly match the volume identifier used when constructing the FSVolume. A mismatch causes loadResource: to fail with EAGAIN "unexpected container state." What's Missing for Production We see three hard blockers and several important-but-workable gaps. Blocker 1: No management plane Every ZFS management tool — zpool, zfs, zdb, zed, zinject — communicates with the ZFS engine via /dev/zfs ioctls (ZFS_IOC_*). Without an equivalent: No pool create, destroy, import, export, scrub, resilver, or status No snapshots, clones, send/receive, or bookmark management No dataset property management No ZFS event daemon (zed) for fault handling and auto-replacement The need isn't specifically "ioctls" — it's a defined IPC contract between the FSKit-hosted ZFS engine and management tools. Whether that's a character device DEXT, an XPC service exposed by the extension, or a new FSKit API, the mechanism needs to exist. The management tools would be adapted to whatever is provided. Question: Is there a recommended pattern for a filesystem extension to expose a management interface to non-sandboxed privileged tools? Blocker 2: No virtual block device publication ZFS Volumes (zvols) are block devices backed by the ZFS storage pool — used for VM disks, iSCSI targets, swap, and more. The current kext implementation (IOBlockStorageDevice subclass) works because it creates a kernel service that IOKit matches into IOMedia → /dev/diskN. There is no userspace equivalent. DriverKit's IOUserBlockStorageDevice is the closest analog, but IOKit matching is hardware-triggered. There's no mechanism for a filesystem extension discovering a zvol inside a pool to say "please also publish a block device for this." A static DEXT can't know what zvols exist until the pool is imported. Question: What is the intended path for a filesystem to publish virtual block devices — for example, a software RAID layer or a volume manager that needs to create block device nodes dynamically at runtime? Blocker 3: N:M — multiple devices per pool, multiple datasets per mount ZFS has a fundamental mismatch with FSKit's current resource model on two axes: N devices → 1 pool. ZFS pools span multiple block devices: a 3-disk RAIDZ, a 4-disk mirror, etc. FSKit's probe/activate model is one FSBlockDeviceResource per activation. For a RAIDZ pool, all member device fds need to be available at spa_import time. There's no multi-resource activation concept, and no way for probing one member disk to "claim" the others. (Our PoC works only because we used a single-vdev file pool.) 1 pool → M mounts. A pool typically contains many datasets, each with its own mountpoint (tank, tank/home, tank/data, tank/vm). These should each appear as separately mounted volumes. FSUnaryFileSystem is explicitly one volume per activation — there's no mechanism for one pool import to produce multiple mounted volumes. These likely need separate primitives: something like a multi-resource pool probe that coalesces member devices, and a dataset iterator that produces multiple FSVolume instances per pool activation. Question: Is multi-resource activation (multiple FSBlockDeviceResource objects for one filesystem instance) on the roadmap? Is there a pattern for one extension activation to produce multiple mount points? Secondary Gaps (important but not immediate blockers) ARC memory limits. ZFS's Adaptive Replacement Cache is designed to use a significant portion of system RAM. Sandbox memory limits constrain it. A declared "buffer cache" process role with higher memory entitlements would meaningfully improve performance. Background operations. Pool scrub and vdev resilver (RAID rebuild) are long-running background I/O tasks essential for data integrity. There's no mechanism for an FSKit extension to run sustained background work while mounted. Resilver after a disk failure can't wait for user interaction. No unified buffer cache integration. All reads go through the extension process with no kernel page cache sharing. mmap either goes through readFromFile: per page fault or doesn't work at all. This is significant for database and VM image workloads. NFS/SMB re-export. ZFS is widely used as a NAS backend. Correctness of persistent file IDs, fsid stability, and server-side locking semantics under FSKit needs validation. The code is open source at https://github.com/openzfsonosx/openzfs-fork/tree/FSKit Jorgen Lundman Co-Authored-By: Claude Sonnet 4.6
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
May ’26
Reply to Symbolicating kernel backtraces on Apple Silicon
I am unsure if there has been any progress in this area. But I needed to resolve a recent Tahoe panic, so chatgpt cobbled together this: https://gist.github.com/lundman/54e633a850e7623aae5adab38a39f464 If we are allowed to share? Either way, output was: ./symbolicate_panic.py -p ~/ZFS.2.3.1rc1.kernel.panic.-.Tahoe.M4.Pro.Mac.Mini.txt -k /Library/Extensions/zfs.kext/Contents/MacOS/zfs --kernel --kdk-nearest --accept-mismatch === KEXT mapping === bundle: org.openzfsonosx.zfs arch: arm64e base@: 0xfffffe004400d840 file __TEXT vmaddr: 0x0 file __TEXT_EXEC vmaddr: 0x70000 delta=0x70000 chosen TEXT_LOAD: 0xfffffe0043f9d840 === Kernel mapping (via KDK) === checking for KDK build 25A354 ... nearest: /Library/Developer/KDKs/KDK_26.0_25A353.kdk build: 25A354 SoC: t6041 kernel: /Library/Developer/KDKs/KDK_26.0_25A353.kdk/System/Library/Kernels/kernel.release.t6041 panic Kernel UUID: E67CAF31-8F84-389C-BB27-7FAEC762FA14 KDK Kernel UUID: E67CAF31-8F84-389C-BB27-7FAEC762FA14 file __TEXT vmaddr: 0xfffffe0007004000 file __TEXT_EXEC vmaddr: 0xfffffe00072cc000 delta=0x2c8000 panic Kernel text exec base: 0xfffffe003e2c4000 chosen TEXT_LOAD: 0xfffffe003dffc000 ESR: 0x96000005 -> Data Abort, same EL; Translation fault, level 1 FAR: 0x0000000000000084 === Panicked thread (merged, ordered) === 0xfffffe004401d934 [zfs] taskq_dispatch (in zfs) (spl-taskq.c:1457) 0xfffffe003e3164c0 [kernel] handle_debugger_trap (in kernel.release.t6041) (debug.c:1863) 0xfffffe003e48bc54 [kernel] handle_uncategorized (in kernel.release.t6041) (sleh.c:1818) 0xfffffe003e489e8c [kernel] sleh_synchronous (in kernel.release.t6041) (sleh.c:0) 0xfffffe003e2c7d48 [kernel] fleh_synchronous (in kernel.release.t6041) + 72 0xfffffe003e3167d0 [kernel] DebuggerTrapWithState (in kernel.release.t6041) (debug.c:830) 0xfffffe003ec199c8 [kernel] Assert (in kernel.release.t6041) (debug.c:841) 0xfffffe003ec24c74 [kernel] sleh_synchronous_sp1 (in kernel.release.t6041) (sleh.c:1191) 0xfffffe003e48bac4 [kernel] handle_kernel_abort (in kernel.release.t6041) (sleh.c:3960) 0xfffffe003e489ed0 [kernel] sleh_synchronous (in kernel.release.t6041) (sleh.c:1544) 0xfffffe0044155ab4 [zfs] vdev_disk_io_start (in zfs) (vdev_disk.c:750) 0xfffffe0044140c54 [zfs] zio_vdev_io_start (in zfs) (zio.c:0) 0xfffffe004413bdd8 [zfs] zio_nowait (in zfs) (zio.c:2580) 0xfffffe00440dbb98 [zfs] vdev_probe (in zfs) (vdev.c:1840) 0xfffffe00440dc92c [zfs] vdev_open (in zfs) (vdev.c:2273) 0xfffffe00440e4d90 [zfs] vdev_open_child (in zfs) (vdev.c:1868) 0xfffffe004401ffe4 [zfs] taskq_thread (in zfs) (spl-taskq.c:2144) 0xfffffe0044020574 [zfs] spl_thread_setup (in zfs) (spl-thread.c:128) 0xfffffe003e2c87cc [kernel] Call_continuation (in kernel.release.t6041) + 204 Maybe it will help someone, or, if there is an official way now, please let me know.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’25
Reply to Porting VFS kext to FSKit
OK, so wait, now is not the time. Thanks Quinn.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Oct ’24
Reply to Looking for prebuilt notary tool for macOS 10.14
Cooked together a "notarytool" replacement that will do the work for me, and the build environment can remain the same. Instead of calling "xcrun notarytool" I run this: https://gist.github.com/lundman/9166dc8bef1973e5d9fc5428e0cedc57 Nothing special, but might save someone a few minutes having to do it themselves.
Replies
Boosts
Views
Activity
Dec ’23
Reply to Looking for prebuilt notary tool for macOS 10.14
Same here, 10.14 and 10.15 both need to be notarized. With 10.15 I can copy the notarytool from Monterey's xcode (13.1), and it runs fine. With 10.14 it does not run due to: dyld: Library not loaded: /System/Library/Frameworks/CryptoKit.framework/Versions/A/CryptoKit If I copy over a local copy of CryptoKit, I can not set DYLD_LIBRARY_PATH anymore, sad-face. Possibly I could strip it of the codesign certificate and add "com.apple.security.cs.allow-dyld-environment-variables" but that feels like I'm close to yak shaving. 10.13 does not need notarizing, so it is odd Apple left one OS out in the cold. Has anyone come up with a solution that doesn't require me to copy it to a 2nd VM running newer macOS?
Replies
Boosts
Views
Activity
Dec ’23
Reply to Can't load KEXT in VMs on M1
Word is that there now is a work-around, if somewhat complicated. I suspect with a bit more time, it will be streamlined down to something nicer.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Sep ’23
Reply to Can't load KEXT in VMs on M1
This is still an issue, including on the sonoma release. Snapshot API calls are there, but the ability to load kexts is still missing.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jun ’23
Reply to Replacing kernel with KDK kernel in general
Possibly it can be done, but I went to beta3 instead, which has a matching KDK.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Mar ’23
Reply to Has anyone found a way to get cpu_number() on aarch64 ?
To be complete, this is what we went with: #if defined(__aarch64__) uint64_t mpidr_el1; asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr_el1)); /* * To save us looking up number of eCores and pCores, we * just wrap eCores backwards from max_ncpu. * 0: [P0 P1 P2 ... Px Ex .. E2 E1 E0] : max_ncpu * * XNU: Aff2: "1" - PCORE, "0" - ECORE */ #define PCORE_BIT (1ULL << 16) if (mpidr_el1 & PCORE_BIT) return ((uint32_t)mpidr_el1 & 0xff); else return ((max_ncpus -1) - (uint32_t)(mpidr_el1 & 0xff)); #else return ((uint32_t)cpu_number()); #endif }
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Mar ’23
Reply to Has anyone found a way to get cpu_number() on aarch64 ?
uint32_t getcpuid(void) { #if defined(__aarch64__) uint64_t mpidr_el1; asm volatile("mrs %0, mpidr_el1" : "=r" (mpidr_el1)); return (uint8_t)(mpidr_el1 & 0xff); #else return ((uint32_t)cpu_number()); #endif } Is a start, on my 8core M2, it returns values 0x00 to 0x03 from Aff0 bits. Generally the 4 fast cores. If you idle for a bit, you can probably look at the Aff1 (cluster) values, returning values like 0x0102 - which probably means the slower cores. But for the sake of reducing contention by spreading out locks over cores, this is a good start.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Mar ’23
Reply to Mac Studio Ventura panic / boot loop with kext
With exhaustive printf debugging - since this is now the best available, we found out the old line: static char initial_default_block[16ULL*1024ULL*1024ULL] __attribute__((aligned(16384))) = { 0 }; Used to bootstrap our memory allocator, would not work on Ventura+M1+more-than-16GB ram. Ie, MacStudio 128G. Monterey on same hardware (As well as all lower M1s, and x86_64) are fine. We replaced it with: initial_default_block = IOMallocAligned(INITIAL_BLOCK_SIZE, 16384); memset(initial_default_block, 0, INITIAL_BLOCK_SIZE); Unlikely anyone else would bump into this issue, but it feels more complete to post the answer.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Nov ’22
Reply to How exactly did 32 bit support get removed in OSX? (read carefully please)
Maybe you could write a program that will dlopen() and 32bit app, the intercept any interlibrary calls, libc, syscall, with glue to translate 32bit to 64bit and back. It would be a massive undertaking, with no guarantee that you wouldn't hit a brick wall at somepoint.. .. when all "they'd" have to do is compile it for 64 bit :) Might even be easier to go translate the 32bit assembler to 64bit in each segment. But that does nothing to help you with the "old" API calls, you'd still need to interpose them with glue.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Aug ’22
Reply to How to boot into development kernel from KDK?
The KDK readme says: Note: Apple silicon doesn’t support installing the kernel and kernel extension variants from the KDK. So I think you just can't, even now, three major versions later.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Aug ’22
Reply to NFS on VFS/ZFS with open(..., O_EXCL) ?
OK, dtracing the debug kernel helped here, as certain functions were not inlined: dtrace -n 'mac*:entry / execname == "nfsd" / {} ' -n 'mac*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'nfs*:entry / execname == "nfsd" / {} ' -n 'nfs*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'vnode*:entry / execname == "nfsd" / {} ' -n 'vnode*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'vn*:entry / execname == "nfsd" / {} ' -n 'vn*:return / execname == "nfsd" / { printf("%d %s", arg1, execname); }' -n 'nfsrv_rephead:entry { nd = arg0 + 0x88; tracemem(nd, 4); printf("rephead %d", arg0);}' Yeah, the whole thing. Anyway, it produced this hint: 1 269114 mac_vnode_check_open:entry 1 269042 mac_cred_check_enforce:entry 1 269043 mac_cred_check_enforce:return 1 nfsd 1 232364 vn_getpath_ext_with_mntlen:entry 1 231850 build_path_with_parent:return 2 nfsd 1 232365 vn_getpath_ext_with_mntlen:return 2 nfsd 1 232364 vn_getpath_ext_with_mntlen:entry 1 231850 build_path_with_parent:return 2 nfsd 1 232365 vn_getpath_ext_with_mntlen:return 2 nfsd 1 268848 mac_error_select:entry 1 268849 mac_error_select:return 2 nfsd 1 269115 mac_vnode_check_open:return 2 nfsd ie, it's not that mac_vnode_check_open() but rather that vn_getpath_ext_with_mntlen() fails. (Technically, build_path_with_parent()). At the end of my zfs_vnop_create() - if I add the call: vnode_update_identity(*ap->a_vpp, NULL, (const char *)ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen, 0, VNODE_UPDATE_NAME); I get a successful test run using O_EXCL over nfs, finally. I am not entirely sure why this is required, only place I call vnode_update_identity() is in vfs_vget(). Otherwise, I've "been getting away" with not calling it all this time. I assume it doesn't hurt to call it.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jan ’22