<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="https://openzfsonosx.org/w/skins/common/feed.css?303"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>https://openzfsonosx.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=101.175.67.14</id>
		<title>OpenZFS on OS X - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="https://openzfsonosx.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=101.175.67.14"/>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Special:Contributions/101.175.67.14"/>
		<updated>2026-05-09T21:00:17Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.22.3</generator>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-21T22:53:18Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Detecting memory handling errors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have been logged the next time the modified after free buffer was allocated.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Unit Test ==&lt;br /&gt;
&lt;br /&gt;
We have created an initial port of the standard ZFS test suite. It consists of a collection of scripts and miscellaneous utility programs and exercise the complete breadth and depth of the ZFS filesystem. &lt;br /&gt;
&lt;br /&gt;
The tests are best run in a virtual machine with a baseline configured setup that has been captured in a snapshot. The tests should be run on the VM, and then due to the destructive nature of the tests, the VM should be reverted to the snapshot in preparation for future test runs.  The tests take 2-4 hours to run depending on hardware setup.&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
The user zfs-test needs to be able to run sudo without issuing a password. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   zfs-tests ALL=(ALL) NOPASSWD: ALL&lt;br /&gt;
&lt;br /&gt;
The sudo root environment must be configured to pass certain enviroment variables from zfs-test through to the root environment. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   Defaults env_keep += &amp;quot;__ZFS_MAIN_MOUNTPOINT_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Modify /etc/bashrc to contain&lt;br /&gt;
&lt;br /&gt;
   export __ZFS_MAIN_MOUNTPOINT_DIR=&amp;quot;/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If your development directory is ~you/Developer, clone zfs, spl and bfs-tests into that directory&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs-test.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/spl.git&lt;br /&gt;
&lt;br /&gt;
Build the ZFS is built using the building from source instructions.&lt;br /&gt;
&lt;br /&gt;
Ensure that /var/tmp has approximately 100GB of free space.&lt;br /&gt;
&lt;br /&gt;
Create theee virtual hard drives of 10-20GB capacity each.&lt;br /&gt;
&lt;br /&gt;
=== Run Test Suite ===&lt;br /&gt;
&lt;br /&gt;
Setup the tests to run&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer/zfs-tests&lt;br /&gt;
   # ./autogen.sh&lt;br /&gt;
   # ./configure CC=clang CXX=clang++&lt;br /&gt;
&lt;br /&gt;
Edit the generated Makefile, change the recipe for the test_hw target such that your three virtual disks are listed in the DISKS environment variable.&lt;br /&gt;
&lt;br /&gt;
   test_hw: test_verify test/zfs-tests/cmd&lt;br /&gt;
           @KEEP=&amp;quot;`zpool list -H -oname`&amp;quot; \&lt;br /&gt;
            STF_TOOLS=$(abs_top_srcdir)/test/test-runner/stf \&lt;br /&gt;
            STF_SUITE=$(abs_top_srcdir)/test/zfs-tests \&lt;br /&gt;
            DISKS=&amp;quot;/dev/disk3 /dev/disk1 /dev/disk2&amp;quot; \&lt;br /&gt;
            su zfs-tests -c &amp;quot;ksh $(abs_top_srcdir)/test/zfs-tests/cmd/scripts/zfstest.ksh $$RUNFILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Run the test suite&lt;br /&gt;
&lt;br /&gt;
   sudo make test_hw&lt;br /&gt;
&lt;br /&gt;
=== Results ===&lt;br /&gt;
&lt;br /&gt;
The test suite write summary pass/fail information to the console as they run. On completion of the test run summary statistics are written to the console.&lt;br /&gt;
&lt;br /&gt;
Test log files are stored in /var/tmp/&amp;lt;testrun&amp;gt; (where test run is a unique looking number). In that directory there is a log file, and a directory per test. Within the test directory is detailed log information regarding the specific test.&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-21T22:34:57Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Run Test Suite */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have logged on the next allocated after the free and modify occurred.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Unit Test ==&lt;br /&gt;
&lt;br /&gt;
We have created an initial port of the standard ZFS test suite. It consists of a collection of scripts and miscellaneous utility programs and exercise the complete breadth and depth of the ZFS filesystem. &lt;br /&gt;
&lt;br /&gt;
The tests are best run in a virtual machine with a baseline configured setup that has been captured in a snapshot. The tests should be run on the VM, and then due to the destructive nature of the tests, the VM should be reverted to the snapshot in preparation for future test runs.  The tests take 2-4 hours to run depending on hardware setup.&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
The user zfs-test needs to be able to run sudo without issuing a password. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   zfs-tests ALL=(ALL) NOPASSWD: ALL&lt;br /&gt;
&lt;br /&gt;
The sudo root environment must be configured to pass certain enviroment variables from zfs-test through to the root environment. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   Defaults env_keep += &amp;quot;__ZFS_MAIN_MOUNTPOINT_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Modify /etc/bashrc to contain&lt;br /&gt;
&lt;br /&gt;
   export __ZFS_MAIN_MOUNTPOINT_DIR=&amp;quot;/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If your development directory is ~you/Developer, clone zfs, spl and bfs-tests into that directory&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs-test.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/spl.git&lt;br /&gt;
&lt;br /&gt;
Build the ZFS is built using the building from source instructions.&lt;br /&gt;
&lt;br /&gt;
Ensure that /var/tmp has approximately 100GB of free space.&lt;br /&gt;
&lt;br /&gt;
Create theee virtual hard drives of 10-20GB capacity each.&lt;br /&gt;
&lt;br /&gt;
=== Run Test Suite ===&lt;br /&gt;
&lt;br /&gt;
Setup the tests to run&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer/zfs-tests&lt;br /&gt;
   # ./autogen.sh&lt;br /&gt;
   # ./configure CC=clang CXX=clang++&lt;br /&gt;
&lt;br /&gt;
Edit the generated Makefile, change the recipe for the test_hw target such that your three virtual disks are listed in the DISKS environment variable.&lt;br /&gt;
&lt;br /&gt;
   test_hw: test_verify test/zfs-tests/cmd&lt;br /&gt;
           @KEEP=&amp;quot;`zpool list -H -oname`&amp;quot; \&lt;br /&gt;
            STF_TOOLS=$(abs_top_srcdir)/test/test-runner/stf \&lt;br /&gt;
            STF_SUITE=$(abs_top_srcdir)/test/zfs-tests \&lt;br /&gt;
            DISKS=&amp;quot;/dev/disk3 /dev/disk1 /dev/disk2&amp;quot; \&lt;br /&gt;
            su zfs-tests -c &amp;quot;ksh $(abs_top_srcdir)/test/zfs-tests/cmd/scripts/zfstest.ksh $$RUNFILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Run the test suite&lt;br /&gt;
&lt;br /&gt;
   sudo make test_hw&lt;br /&gt;
&lt;br /&gt;
=== Results ===&lt;br /&gt;
&lt;br /&gt;
The test suite write summary pass/fail information to the console as they run. On completion of the test run summary statistics are written to the console.&lt;br /&gt;
&lt;br /&gt;
Test log files are stored in /var/tmp/&amp;lt;testrun&amp;gt; (where test run is a unique looking number). In that directory there is a log file, and a directory per test. Within the test directory is detailed log information regarding the specific test.&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T05:10:11Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: added unit test outline&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have logged on the next allocated after the free and modify occurred.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Unit Test ==&lt;br /&gt;
&lt;br /&gt;
We have created an initial port of the standard ZFS test suite. It consists of a collection of scripts and miscellaneous utility programs and exercise the complete breadth and depth of the ZFS filesystem. &lt;br /&gt;
&lt;br /&gt;
The tests are best run in a virtual machine with a baseline configured setup that has been captured in a snapshot. The tests should be run on the VM, and then due to the destructive nature of the tests, the VM should be reverted to the snapshot in preparation for future test runs.  The tests take 2-4 hours to run depending on hardware setup.&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
The user zfs-test needs to be able to run sudo without issuing a password. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   zfs-tests ALL=(ALL) NOPASSWD: ALL&lt;br /&gt;
&lt;br /&gt;
The sudo root environment must be configured to pass certain enviroment variables from zfs-test through to the root environment. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   Defaults env_keep += &amp;quot;__ZFS_MAIN_MOUNTPOINT_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Modify /etc/bashrc to contain&lt;br /&gt;
&lt;br /&gt;
   export __ZFS_MAIN_MOUNTPOINT_DIR=&amp;quot;/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If your development directory is ~you/Developer, clone zfs, spl and bfs-tests into that directory&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs-test.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/spl.git&lt;br /&gt;
&lt;br /&gt;
Build the ZFS is built using the building from source instructions.&lt;br /&gt;
&lt;br /&gt;
Ensure that /var/tmp has approximately 100GB of free space.&lt;br /&gt;
&lt;br /&gt;
Create theee virtual hard drives of 10-20GB capacity each.&lt;br /&gt;
&lt;br /&gt;
=== Run Test Suite ===&lt;br /&gt;
&lt;br /&gt;
Setup the tests to run&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer/zfs-tests&lt;br /&gt;
   # ./autogen.sh&lt;br /&gt;
   # ./configure CC=clang CXX=clang++&lt;br /&gt;
&lt;br /&gt;
Edit the generated Makefile, change the recipe for the test_hw target such that your three virtual disks are listed in the DISKS environment variable.&lt;br /&gt;
&lt;br /&gt;
   test_hw: test_verify test/zfs-tests/cmd&lt;br /&gt;
           @KEEP=&amp;quot;`zpool list -H -oname`&amp;quot; \&lt;br /&gt;
            STF_TOOLS=$(abs_top_srcdir)/test/test-runner/stf \&lt;br /&gt;
            STF_SUITE=$(abs_top_srcdir)/test/zfs-tests \&lt;br /&gt;
            DISKS=&amp;quot;/dev/disk3 /dev/disk1 /dev/disk2&amp;quot; \&lt;br /&gt;
            su zfs-tests -c &amp;quot;ksh $(abs_top_srcdir)/test/zfs-tests/cmd/scripts/zfstest.ksh $$RUNFILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Run the test suite&lt;br /&gt;
&lt;br /&gt;
   sudo make test&lt;br /&gt;
&lt;br /&gt;
=== Results ===&lt;br /&gt;
&lt;br /&gt;
The test suite write summary pass/fail information to the console as they run. On completion of the test run summary statistics are written to the console.&lt;br /&gt;
&lt;br /&gt;
Test log files are stored in /var/tmp/&amp;lt;testrun&amp;gt; (where test run is a unique looking number). In that directory there is a log file, and a directory per test. Within the test directory is detailed log information regarding the specific test.&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T05:09:33Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Unit Test */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have logged on the next allocated after the free and modify occurred.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Unit Test ==&lt;br /&gt;
&lt;br /&gt;
We have created an initial port of the standard ZFS test suite. It consists of a collection of scripts and miscellaneous utility programs and exercise the complete breadth and depth of the ZFS filesystem. &lt;br /&gt;
&lt;br /&gt;
The tests are best run in a virtual machine with a baseline configured setup that has been captured in a snapshot. The tests should be run on the VM, and then due to the destructive nature of the tests, the VM should be reverted to the snapshot in preparation for future test runs.  The tests take 2-4 hours to run depending on hardware setup.&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
The user zfs-test needs to be able to run sudo without issuing a password. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   zfs-tests ALL=(ALL) NOPASSWD: ALL&lt;br /&gt;
&lt;br /&gt;
The sudo root environment must be configured to pass certain enviroment variables from zfs-test through to the root environment. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   Defaults env_keep += &amp;quot;__ZFS_MAIN_MOUNTPOINT_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Modify /etc/bashrc to contain&lt;br /&gt;
&lt;br /&gt;
   export __ZFS_MAIN_MOUNTPOINT_DIR=&amp;quot;/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If your development directory is ~you/Developer, clone zfs, spl and bfs-tests into that directory&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs-test.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/spl.git&lt;br /&gt;
&lt;br /&gt;
Build the ZFS is built using the building from source instructions.&lt;br /&gt;
&lt;br /&gt;
Ensure that /var/tmp has approximately 100GB of free space.&lt;br /&gt;
&lt;br /&gt;
Create theee virtual hard drives of 10-20GB capacity each.&lt;br /&gt;
&lt;br /&gt;
=== Run Test Suite ===&lt;br /&gt;
&lt;br /&gt;
Setup the tests to run&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer/zfs-tests&lt;br /&gt;
   # ./autogen.sh&lt;br /&gt;
   # ./configure CC=clang CXX=clang++&lt;br /&gt;
&lt;br /&gt;
Edit the generated Makefile, change the recipe for the test_hw target such that your three virtual disks are listed in the DISKS environment variable.&lt;br /&gt;
&lt;br /&gt;
   test_hw: test_verify test/zfs-tests/cmd&lt;br /&gt;
           @KEEP=&amp;quot;`zpool list -H -oname`&amp;quot; \&lt;br /&gt;
            STF_TOOLS=$(abs_top_srcdir)/test/test-runner/stf \&lt;br /&gt;
            STF_SUITE=$(abs_top_srcdir)/test/zfs-tests \&lt;br /&gt;
            DISKS=&amp;quot;/dev/disk3 /dev/disk1 /dev/disk2&amp;quot; \&lt;br /&gt;
            su zfs-tests -c &amp;quot;ksh $(abs_top_srcdir)/test/zfs-tests/cmd/scripts/zfstest.ksh $$RUNFILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Run the test suite&lt;br /&gt;
&lt;br /&gt;
   sudo make test&lt;br /&gt;
&lt;br /&gt;
=== Results ===&lt;br /&gt;
&lt;br /&gt;
The test suite write summary pass/fail information to the console as they run. On completion of the test run summary statistics are written to the console.&lt;br /&gt;
&lt;br /&gt;
Test log files are stored in /var/tmp/&amp;lt;testrun&amp;gt; (where test run is a unique looking number). In that directory there is a log file, and a directory per test. Within the test directory is detailed log information regarding the specific test.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T05:00:23Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have logged on the next allocated after the free and modify occurred.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Unit Test ==&lt;br /&gt;
&lt;br /&gt;
We have created an initial port of the standard ZFS test suite. It consists of a collection of scripts and miscellaneous utility programs and exercise the complete breadth and depth of the ZFS filesystem. &lt;br /&gt;
&lt;br /&gt;
The tests are best run in a virtual machine with a baseline configured setup that has been captured in a snapshot. The tests should be run on the VM, and then due to the destructive nature of the tests, the VM should be reverted to the snapshot in preparation for future test runs.  The tests take 2-4 hours to run depending on hardware setup.&lt;br /&gt;
&lt;br /&gt;
=== Setup ===&lt;br /&gt;
&lt;br /&gt;
The user zfs-test needs to be able to run sudo without issuing a password. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   zfs-tests ALL=(ALL) NOPASSWD: ALL&lt;br /&gt;
&lt;br /&gt;
The sudo root environment must be configured to pass certain enviroment variables from zfs-test through to the root environment. Add the following to sudoers:&lt;br /&gt;
&lt;br /&gt;
   Defaults env_keep += &amp;quot;__ZFS_MAIN_MOUNTPOINT_DIR&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Modify /etc/bashrc to contain&lt;br /&gt;
&lt;br /&gt;
   export __ZFS_MAIN_MOUNTPOINT_DIR=&amp;quot;/&amp;quot;&lt;br /&gt;
&lt;br /&gt;
If your development directory is ~you/Developer, clone zfs, spl and bfs-tests into that directory&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs-test.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/zfs.git&lt;br /&gt;
   # git clone git@github.com:openzfsonosx/spl.git&lt;br /&gt;
&lt;br /&gt;
Build the ZFS is built using the building from source instructions.&lt;br /&gt;
&lt;br /&gt;
Ensure that /var/tmp has approximately 100GB of free space.&lt;br /&gt;
&lt;br /&gt;
Create theee virtual hard drives of 10-20GB capacity each.&lt;br /&gt;
&lt;br /&gt;
=== Run Test Suite ===&lt;br /&gt;
&lt;br /&gt;
Setup the tests to run&lt;br /&gt;
&lt;br /&gt;
   # cd ~you/Developer/zfs-tests&lt;br /&gt;
   # ./autogen.sh&lt;br /&gt;
   # ./configure CC=clang CXX=clang++&lt;br /&gt;
&lt;br /&gt;
Edit the generated Makefile, change the recipe for the test_hw target such that your three virtual disks are listed in the DISKS environment variable.&lt;br /&gt;
&lt;br /&gt;
   test_hw: test_verify test/zfs-tests/cmd&lt;br /&gt;
           @KEEP=&amp;quot;`zpool list -H -oname`&amp;quot; \&lt;br /&gt;
            STF_TOOLS=$(abs_top_srcdir)/test/test-runner/stf \&lt;br /&gt;
            STF_SUITE=$(abs_top_srcdir)/test/zfs-tests \&lt;br /&gt;
            DISKS=&amp;quot;/dev/disk3 /dev/disk1 /dev/disk2&amp;quot; \&lt;br /&gt;
            su zfs-tests -c &amp;quot;ksh $(abs_top_srcdir)/test/zfs-tests/cmd/scripts/zfstest.ksh $$RUNFILE&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Run the test suite&lt;br /&gt;
&lt;br /&gt;
   sudo make test&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T01:00:34Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Detecting memory handling errors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have logged on the next allocated after the free and modify occurred.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T00:21:03Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Detecting memory handling errors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see the kind of memory corruption, the actual corrupted data, which kmem cache was involved, the relative time that the last action occurred, and the stack trace for the last action (which was a call to zfs_kmem_free()) - indicating that spl_start() was implicated in the fault. This event would have logged on the next allocated after the free and modify occurred.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T00:17:22Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Detecting memory handling errors */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
   { &lt;br /&gt;
       ...&lt;br /&gt;
       int *p;&lt;br /&gt;
       for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
          p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
          spl_kmem_free(p);&lt;br /&gt;
          *p = 0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
   14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see that spl_start() was present in the trace.&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T00:14:40Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
{ &lt;br /&gt;
    ...&lt;br /&gt;
    int *p;&lt;br /&gt;
    for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
       p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
       spl_kmem_free(p);&lt;br /&gt;
       *p = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: kernel memory allocator: buffer modified after being freed&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: modification occurred at offset 0x0 (0xdeadbeefdeadbeef replaced by 0xdeadbeef00000000)&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: buffer=0xffffff887a87d980  bufctl=0xffffff887a7ad840  cache: kmem_alloc_1152&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: previous transaction on buffer 0xffffff887a87d980:&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: thread=0  time=T-0.000001383  slab=0xffffff887a5ffe68  cache: kmem_alloc_1152&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free_debug + 0x227&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _kmem_cache_free + 0x173&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _zfs_kmem_free + 0x2c4&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: net.lundman.spl : _spl_start + 0x2bb&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext5startEb + 0x40b&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0xdd&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext4loadEhhP7OSArray + 0x3e1&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN6OSKext22loadKextWithIdentifierEP8OSStringbbhhP7OSArray + 0xf2&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZNK11IOCatalogue14isModuleLoadedEP12OSDictionary + 0xe0&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService15probeCandidatesEP12OSOrderedSet + 0x2c4&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN9IOService14doServiceMatchEj + 0x22a&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : __ZN15_IOConfigThread4mainEPvi + 0x13c&lt;br /&gt;
14/08/2015 5:09:47.000 PM kernel[0]: SPL: mach_kernel : _call_continuation + 0x17&lt;br /&gt;
&lt;br /&gt;
You can clearly see that spl_start() was present in the trace&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-15T00:13:18Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Kernel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Detecting memory handling errors ===&lt;br /&gt;
&lt;br /&gt;
The kmem allocator has an internal diagnostic mode. In diagnostic mode the allocator instruments heap memory with various features and markers as it is allocated and released by application code. These markers are checked as the program runs, and can determine when an application has exhibited one or more of a set of common memory handling errors. The debugging mode is disabled by default as it carries a significant performance penalty.&lt;br /&gt;
&lt;br /&gt;
The memory handling errors that can be detected include:&lt;br /&gt;
* Modify after free&lt;br /&gt;
* Write past end of buffer&lt;br /&gt;
* Free of memory not managed by kmem&lt;br /&gt;
* Double free of memory&lt;br /&gt;
* Various other corruptions&lt;br /&gt;
* Freed size != allocated size&lt;br /&gt;
* Freed address != allocated address&lt;br /&gt;
&lt;br /&gt;
Debug mode is enabled by compiling with the preprocessor symbol DEBUG defined. At a minimum spl-kmem.c and spl-osx.c need to see this define for the debugging features to be completely enabled.&lt;br /&gt;
&lt;br /&gt;
In debugging mode you must choose whether kmem will log the fault and then panic, or just log. If you elect to panic, there is a very high chance that the full log message will not be stored in system.log before the OS halts, and you will have to connect to the machine with lldb and use the &amp;quot;systemlog&amp;quot; command to view the diagnostic message. If you elect to not panic, the program will continue to run despite the memory corruption, with undefined consequences. In spl-kmem.c set kmem_panic=0 to log, kmem_panic=1 to log+panic.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
I modified spl_start() to include the following:&lt;br /&gt;
&lt;br /&gt;
{ &lt;br /&gt;
    ...&lt;br /&gt;
    int *p;&lt;br /&gt;
    for(int i=0; i&amp;lt;20;i++) {&lt;br /&gt;
       p = (int*)spl_kmem_alloc(1024);&lt;br /&gt;
       spl_kmem_free(p);&lt;br /&gt;
       *p = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
With the debug mode enabled the following was logged:&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can clearly see that spl_start() was present in the trace&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	<entry>
		<id>https://openzfsonosx.org/wiki/Development</id>
		<title>Development</title>
		<link rel="alternate" type="text/html" href="https://openzfsonosx.org/wiki/Development"/>
				<updated>2015-08-14T23:49:26Z</updated>
		
		<summary type="html">&lt;p&gt;101.175.67.14: /* Memory leaks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:O3X development]]&lt;br /&gt;
You should also familiarize yourself with the [[Project_roadmap|project roadmap]] so that you can put the technical details here in context.&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
&lt;br /&gt;
=== Debugging with GDB ===&lt;br /&gt;
&lt;br /&gt;
Dealing with [[Panic|panics]].&lt;br /&gt;
&lt;br /&gt;
Apple's documentation: https://developer.apple.com/library/mac/documentation/Darwin/Conceptual/KEXTConcept/KEXTConceptDebugger/debug_tutorial.html&lt;br /&gt;
&lt;br /&gt;
Boot target VM with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo nvram boot-args=&amp;quot;-v keepsyms=y debug=0x144&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make it panic.&lt;br /&gt;
&lt;br /&gt;
On your development machine, you will need the Kernel Debug Kit. Download it from Apple [https://developer.apple.com/downloads/index.action?q=Kernel%20Debug%20Kit here].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ gdb /Volumes/Kernelit/mach_kernel&lt;br /&gt;
(gdb) source /Volumes/KernelDebugKit/kgmacros&lt;br /&gt;
(gdb) target remote-kdp&lt;br /&gt;
(gdb) kdp-reattach  192.168.30.133   # obviously use the IP of your target / crashed VM&lt;br /&gt;
(gdb) showallkmods&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Find the addresses for ZFS and SPL modules.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;^Z&amp;lt;/code&amp;gt; to suspend gdb, or, use another terminal&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
^Z&lt;br /&gt;
$ sudo kextutil -s /tmp -n \&lt;br /&gt;
-k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ \&lt;br /&gt;
../spl/module/spl/spl.kext/&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then resume gdb, or go back to gdb terminal.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
$ fg&lt;br /&gt;
(gdb) set kext-symbol-file-path /tmp&lt;br /&gt;
(gdb) add-kext /tmp/spl.kext &lt;br /&gt;
(gdb) add-kext /tmp/zfs.kext&lt;br /&gt;
(gdb) bt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Debugging with LLDB ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ echo &amp;quot;settings set target.load-script-from-symbol-file true&amp;quot; &amp;gt;&amp;gt; ~/.lldbinit&lt;br /&gt;
$ lldb /Volumes/KernelDebugKit/mach_kernel     # From Yosemite, &amp;quot;/Library/Developer/KDKs/KDK_10.10_14B25.kdk/System/Library/Kernels/kernel&amp;quot; &lt;br /&gt;
(lldb) kdp-remote  192.168.30.146&lt;br /&gt;
(lldb) showallkmods&lt;br /&gt;
(lldb) addkext -F /tmp/spl.kext/Contents/MacOS/spl 0xffffff7f8ebb0000   (Address from showallkmods)&lt;br /&gt;
(lldb) addkext -F /tmp/zfs.kext/Contents/MacOS/zfs 0xffffff7f8ebbf000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then follow the guide for GDB above.&lt;br /&gt;
&lt;br /&gt;
=== Non-panic ===&lt;br /&gt;
&lt;br /&gt;
If you prefer to work in GDB, you can always panic a kernel with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -w -n &amp;quot;BEGIN{ panic();}&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But this was revealing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo /usr/libexec/stackshot -i -f /tmp/stackshot.log &lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -w /tmp/trace.txt&lt;br /&gt;
$ less /tmp/trace.txt&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that my hang is here:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
PID: 156&lt;br /&gt;
    Process: zpool&lt;br /&gt;
    Thread ID: 0x4e2&lt;br /&gt;
    Thread state: 0x9 == TH_WAIT |TH_UNINT &lt;br /&gt;
    Thread wait_event: 0xffffff8006608a6c&lt;br /&gt;
    Kernel stack: &lt;br /&gt;
    machine_switch_context (in mach_kernel) + 366 (0xffffff80002b3d3e)&lt;br /&gt;
      0xffffff800022e711 (in mach_kernel) + 1281 (0xffffff800022e711)&lt;br /&gt;
        thread_block_reason (in mach_kernel) + 300 (0xffffff800022d9dc)&lt;br /&gt;
          lck_mtx_sleep (in mach_kernel) + 78 (0xffffff80002265ce)&lt;br /&gt;
            0xffffff8000569ef6 (in mach_kernel) + 246 (0xffffff8000569ef6)&lt;br /&gt;
              msleep (in mach_kernel) + 116 (0xffffff800056a2e4)&lt;br /&gt;
                0xffffff7f80e52a76 (0xffffff7f80e52a76)&lt;br /&gt;
                  0xffffff7f80e53fae (0xffffff7f80e53fae)&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
                        0xffffff7f80f2bb4e (0xffffff7f80f2bb4e)&lt;br /&gt;
                          0xffffff7f80f1a9b7 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            0xffffff7f80f1b65f (0xffffff7f80f1b65f)&lt;br /&gt;
                              0xffffff7f80f042ee (0xffffff7f80f042ee)&lt;br /&gt;
                                0xffffff7f80f45c5b (0xffffff7f80f45c5b)&lt;br /&gt;
                                  0xffffff7f80f4ce92 (0xffffff7f80f4ce92)&lt;br /&gt;
                                    spec_ioctl (in mach_kernel) + 157 (0xffffff8000320bfd)&lt;br /&gt;
                                      VNOP_IOCTL (in mach_kernel) + 244 (0xffffff8000311e84)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is a shame that it only shows the kernel symbols, and not inside SPL and ZFS, but we can ask it to load another sym file. (Alas, it cannot handle multiple symbols files. Fix this Apple.)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo kextstat #grab the addresses of SPL and ZFS again&lt;br /&gt;
$ sudo kextutil -s /tmp -n -k /Volumes/KernelDebugKit/mach_kernel \&lt;br /&gt;
-e -r /Volumes/KernelDebugKit module/zfs/zfs.kext/ ../spl/module/spl/spl.kext/ &lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.spl.sym&lt;br /&gt;
              0xffffff800056a2e4 (0xffffff800056a2e4)&lt;br /&gt;
                spl_cv_wait (in net.lundman.spl.sym) + 54 (0xffffff7f80e52a76)&lt;br /&gt;
                  taskq_wait (in net.lundman.spl.sym) + 78 (0xffffff7f80e53fae)&lt;br /&gt;
                    taskq_destroy (in net.lundman.spl.sym) + 35 (0xffffff7f80e54173)&lt;br /&gt;
                      0xffffff7f80f1a870 (0xffffff7f80f1a870)&lt;br /&gt;
&lt;br /&gt;
$ sudo symstacks.rb -f /tmp/stackshot.log -s -k /tmp/net.lundman.zfs.sym&lt;br /&gt;
                    0xffffff7f80e54173 (0xffffff7f80e54173)&lt;br /&gt;
                      vdev_open_children (in net.lundman.zfs.sym) + 336 (0xffffff7f80f1a870)&lt;br /&gt;
                        vdev_root_open (in net.lundman.zfs.sym) + 94 (0xffffff7f80f2bb4e)&lt;br /&gt;
                          vdev_open (in net.lundman.zfs.sym) + 311 (0xffffff7f80f1a9b7)&lt;br /&gt;
                            vdev_create (in net.lundman.zfs.sym) + 31 (0xffffff7f80f1b65f)&lt;br /&gt;
                              spa_create (in net.lundman.zfs.sym) + 878 (0xffffff7f80f042ee)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Voilà!&lt;br /&gt;
&lt;br /&gt;
=== Memory leaks ===&lt;br /&gt;
&lt;br /&gt;
(Note that this section is only relevant to old O3X implementation that used the zones allocator - we now use our own kmem allocator).&lt;br /&gt;
&lt;br /&gt;
In some cases, you may suspect memory issues, for instance if you saw the following panic:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
panic(cpu 1 caller 0xffffff80002438d8): &amp;quot;zalloc: \&amp;quot;kalloc.1024\&amp;quot; (100535 elements) retry fail 3, kfree_nop_count: 0&amp;quot;@/SourceCache/xnu/xnu-2050.7.9/osfmk/kern/zalloc.c:1826&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To debug this, you can attach GDB and use the zprint command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) zprint&lt;br /&gt;
ZONE                   COUNT   TOT_SZ   MAX_SZ   ELT_SZ ALLOC_SZ         TOT_ALLOC         TOT_FREE NAME&lt;br /&gt;
0xffffff8002a89250   1620133  18c1000  22a3599       16     1000         125203838        123583705 kalloc.16 CX&lt;br /&gt;
0xffffff8006306c50    110335   35f000   4ce300       32     1000          13634985         13524650 kalloc.32 CX&lt;br /&gt;
0xffffff8006306a00    133584   82a000   e6a900       64     1000          26510120         26376536 kalloc.64 CX&lt;br /&gt;
0xffffff80063067b0    610090  4a84000  614f4c0      128     1000          50524515         49914425 kalloc.128 CX&lt;br /&gt;
0xffffff8006306560   1070398 121a2000 1b5e4d60      256     1000          72534632         71464234 kalloc.256 CX&lt;br /&gt;
0xffffff8006306310    399302  d423000  daf26b0      512     1000          39231204         38831902 kalloc.512 CX&lt;br /&gt;
0xffffff80063060c0    100404  6231000  c29e980     1024     1000          22949693         22849289 kalloc.1024 CX&lt;br /&gt;
0xffffff8006305e70       292    9a000   200000     2048     1000          77633725         77633433 kalloc.2048 CX&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case, kalloc.256 is suspect.&lt;br /&gt;
&lt;br /&gt;
Reboot kernel with zlog=kalloc.256 on the command line, then we can use&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) findoldest                                                                &lt;br /&gt;
oldest record is at log index 393:&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff803276ec00 : index 393  :  ztime 21643824 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
and indeed, list any index&lt;br /&gt;
&lt;br /&gt;
(gdb) zstack 394&lt;br /&gt;
&lt;br /&gt;
--------------- ALLOC  0xffffff8032d60700 : index 394  :  ztime 21648810 -------------&lt;br /&gt;
0xffffff800024352e &amp;lt;zalloc_canblock+78&amp;gt;:        mov    %eax,-0xcc(%rbp)&lt;br /&gt;
0xffffff80002245bd &amp;lt;get_zone_search+23&amp;gt;:        jmpq   0xffffff80002246d8 &amp;lt;KALLOC_ZINFO_SALLOC+35&amp;gt;&lt;br /&gt;
0xffffff8000224c39 &amp;lt;OSMalloc+89&amp;gt;:       mov    %rax,-0x18(%rbp)&lt;br /&gt;
0xffffff7f80e847df &amp;lt;zfs_kmem_alloc+15&amp;gt;: mov    %rax,%r15&lt;br /&gt;
0xffffff7f80e90649 &amp;lt;arc_buf_alloc+41&amp;gt;:  mov    %rax,-0x28(%rbp)&lt;br /&gt;
How many times was zfs_kmem_alloc involved in the leaked allocs?&lt;br /&gt;
&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e847df&lt;br /&gt;
occurred 3999 times in log (100% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At least we know it is our fault.&lt;br /&gt;
&lt;br /&gt;
How many times is it arc_buf_alloc?&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
(gdb) countpcs 0xffffff7f80e90649&lt;br /&gt;
occurred 2390 times in log (59% of records)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Memory Architecture ===&lt;br /&gt;
&lt;br /&gt;
ZFS is designed to aggressively cache filesystem data in main memory. The result of this caching can be significant filesystem performance improvement.&lt;br /&gt;
&lt;br /&gt;
Selection of an allocator has been very challenging on OS X. In the last year we have evolved from:&lt;br /&gt;
* Direct call to OSMalloc - a very low level allocator in the kernel - rejected because of slow performance and because the minimum allocation size is one page (4k)&lt;br /&gt;
* Direct call to zalloc - the OS X zones allocator - rejected because only 25% of the machines memory can be accessed (50% under some circumstances), and because the result of exceeding this limit is a kernel panic with no other feedback mechanisms available.&lt;br /&gt;
* Direct call to bmalloc - bmalloc was a home grown slice allocator that allocated slices of memory from the kernel page allocator, and subdivided into smaller units of allocation to use by ZFS. This was quite successful but very space inefficient. Was used in O3X 1.2.7 and 1.3.0. At this stage we had no real response to memory pressure in the machine, so the total memory allocation to O3X was kept to 50% of the machine.&lt;br /&gt;
* Implementation of kmem and vmem allocators using code from Illumos. Provision of a memory pressure monitor mechanism - we are now able to allocate most of the machines memory to ZFS, and scale that back when the machine experiences memory pressure.&lt;br /&gt;
&lt;br /&gt;
O3X has the Solaris Porting Layer (SPL). The SPL has long since provided the Illumos kmem.h API for use by ZFS. In O3X releases up to 1.3.0 the kmem implementation has been a stub that passes allocation requests to an underlying allocator. In O3X 1.3.0 we were still missing some key behaviours in the allocator - efficient lifecycle control of objects, and an effective response to memory pressure in the machine, and the allocator was not very space efficient because of metadata overheads in bmalloc. We were also not convinced that bmalloc represented the state of the art.&lt;br /&gt;
&lt;br /&gt;
Our strategy was to determine how much of the Illumos allocator could be implemented on OS X. After a series of experiments where we implemented significant portions of the kmem code from illumos on top of bmalloc, we had learned enough to take the final step of essentially copying the entire kmem/vmem allocator stack from Illumos. Some portions of the kmem code have been disabled in kmem such as logging, and hot swap CPU support have been disabled due to architectural differences between OS X and Illumos.&lt;br /&gt;
&lt;br /&gt;
By default kmem/vmem require a certain level of performance from the OS page allocator. It is easy to overwhelm the OS X page allocator. We tuned vmem to use 512Kb chunks of memory from the page allocator rather than the smaller allocations that vmem prefers. This is less than ideal as it reduces the ability for vmem to smoothly release memory to the page allocator when the machine is under pressure. While we have an adequately performing solution now, there will always be a tension between our allocator and OS X itself. OS X only provides minimal mechanisms to observe and respond to memory pressure in the machine, so we are somewhat limited in what can be achieved in this regard.  &lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;br /&gt;
Jeff Bonwicks paper - kmem and vmem implement this design. https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick_html/&lt;br /&gt;
&lt;br /&gt;
=== Compiling to lower OSX versions ===&lt;br /&gt;
&lt;br /&gt;
If you wish to compile O3X to a specific OSX version, in this case, compiling for 10.9 on a 10.10&lt;br /&gt;
&lt;br /&gt;
SPL:&lt;br /&gt;
 ./configure --with-kernel-headers=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
ZFS:&lt;br /&gt;
 ./configure --with-kernelsrc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Kernel.framework/ CFLAGS=-mmacosx-version-min=10.9&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Flamegraphs ==&lt;br /&gt;
&lt;br /&gt;
Huge thanks to [http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html BrendanGregg] for so much of the dtrace magic.&lt;br /&gt;
&lt;br /&gt;
dtrace the kernel while running a command:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo dtrace -x stackframes=100 -n 'profile-997 /arg0/ {&lt;br /&gt;
    @[stack()] = count(); } tick-60s { exit(0); }' -o out.stacks&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will run for 60 seconds.&lt;br /&gt;
&lt;br /&gt;
Convert it to a flamegraph:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./stackcollapse.pl out.stacks &amp;gt; out.folded&lt;br /&gt;
$ ./flamegraph.pl out.folded &amp;gt; out.svg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is &amp;lt;code&amp;gt;rsync -a /usr/ /BOOM/deletea/&amp;lt;/code&amp;gt; running:&lt;br /&gt;
&lt;br /&gt;
[[File:rsyncflamegraph.svg|thumb|rsync flamegraph]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Or running '''Bonnie++''' in various stages:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed-hover&amp;quot;&amp;gt;&lt;br /&gt;
File:create.svg|Create files in sequential order|alt=[[File:create.svg]]&lt;br /&gt;
File:stat.svg|Stat files in sequential order|alt=Stat files in sequential order&lt;br /&gt;
File:delete.svg|Delete files in sequential order|alt=Delete files in sequential order&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:VX_create.svg|thumb|Create files in sequential order]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:iozone.svg|thumb|IOzone flamegraph]]&lt;br /&gt;
&lt;br /&gt;
[[File:iozoneX.svg|thumb|IOzone flamegraph (untrimmed)]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
------&lt;br /&gt;
&lt;br /&gt;
== Iozone ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Quick peek at how they compare, just to see how much we should improve it by.&lt;br /&gt;
&lt;br /&gt;
HFS+ and ZFS were created on the same virtual disk in VMware. Of course, this is not ideal testing specs, but should serve as an indicator. &lt;br /&gt;
&lt;br /&gt;
The pool was created with&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 \&lt;br /&gt;
-O atime=off \&lt;br /&gt;
-O casesensitivity=insensitive \&lt;br /&gt;
-O normalization=formD \&lt;br /&gt;
BOOM /dev/disk1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and the HFS+ file system was created with the standard OS X Disk Utility.app, with everything default (journaled, case-insensitive).&lt;br /&gt;
&lt;br /&gt;
'''Iozone''' was run with standard automode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo iozone -a -b outfile.xls&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:hfs2_read.png|thumb|HFS+ read]]&lt;br /&gt;
[[File:hfs2_write.png|thumb|HFS+ write]]&lt;br /&gt;
[[File:zfs2_read.png|thumb|ZFS read]]&lt;br /&gt;
[[File:zfs2_write.png|thumb|ZFS write]]&lt;br /&gt;
&lt;br /&gt;
As a guess, writes need to double, and reads need to triple.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== VFS ===&lt;br /&gt;
&lt;br /&gt;
[[VFS]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== File-based zpools for testing==&lt;br /&gt;
&lt;br /&gt;
* create 2 files (each 100 MB) to be used as block devices:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk1&lt;br /&gt;
$ dd if=/dev/zero bs=1m count=100 of=vdisk2&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* attach files as raw disk images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk1&lt;br /&gt;
/dev/disk2&lt;br /&gt;
$ hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount vdisk2&lt;br /&gt;
/dev/disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* create mirrored zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool create -f -o ashift=12 -O casesensitivity=insensitive -O normalization=formD tank mirror disk2 disk3&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* show zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool status&lt;br /&gt;
  pool: tank&lt;br /&gt;
 state: ONLINE&lt;br /&gt;
  scan: none requested&lt;br /&gt;
config:&lt;br /&gt;
&lt;br /&gt;
	NAME        STATE     READ WRITE CKSUM&lt;br /&gt;
	tank        ONLINE       0     0     0&lt;br /&gt;
	  mirror-0  ONLINE       0     0     0&lt;br /&gt;
	    disk2   ONLINE       0     0     0&lt;br /&gt;
	    disk3   ONLINE       0     0     0&lt;br /&gt;
&lt;br /&gt;
errors: No known data errors&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* test ZFS features, find bugs, ...&lt;br /&gt;
&lt;br /&gt;
* export zpool:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ sudo zpool export tank&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* detach raw images:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
$ hdiutil detach disk2&lt;br /&gt;
&amp;quot;disk2&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk2&amp;quot; ejected.&lt;br /&gt;
$ hdiutil detach disk3&lt;br /&gt;
&amp;quot;disk3&amp;quot; unmounted.&lt;br /&gt;
&amp;quot;disk3&amp;quot; ejected.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Platform differences ==&lt;br /&gt;
&lt;br /&gt;
This section is an attempt to outline the differences from ZFS versions of other platforms, as compared to OS X. To assist developers new to the Apple platform, who wishes to assist, or understand, development of the O3X version.&lt;br /&gt;
&lt;br /&gt;
=== Reclaim ===&lt;br /&gt;
&lt;br /&gt;
One of the biggest hassles with OS X is the VFS layer's handling of reclaim. First it is worth noting that &amp;quot;struct vnode&amp;quot; is an opaque type, so we are not allowed to see, nor modify, the contents of a vnode.&lt;br /&gt;
(Of course, we could craft a mirror struct of vnode and tailor it to each OS X version where vnode changes. But that is rather hacky.)&lt;br /&gt;
&lt;br /&gt;
Following that, the '''only''' place where you can set the '''vtype''' (VREG, VDIR), '''vdata''' (user pointer to hold the ZFS znode), '''vfsops''' (list of filesystem calls &amp;quot;vnops&amp;quot;) etc, is '''only in calling vnode_create()'''. &lt;br /&gt;
So there is no way to &amp;quot;allocate an empty vnode, and set its values later&amp;quot;. The FreeBSD method of pre-allocating vnodes, to avoid reclaim, can not be done. &lt;br /&gt;
ZFS will start a new dmu_tx, then call zfs_mknode which will eventually call vnode_create, so we can not do anything with dmu_tx in those vnops.&lt;br /&gt;
&lt;br /&gt;
The problem is, if vnode_create decides to reclaim, it will do so directly, as the same thread. It will end up in vclean() which can call vnop_fsync, vnop_pageout, vnop_inactive and vnop_reclaim. The first three of these calls, we can &lt;br /&gt;
use the API call vnode_isrecycled() to detect if these vnops are called &amp;quot;the normal way&amp;quot;, or from vclean. If we come from vclean, and the vnode is doomed, we will do as little as possible. We can not open a new TX, and&lt;br /&gt;
we can not use mutex locks (panic: locking against ourselves).&lt;br /&gt;
&lt;br /&gt;
Nor is there any way to defer, or delay, a doomed vnode. If vnop_reclaim returns anything but 0, you find the lovely XNU code of &lt;br /&gt;
 2205         if (VNOP_RECLAIM(vp, ctx))&lt;br /&gt;
 2206                 panic(&amp;quot;vclean: cannot reclaim&amp;quot;);&lt;br /&gt;
in vfs_subr.c&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, at the moment there is some extra logic in '''zfs_vnop_reclaim''' to handle that we might be re-entrant as the '''vnode_create''' thread.&lt;br /&gt;
&lt;br /&gt;
    exception = ((zp-&amp;gt;z_sa_hdl != NULL) &amp;amp;&amp;amp;&lt;br /&gt;
        zp-&amp;gt;z_unlinked) ? B_TRUE : B_FALSE;&lt;br /&gt;
    fastpath = zp-&amp;gt;z_fastpath;&lt;br /&gt;
&lt;br /&gt;
if both exception and fastpath are FALSE, we can call direct reclaim right there. As in those cases, no final dmu_tx is caused. Following&lt;br /&gt;
the zfs_rmnode-&amp;gt;zfs_purgedir-&amp;gt;zget and similar paths, exception is set to TRUE.&lt;br /&gt;
&lt;br /&gt;
If exception is TRUE, we add the zp to the reclaim_list, and the separate reclaim_thread will call zfs_rmnode(zp). As a separate thread it can handle calling&lt;br /&gt;
dmu_tx. &lt;br /&gt;
&lt;br /&gt;
If fastpath is TRUE, we do no more/nothing in zfs_vnop_reclaim. See below.&lt;br /&gt;
&lt;br /&gt;
=== Fastpath vs Recycle ===&lt;br /&gt;
&lt;br /&gt;
Another interesting aspect is that IllumOS has a delete fastpath. In zfs_remove, if it is detected that the znode can be &amp;quot;deleted_now&amp;quot;, it marks the vnode as free and directly calls zfs_znode_delete(), if it can not, adds it to zfs_unlinked_add().&lt;br /&gt;
&lt;br /&gt;
In OS X, there is no way to directly release a vnode. Ie, XNU always has full control of the vnodes. Even if you call vnode_recycle(), the vnode is not released '''until''' vnop_reclaim is called. The vnode can just be marked for later reclaim, but remain active (especially if you are racing against other threads using the same vnode). So in zfs_remove, we attempt to call vnode_recycle(), and only if this returns &amp;quot;1&amp;quot; do we know that vnop_reclaim was called, and we can directly call zfs_znode_delete(). Note that the O3X vnop_reclaim handler then has special code to not do anything with the vnode (zp-&amp;gt;z_fastpath) but to only clear out the z_vnode and return.&lt;br /&gt;
&lt;br /&gt;
    zp-&amp;gt;z_fastpath = B_TRUE;&lt;br /&gt;
    if (vnode_recycle(vp) == 1) {&lt;br /&gt;
        /* recycle/reclaim is done, so we can just release now */&lt;br /&gt;
        zfs_znode_delete(zp, tx);&lt;br /&gt;
    } else {&lt;br /&gt;
        /* failed to recycle, so just place it on the unlinked list */&lt;br /&gt;
        zp-&amp;gt;z_fastpath = B_FALSE;&lt;br /&gt;
        zfs_unlinked_add(zp, tx);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There is also a little special lock-handling in zfs_zinactive, since we can call it from inside of a vnode_create() which is called by ZFS with locks held. If this is the case, we do not attempt to acquire locks in zfs_zinactive.&lt;br /&gt;
&lt;br /&gt;
=== snapshot mounts ===&lt;br /&gt;
&lt;br /&gt;
There is no way to cause a mount in XNU kernel. None. At. All. Apple themselves cheated and added a static nfsmount() that we can not call. So instead, we have to jump through a whole bunch of&lt;br /&gt;
hoops to get there. We create a fake/virtual /dev/diskX entry for the snapshot.  '''diskarbitrationd''' will wake up due to new disk, it will enter the probe phase, which includes calling&lt;br /&gt;
all the /System/Library/Filesystems/ bundles. Eventually, zfs.util is called and we reply affirmative. However, automount is disable here, as there is no way to specify a mountpoint with auto.&lt;br /&gt;
zfs.util will call DADiskMount to mount it to the correct directory.&lt;br /&gt;
&lt;br /&gt;
This means we have a few more VNOPs in zfs_ctldir.c, as we have to reply with correct information to make mount successful. The first getattr will cause the mount attempt, the DADiskMount call will cause getattr to be called&lt;br /&gt;
and we have to pretend to have said entry.&lt;br /&gt;
&lt;br /&gt;
=== spl_vn_rdwr vs vn_rdwr ===&lt;br /&gt;
&lt;br /&gt;
There are two calls to vn_rdwr() in OSX's SPL. The '''spl_vn_rdwr()''' call needs to be used when zfs_onexit is in use. For example, dmu_send.c (zfs recv/send) and zfs_ioc_diff (zfs diff). The XNU implementation of&lt;br /&gt;
zfs_onexit (as in calls to '''getf''' and '''releasef''') need to place the internal XNU ''struct fileproc''' in the wrapper ''struct spl_fileproc'', so that '''spl_vn_rdwr()''' can use it to do IO.&lt;br /&gt;
This is the only way to do IO on a non-file based vnode (ie, pipe or socket). Other places that call vn_rdwr(), for example vdev_file.c, needs to call the regular vn_rdwr.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== getattr ===&lt;br /&gt;
&lt;br /&gt;
XNU has a whole bunch of items that it can ask for in vnop_getattr, including VA_NAME, which is used heavily by Finder (especially in the vfs_vget path). Care is needed here to return the correct name, &lt;br /&gt;
including for link (hard links) targets. VNOP_LOOKUP records the name that was used in the lookup, so that a following stat call (vnop_getattr) on the vnode will return the correct name if VA_NAME is requested.&lt;/div&gt;</summary>
		<author><name>101.175.67.14</name></author>	</entry>

	</feed>