SPL(4)

🌸 SPL(4) 🌸

okay bestie, sit down, hydrate, and let me tell you about spl, the kernel module for the Solaris Porting Layer πŸ’– this is the page for all its tunable parameters, and there are a lot of them, but don't panic β€” we're getting through every single one together 🫢


✨ NAME ✨

spl β€” parameters of the SPL kernel module πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

these are the knobs and dials of the spl kernel module, and honestly? they're giving "control freak but make it useful" πŸ‘‘


  • spl_kmem_cache_kmem_threads=4 (uint) β€” the number of threads that get created for the spl_kmem_cache task queue. this queue is the one doing the actual work of allocating new slabs for the kmem caches to use. and ngl, for most systems and most workloads you genuinely only need a few threads. it's not that deep 🌸
  • spl_kmem_cache_obj_per_slab=8 (uint) β€” the preferred number of objects per slab in the cache. the tradeoff is real here: bigger value = the cache eats more memory but allocations get faster. smaller value = footprint stays smol and cache reclaim is quicker, but individual allocations may take longer. pick your fighter πŸ’…
  • spl_kmem_cache_max_size=32 (64-bit) or 4 (32-bit) (uint) β€” the max size of a kmem cache slab, measured in MiB. this basically caps your maximum cache object size at spl_kmem_cache_max_size/spl_kmem_cache_obj_per_slab.
heads up: caches cannot be created with an object size larger than this limit. not a vibe, just a fact 🚫
  • spl_kmem_cache_slab_limit=16384 (uint) β€” so for small objects, the Linux slab allocator should be the one doing the work, because it makes the most efficient use of memory. but large objects? Linux slab does not support those, so the SPL implementation gets the job instead. this value is literally the cutoff line between "small object" and "large object" 🌈

objects sized spl_kmem_cache_slab_limit or smaller get allocated by the Linux slab allocator; larger objects go to the SPL allocator. a cutoff of 16K was determined to be optimal for architectures using 4K pages. iconic πŸŽ€

  • spl_kmem_alloc_warn=32768 (uint) β€” general rule, bestie: kmem_alloc() allocations should be small, preferably just a few pages, because they must be physically contiguous. so if a kmem_alloc() blows past a reasonable threshold, a rate limited warning gets printed to the console. that's the vibe check 🚨

the default warning threshold is set to eight pages but capped at 32K to accommodate systems using large pages. this value was picked to be small enough that the largest allocations get noticed and fixed fast, but large enough that nothing gets logged when an allocation size is bigger than optimal but not actually a serious concern. and since this value is tunable, developers are encouraged to set it lower when testing so any new largish allocations get caught quickly. you can also disable these warnings entirely by setting the threshold to zero 🫢

  • spl_kmem_alloc_max=KMALLOC_MAX_SIZE/4 (uint) β€” big kmem_alloc() allocations will straight up fail if they exceed KMALLOC_MAX_SIZE. and even the ones that are marginally smaller than that limit may succeed but should still be avoided, because finding a contiguous range of free pages is expensive 😭 so a maximum kmem size with a reasonable safely margin of 4x is set. kmem_alloc() allocations larger than this max will quickly fail. meanwhile vmem_alloc() allocations less than or equal to this value will use kmalloc(), but shift to vmalloc() once they exceed it ✨
  • spl_kmem_cache_magazine_size=0 (uint) β€” cache magazines are an optimization built to minimize the cost of allocating memory. how? by keeping a per-CPU cache of recently freed objects, which can then be reallocated without taking a lock. that can improve performance on highly contended caches πŸ”₯ but because objects sitting in magazines prevent otherwise empty slabs from being immediately released, it may not be ideal for low memory machines πŸ’€

so that's what spl_kmem_cache_magazine_size is for: setting a maximum magazine size. when it's set to 0, the magazine size gets automatically determined based on the object size. otherwise magazines are limited to 2-256 objects per magazine (i.e. per CPU). and no, magazines may never be entirely disabled in this implementation. periodt πŸ’…

  • spl_hostid=0 (ulong) β€” the system hostid. when it's set, it can be used to uniquely identify a system. by default this value is zero, which means the hostid is disabled. you can explicitly enable it by placing a unique non-zero value in /etc/hostid πŸ¦‹
  • spl_hostid_path=/etc/hostid (charp) β€” the expected path to locate the system hostid when one is specified. this value may be overridden for non-standard configurations 🌸
  • spl_panic_halt=0 (uint) β€” cause a kernel panic on assertion failures. when it's not enabled, the thread is halted instead so you can do further debugging. set it to a non-zero value to enable πŸ’€
  • spl_taskq_kick=0 (uint) β€” kick stuck taskq to spawn threads. when you write a non-zero value to it, it scans all the taskqs, and if any of them have a pending task more than 5 seconds old, it kicks that one to spawn more threads. this is your move if you find a rare deadlock happening because one or more taskqs didn't spawn a thread when it should have πŸ™ˆ
  • spl_taskq_thread_bind=0 (int) β€” bind taskq threads to specific CPUs. when enabled, all taskq threads get distributed evenly across the available CPUs. by default this behavior is disabled, to let the Linux scheduler have maximum flexibility in deciding where a thread should run ✨
  • spl_taskq_thread_dynamic=1 (int) β€” allow dynamic taskqs. when enabled, taskqs which set the TASKQ_DYNAMIC flag will by default create only a single thread, then new threads get created on demand up to a maximum allowed number so outstanding tasks can actually get completed. threads that are no longer needed get promptly destroyed πŸ”₯ by default this behavior is enabled, but you can disable it to aid performance analysis or troubleshooting πŸ‘‘
  • spl_taskq_thread_priority=1 (int) β€” allow newly created taskq threads to set a non-default scheduler priority. when enabled, the priority specified when a taskq is created gets applied to all threads created by that taskq. when disabled, all threads use the default Linux kernel thread priority. by default, this behavior is enabled πŸ’œ
  • spl_taskq_thread_sequential=4 (int) β€” the number of items a taskq worker thread must handle without interruption before it requests a new worker thread be spawned. this controls how quickly taskqs ramp up the number of threads processing the queue. and because Linux thread creation and destruction are relatively inexpensive, a small default value was chosen πŸŽ€ which means threads normally get created aggressively β€” and that's the desirable outcome. increasing this value slows the thread creation rate down, which may be preferable for some configurations 🌈
  • spl_taskq_thread_timeout_ms=5000 (uint) β€” minimum idle threads exit interval for dynamic taskqs. smaller values let idle threads exit more often and potentially get respawned again on demand, which causes more churn πŸŒ€

and that's the whole page, bestie, you made it to the end πŸ₯³ this one came from OpenZFS, dated May 7, 2025, and it's living in section SPL(4) of the kernel interfaces manual πŸ’– go forth and tune responsibly πŸ’…βœ¨

πŸ“¦ where this page comes from