alloc_slab_page() 是 SLUB 中负责向伙伴系统申请物理页的底层函数,是 slab 页的“进货渠道”。它把 kmem_cache_order_objects 里编码的 order 解出来,调用伙伴系统分配 2^order 个连续页。
一、函数签名
static inline struct slab *alloc_slab_page(gfp_t flags, int node,
struct kmem_cache_order_objects oo)
| flags | 分配标志(GFP_KERNEL、GFP_ATOMIC、__GFP_ZERO 等) |
| node | 目标 NUMA 节点 |
| oo | 打包的 order + objects |
返回:新分配的 struct slab *,失败返回 NULL。
二、典型实现
static inline struct slab *alloc_slab_page(gfp_t flags, int node,
struct kmem_cache_order_objects oo)
{
struct folio *folio;
struct slab *slab;
unsigned int order = oo_order(oo);
if (node == NUMA_NO_NODE)
folio = (struct folio *)alloc_pages(flags, order);
else
folio = (struct folio *)__alloc_pages_node(node, flags, order);
if (!folio)
return NULL;
slab = folio_slab(folio);
__folio_set_slab(folio);
…
return slab;
}
逻辑:
解出 order:oo_order(oo)(前面讲过,x.x >> OO_SHIFT)。
按节点分配:
-
NUMA_NO_NODE:用 alloc_pages(),由伙伴系统选节点。
-
指定节点:用 __alloc_pages_node(),优先从该节点分配。
失败返回 NULL:调用者(allocate_slab)据此走 ORDER_FALLBACK 或报错。
转换为 slab:folio_slab(folio) 取得对应的 struct slab(较新内核里 folio/page/slab 关系紧密)。
设置页标志:__folio_set_slab(folio) 标记该页为 slab 页,供后续 PageSlab() 判断。
三、oo 参数的来源
oo 来自 struct kmem_cache 的 s->oo:
s->oo = oo_make(order, size); // calculate_order() 里算好
alloc_slab_page() 只是消费这个打包值,取出 order 去分配。对象数由 oo_objects(oo) 在别处使用。
四、与伙伴系统的接口
| alloc_pages(flags, order) | 未指定节点,伙伴系统自选 |
| __alloc_pages_node(node, flags, order) | 指定节点,优先本地 |
| alloc_pages_node(node, flags, order) | 封装,node == NUMA_NO_NODE 时退化为 alloc_pages |
SLUB 用 __alloc_pages_node() 而非普通 alloc_pages(),是为了NUMA 亲和性:尽量从对象归属的节点分配,减少跨节点访问。
五、node 参数的意义
-
分配时:node 可能来自 kmalloc_node() 参数、numa_node_id(),或 NUMA_NO_NODE。
-
释放时:不用这个函数,走 __free_pages()。
-
NUMA 严格模式(strict_numa):若开启,分配严格要求从 node 取,不匹配可能失败;默认允许回退到其他节点。
六、分配失败与 ORDER_FALLBACK
如果 alloc_pages() 返回 NULL:
-
调用者 allocate_slab() 可能尝试更大的 order 或不同的对象数方案。
-
相关统计 ORDER_FALLBACK 递增。
-
最终仍失败则返回 NULL,kmem_cache_alloc 走错误路径。
七、返回的是 struct slab * 而非 struct page *
较新内核里:
-
struct slab 是独立类型,与 struct page 通过 folio 关联。
-
folio_slab(folio) 把 folio 转成 struct slab。
-
早期版本直接用 struct page,SLUB 复用其字段;现在独立出来,更清晰。
这也是 print_slab_info()、slab->objects、slab->inuse 等字段能直接访问的原因。
八、与前面的呼应
| oo_order() | 解出 order |
| oo_objects() | 在别处用对象数 |
| struct kmem_cache_order_objects | oo 参数类型 |
| calculate_order() | 生成 s->oo |
| allocate_slab() | 调用者 |
| ORDER_FALLBACK | 分配失败的统计 |
| NUMA / strict_numa | node 参数的作用 |
| __folio_set_slab() | 标记页为 slab |
九、总结
alloc_slab_page(flags, node, oo) 从打包的 oo 中解出 order,按 node 用 alloc_pages() / __alloc_pages_node() 向伙伴系统申请 2^order 个连续页,取得对应的 struct slab,设置 PageSlab 标志后返回;失败返回 NULL,由 allocate_slab() 决定回退或报错。它是 SLUB 的页级进货入口,负责 NUMA 亲和性和与伙伴系统的对接。



