struct ttm_mem_zone 与 struct ttm_page_pool 的联系
内核版本5.4
它们 没有直接的指针引用 ,而是通过 struct ttm_mem_global 这个中间层间接关联。二者属于 TTM 内存管理体系的两个不同层次,各司其职,通过初始化时的参数传递和运行时的 API 调用协作。

关键联系点
1. 初始化时的联系 --- max_mem 参数传递
ttm_page_alloc_init() 在 ttm_mem_global_init() 中被调用,zone 的内存上限决定了 pool 的最大缓存页数:
ttm_memory.h
struct ttm_mem_zone *zone_kernel;
// zone_kernel->max_mem = 系统 RAM 的一半 (减去一些余量)
ttm_memory.c
ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// zone 的 max_mem / (2 * PAGE_SIZE) → pool 的 max_pages
即 pool 的最大缓存容量 = zone 内核区可用显存的一半(按页计算)。这是一个松耦合------zone 只提供一个数字,pool 用这个数字限制自己的缓存大小。
2. sysfs 层级的父子关系
pool manager 的 kobject 挂在 glob->kobj 下面,而每个 zone 的 kobj 也挂在 glob->kobj 下面:
ttm_page_alloc.c
ret = kobject_init_and_add(&_manager->kobj, &ttm_pool_kobj_type,
&glob->kobj, "pool");
所以在 sysfs 中它们是兄弟节点:
/sys/.../memory_accounting/
├── zone_kernel/ ← ttm_mem_zone (zone_kernel)
├── zone_dma32/ ← ttm_mem_zone (zone_dma32 或 highmem)
└── pool/ ← ttm_pool_manager
└── (各个 page_pool 的统计信息)
3. 运行时的联系 --- 记账 API
Page Pool 不直接操作 ttm_mem_zone 。Page Pool 负责物理页面的缓存和分配 ,而 ttm_mem_zone 负责内存用量记账 。它们通过 ttm_mem_global_alloc_page() / ttm_mem_global_free_page() 这个 API 桥接。
当 ttm_pool_populate() 分配页面时:
ttm_page_alloc.c
struct ttm_mem_global *mem_glob = ttm->bdev->glob->mem_glob;
ret = ttm_get_pages(ttm->pages, ttm->num_pages, ttm->page_flags,
ttm->caching_state); // 从 pool 获取页面
// ...
for (i = 0; i < ttm->num_pages; ++i) {
ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i],
PAGE_SIZE, ctx); // 向 zone 记账
}
ttm->caching_state 所以通过这个变量,mem zone和ttm_pool关联了起来
enum ttm_caching_state {
tt_uncached,
tt_wc,
tt_cached
};
释放时同理:
ttm_page_alloc.c
ttm_mem_global_free_page(mem_glob, ttm->pages[i], PAGE_SIZE); // 向 zone 减记账
ttm_mem_global_alloc_page() 内部会根据页面属性(是否 HIGHMEM)找到对应的 zone,更新其 used_mem:
ttm_memory.c
int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
struct page *page, uint64_t size,
struct ttm_operation_ctx *ctx)
{
struct ttm_mem_zone *zone = NULL;
// ...
#ifdef CONFIG_HIGHMEM
if (PageHighMem(page) && glob->zone_highmem != NULL)
zone = glob->zone_highmem;
else
#endif
zone = glob->zone_kernel;
// ... 在 zone 上记账,检查是否超过 max_mem/emer_mem ...
}
总结对比
| 维度 | ttm_mem_zone |
ttm_page_pool |
|---|---|---|
| 职责 | 内存用量记账、限额管理、swap 控制 | 物理页面的缓存池(减少 alloc/free 开销) |
| 文件 | ttm_memory.c |
ttm_page_alloc.c |
| 数量 | 最多 2 个(kernel + highmem/dma32) | 6 个(wc/uc × normal/dma32/huge) |
| 数据结构 | zone_mem, max_mem, used_mem, swap_limit |
list_head 页面链表, npages 计数 |
| 关联方式 | 通过 ttm_mem_global_alloc/free_page() 被调用 |
在 populate/unpopulate 时调用 zone 的记账 API |
| 初始化耦合 | zone_kernel->max_mem 传入 pool 的 max_pages |
max_pages 来自 zone 的 max_mem |
简而言之:ttm_mem_zone 是"会计",ttm_page_pool 是"仓库" 。它们不直接引用对方,而是都通过 ttm_mem_global 这个全局协调者进行间接协作------仓库每次进出货物都会通知会计记账。