check_slab()是 SLUB 调试中校验 slab 页级基本状态的函数,位于对象级检查(check_object)之前,先确认 slab 页本身的结构字段是否合理。
一、函数签名
static int check_slab(struct kmem_cache *s, struct slab *slab)
| 参数 | 含义 |
|---|---|
s | 缓存 |
slab | 要校验的 slab 页 |
返回int:1 表示 slab 基本状态正常,0 表示发现异常。
二、典型实现
static int check_slab(struct kmem_cache *s, struct slab *slab) { int maxobj; if (!virt_addr_valid(slab)) { slab_err(s, slab, "Not a valid slab page"); return 0; } if (!PageSlab(slab_page(slab))) { slab_err(s, slab, "Not a slab page"); return 0; } maxobj = (PAGE_SIZE << oo_order(s->oo)) / s->size; if (slab->objects > maxobj) { slab_err(s, slab, "objects %u > max %u", slab->objects, maxobj); return 0; } if (slab->inuse > slab->objects) { slab_err(s, slab, "inuse %u > max %u", slab->inuse, slab->objects); return 0; } if (slab->objects == 0 && slab->inuse != 0) { slab_err(s, slab, "inuse %u, but no objects", slab->inuse); return 0; } return 1; }逻辑分几层:
1. 地址有效性
if (!virt_addr_valid(slab)) { slab_err(s, slab, "Not a valid slab page"); return 0; }virt_addr_valid(slab):检查slab指针是否是合法内核虚拟地址。防止后续字段访问触发缺页或访问非法地址。
失败则报告
"Not a valid slab page"。
2. 确实是 slab 页
if (!PageSlab(slab_page(slab))) { slab_err(s, slab, "Not a slab page"); return 0; }slab_page(slab):从struct slab取回对应的struct page(较新内核里struct slab与struct page关系密切,可能有转换)。PageSlab():页标志,确认该页确实是 SLUB 管理的 slab 页,而不是普通匿名页、页缓存页等。失败则报告
"Not a slab page"——说明传进来的slab根本不是 slab。
3. 对象总数上限
maxobj = (PAGE_SIZE << oo_order(s->oo)) / s->size; if (slab->objects > maxobj) { slab_err(s, slab, "objects %u > max %u", slab->objects, maxobj); return 0; }maxobj:一个 slab 页理论上最多能放的对象数,由页大小除以对象槽位大小算出。如果
slab->objects超过这个上限,说明字段被破坏或缓存布局不一致。这是前面
order_objects()的简化版(不含元数据扣除,用于上限判断足够)。
4. inuse 不超 objects
if (slab->inuse > slab->objects) { slab_err(s, slab, "inuse %u > max %u", slab->inuse, slab->objects); return 0; }inuse(已分配对象数)不能超过objects(总对象数)。超过说明计数错误。
5. objects 为 0 时 inuse 必须为 0
if (slab->objects == 0 && slab->inuse != 0) { slab_err(s, slab, "inuse %u, but no objects", slab->inuse); return 0; }空 slab(
objects == 0)不应有已分配对象。这是边界情况的一致性检查。
三、校验层次
check_slab()是第一层,只检查 slab 页的结构字段是否合理:
check_slab(s, slab) ← 本函数:页级基本状态 ├─ 地址有效? ├─ 是 slab 页? ├─ objects ≤ 上限? ├─ inuse ≤ objects? └─ objects=0 ⇒ inuse=0? validate_slab(s, slab, map) ├─ check_slab(s, slab) ← 先做页级检查 ├─ __fill_map(...) ← 填充已分配位图 └─ for_each_object(...) └─ check_object(...) ← 对象级详细校验
| 层次 | 函数 | 检查内容 |
|---|---|---|
| 页级 | check_slab | 地址、页类型、objects/inuse 合理范围 |
| 对象级 | check_object | 红区、poison、padding 内存内容 |
| 汇总 | validate_slab | 组合前两者,遍历所有对象 |
四、与slab_err()的配合
每个失败分支都调用slab_err():
slab_err(s, slab, "Not a slab page");
回顾slab_err():
__printf(3, 4)编译期格式检查。KUnit 环境下静默计数。
__slab_bug()打印错误描述。__slab_err(slab)打印 slab 状态(objects/inuse/freelist/flags)。add_taint(TAINT_BAD_PAGE, ...)污染内核。
所以check_slab发现异常时,日志会自动附带 slab 状态,方便定位。
五、输出示例
============================================================================= BUG kmalloc-64 (debug): Not a slab page ... Not a slab page Slab 0xffffea0004a8c000 objects=0 used=1 fp=0x0000000000000000 flags=-
或:
BUG kmalloc-64 (debug): inuse 65 > max 64 Slab 0xffffea0004a8c000 objects=