Linux kernel 7.0 Slub allocator analyze
Overview
note for linux kernel 7.0 slub allocator analyze
in linux kernel to manage small kernel object uses slab allocator , a bit similar to userspace glibc ptmalloc same size object goes to same size slab cache.
as for slab allocator implementation theres 3 ways:
- slab , removed in linux kernel 6.8
- slub , the only slab allocator in mainline currently
- slob , removed in linux kernel 6.4
this note only talked about slub allocator , the slab mentioned below all refers to slub
Object
if CONFIG_SLUB_DEBUG_ON and CONFIG_KASAN is on , its layout would look like this
but in default setting these won’t be on , default layout would be look like this instead
the FP is free pointer , which used when the object get freed and append to the freelist , pointing the next freed object
kmem_cache
different kernel object(size) got it’s own slab cache , struct kmem_cache including all the slab cache info.
https://elixir.bootlin.com/linux/v7.0/source/mm/slab.h#L197
1 | struct kmem_cache { |
__kmem_cache_create_args()
__kmem_cache_create_args() is used to create kmem_cache for specific kernel object
example: cred object
macro KMEM_CACHE()
https://elixir.bootlin.com/linux/v7.0/source/kernel/cred.c#L535
1 | /* |
expand the macro KMEM_CACHE()
https://elixir.bootlin.com/linux/v7.0/source/include/linux/slab.h#L484
1 |
kmem_cache_alloc()
kmem_cache_alloc() used to allocate object from slab cache
https://elixir.bootlin.com/linux/v7.0/source/kernel/cred.c#L558
1 | struct cred *prepare_kernel_cred(struct task_struct *daemon) |
slub_percpu_sheaves
every logical cpu has it’s own slub_percpu_sheaves
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L420
1 | struct slub_percpu_sheaves { |
example: CPU 0 and CPU 1
slab_sheaf
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L404
1 | struct slab_sheaf { |
slab_sheaves layout
struct slab_sheaf *main: full; default sheaf. alloc/free fastpath hits this first.struct slab_sheaf *spare: partially filled; swapped in when main emptiesstruct slab_sheaf *rcu_free: collects kfree_rcu objects, batch-freed after grace periodvoid *objects[]: points to an object in some slab held by sheaf (“held by sheaf” state, NOT in slab.freelist)- one
slub_percpu_sheavesper logical CPU, perkmem_cache, 16 logical CPUs × 200 caches = 3200 sheaf-sets in the system.
kmem_cache_node
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L430
1 | struct kmem_cache_node { |
slab
struct slab used to manage a single slab
a single slab contains multiple objects based on the object size and page size allocated by buddy allocator
example: allocated one 0x1000 size page from buddy allocator and the object size is 0x100 then theres 0x10 objects in the slab
https://elixir.bootlin.com/linux/v7.0/source/mm/slab.h#L74
1 | struct slab { |
node_barn
https://elixir.bootlin.com/linux/v7.0.10/source/mm/slub.c#L396
1 | struct node_barn { |
kmem_cache_node slabs/node barns layout

full layout

Allocation
Allocation flow overview

- API layer :
kmem_cache_alloc()/kmalloc(), only pick which kmem_cache to use - Fastpath :
alloc_from_pcs(), pop one object from the percpu sheaf . no lock on the node - Slowpath :
___slab_alloc(), go to the node partial list , then to the buddy allocator
Type of allocation API
kmem_cache_alloc()
used to allocate specific type of cache
https://elixir.bootlin.com/linux/v7.0/source/include/linux/slab.h#L768
1 |
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L4871
1 | void *kmem_cache_alloc_noprof(struct kmem_cache *s, gfp_t gfpflags) |
example:
kmalloc()
allocate from generic slab cache (kmalloc-8, kmalloc-16, … kmalloc-8k).
the kernel picks the right kmem_cache based on size and gfpflags
https://elixir.bootlin.com/linux/v7.0/source/include/linux/slab.h#L956
1 |
https://elixir.bootlin.com/linux/v7.0/source/include/linux/slab.h#L941
1 | static __always_inline __alloc_size(1) void *kmalloc_noprof(size_t size, gfp_t flags) |
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L5373
1 | void *__kmalloc_cache_noprof(struct kmem_cache *s, gfp_t gfpflags, size_t size) |
Entrypoint
the API mentioned above just to decide which slab cache to allocate and will always call the slab_alloc_node() in the end which is the actual entrypoint of the slab allocator
slab_alloc_node()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L4837
1 | static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list_lru *lru, |
alloc_from_pcs(), the Fastpath__slab_alloc_node(), the Slowpath, if Fastpath return null
Fastpath
alloc_from_pcs()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L4671
1 | static __fastpath_inline |
the core behavior of alloc_from_pcs() is basically 3 lines
took the cpu_sheaves from provided slab cache
1 | pcs = this_cpu_ptr(s->cpu_sheaves); |
the objects[] is a LIFO pointer array(like stack)
it pop the pointer from top of the array
the size control the index of array
1 | object = pcs->main->objects[pcs->main->size - 1]; |
decrease index
1 | pcs->main->size--; |

but if the main sheaf is empty the it will try refill with __pcs_replace_empty_main()
when main is empty : __pcs_replace_empty_main()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L4560
1 | static struct slub_percpu_sheaves * |
- its job is to get a non-empty main sheaf back
- when spare sheaf isnt empty it will
swap(pcs->main, pcs->spare)which is swapping the spare sheaf with main sheaf - if the spare sheaf is empty too
it will trybarn_replace_empty_sheaf(barn, pcs->main, allow_spin)to get full sheaf from barn - refill an empty sheaf
take spare orbarn_get_empty_sheaf()andrefill_sheaf()it from the node partial lists / buddy alloc_full_sheaf(), when theres no empty sheaf around , allocate a new sheaf struct and refill it
refill_sheaf()
when both spare or the barn only have empty sheaves
it will call this function to refill them from node partial lists/buddy
it also count as another entry of slowpath since it refill from node partial lists/buddy
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L2803
1 | static int refill_sheaf(struct kmem_cache *s, struct slab_sheaf *sheaf, |
it calculate how many objects needed to fill then pass it to refill_objects()
refill_objects()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L7151
1 | static unsigned int |
try order: local node partial -> remote node partial -> buddy , in a loop until min is reached
Slowpath
happened when alloc_from_pcs() failed
__slab_alloc_node()
1 | static __always_inline void *__slab_alloc_node(struct kmem_cache *s, |
pretty much just to call ___slab_alloc()
___slab_alloc()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L4374
1 | static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, |
get_from_partial(), take one object from a partial slabnew_slab(), get a new slab from buddy allocator ifget_from_partial()failed- after
new_slab()it callalloc_from_new_slab()
to take the first object out of it
get_from_partial()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L3926
1 | /* |
try local node partial list get_from_partial_node() first
if get_from_partial_node() failed it will try remote nodes get_from_any_partial()
get_from_partial_node()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L3789
1 | /* |
pop slab->freelist once with cmpxchg on freelist and counters
usually CONFIG_SLAB_FREELIST_HARDENED is on so the freelist pointer will get encoded so it use get_freepointer() to take the free pointer

Summarize

Free
Free flow overview

- API layer :
kmem_cache_free()/kfree()find whichkmem_cachethe object belongs to - fastpath :
free_to_pcs()push the object pointer into the percpu sheaf - slowpath :
__slab_free(), put the object back onslab->freelist
Type of free API
kmem_cache_free()
free an object back to a specific cache
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L6275
1 | void kmem_cache_free(struct kmem_cache *s, void *x) |
kfree()
free generic slab cache (kmalloc-8, kmalloc-16, … kmalloc-8k).
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L6462
1 | void kfree(const void *object) |
first calculate object located at what page
convert it to slab struct, the page and slab struct use the same memory just like union
after converted to slab
it use slab->slab_cache to know which kmem_cache this object from
Entrypoint
above APIs end in slab_free() the entry of slub allocator
slab_free()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L6158
1 | static __fastpath_inline |
free_to_pcs(), the Fastpath__slab_free, the Slowpath, if fastpath return null
Fastpath
free_to_pcs()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L5763
1 | static __fastpath_inline |
the core of fastpath just 1 line:
push the object to main sheaf
1 | pcs->main->objects[pcs->main->size++] = object; |
when main is full : __pcs_replace_full_main()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L5640
basically the opposite of __pcs_replace_empty_main()
Slowpath
__slab_free()
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c#L5470
1 | /* |
the core:
1 | set_freepointer(s, tail, old.freelist); /* object's FP <- old head */ |
just set the FP of object to current slab freelist head address
and set the current slab freelist head to the object we are freeing
after the object freed into the slab
it will check the current slab status:
- still partial: just return normally
- from full to partial:
calladd_partial(n, slab, ADD_TO_TAIL)to add the slab to partial slab list - from empty to partial:
callremove_partial(n, slab);to remove it from partial slab list
secondly calldiscard_slab(s, slab);return the slab back to buddy allocator
Summarize

Afterall
after the change of linux kernel 7.0 slub allocator
the performance in general showing an improvement, but with specific cases showing worse performance
but theres some worth mentioning security issue:
the CONFIG_SLAB_FREELIST_HARDENED and CONFIG_SLAB_FREELIST_RANDOM mitgations does not apply to sheaf layer also it wont check double free or memory corruption while freeing in sheaf layer and more issues…
References
https://slavin.cn/2024/05/06/Slub-allocator-%E5%88%86%E6%9E%90/
https://fosdem.org/2026/events/attachments/DPXVYJ-slub-sheaves-update/slides/267397/fosdem-sh_pvi6fpf.pdf
https://elixir.bootlin.com/linux/v7.0/source/mm/slub.c
https://elixir.bootlin.com/linux/v7.0/source/include/linux/slab.h
https://naup.mygo.tw/2026/08/22/Harvest-Season-For-Slub/