读取某个 pageblock 的迁移类型(migratetype)。它是反碎片机制的核心读取接口。
一、函数签名
__always_inline enum migratetype get_pfnblock_migratetype(const struct page *page, unsigned long pfn)
参数
| 参数 | 类型 | 含义 |
|---|---|---|
page | const struct page * | 目标 pageblock 内的某一页(用于定位位图) |
pfn | unsigned long | 目标页帧号 |
返回值
enum migratetype:该 pageblock 的迁移类型(MIGRATE_UNMOVABLE、MIGRATE_MOVABLE、MIGRATE_RECLAIMABLE、MIGRATE_CMA等)。
__always_inline
强制内联。这个函数在分配/释放/规整的热路径上频繁调用,内联后零调用开销。
与前面的
get_pfnblock_bit()(普通函数)不同,这里因为是简单读取+移位,适合强制内联。
注释的关键点
* Use get_pfnblock_migratetype() if caller already has both @page and @pfn * to save a call to page_to_pfn().
提示:如果调用方已经有
page和pfn,就用这个双参数版本,省去page_to_pfn()的开销。还存在一个单参数版本(通常叫
get_pageblock_migratetype(page)),内部会调用page_to_pfn(page)把 page 转成 pfn。所以这是性能优化接口:已知 pfn 时避免重复转换。
二、典型实现(补充)
__always_inline enum migratetype get_pfnblock_migratetype(const struct page *page, unsigned long pfn) { unsigned long flags; flags = __get_pfnblock_flags_mask(page, pfn, PB_migrate_mask); return (enum migratetype)((flags >> PB_migrate) & ((1 << PB_migratetype_bits) - 1)); }逐步说明
1. 读取迁移类型字段
flags = __get_pfnblock_flags_mask(page, pfn, PB_migrate_mask);
PB_migrate_mask:迁移类型字段的掩码,覆盖PB_migrate到PB_migrate_end的位。典型定义:
#define PB_migrate_mask (((1 << PB_migratetype_bits) - 1) << PB_migrate)
即"从
PB_migrate开始的PB_migratetype_bits位全 1"。__get_pfnblock_flags_mask():前面讲的读取实现,返回按 mask 过滤后的位。结果
flags是原始位模式(可能还带偏移,取决于__get_pfnblock_flags_mask是否右移对齐)。
2. 提取迁移类型值
return (enum migratetype)((flags >> PB_migrate) & ((1 << PB_migratetype_bits) - 1));
flags >> PB_migrate:把字段右移到最低位。& ((1 << PB_migratetype_bits) - 1):只保留PB_migratetype_bits位,得到纯数值。强转为
enum migratetype返回。
注意:具体移位方式取决于__get_pfnblock_flags_mask()的实现约定:
如果它返回的是已右移对齐的值(
(flags & mask) >> __ffs(mask)),那么这里只需& ((1 << PB_migratetype_bits) - 1),不用再>> PB_migrate。如果它返回未移位的原始位,则这里需要
>> PB_migrate。
不同内核版本实现略有差异,但语义一致:取出迁移类型字段的数值。
三、单参数版本对比
典型的单参数版本:
static inline enum migratetype get_pageblock_migratetype(const struct page *page) { return get_pfnblock_migratetype(page, page_to_pfn(page)); }调用
page_to_pfn(page)把页转成 pfn。page_to_pfn()本身是(page - vmemmap) + ...或类似计算,在CONFIG_SPARSEMEM_VMEMMAP下是简单减法,但仍是一步运算。如果调用方已经知道 pfn,直接用双参数版本可以省掉这一步。
这就是注释所说的 "save a call to page_to_pfn()"。
使用建议
| 场景 | 用哪个 |
|---|---|
只有page | get_pageblock_migratetype(page) |
已有page和pfn | get_pfnblock_migratetype(page, pfn)(更快) |
| 在遍历 pfn 的循环里 | 双参数版本,避免反复转换 |
四、迁移类型回顾
enum migratetype的值(与前面的migratetype_names[]对应):
| 值 | 名字 | 含义 |
|---|---|---|
MIGRATE_UNMOVABLE | "Unmovable" | 不可移动(内核结构、页表) |
MIGRATE_MOVABLE | "Movable" | 可移动(用户态页) |
MIGRATE_RECLAIMABLE | "Reclaimable" | 可回收(slab 缓存) |
MIGRATE_HIGHATOMIC | "HighAtomic" | 高优先级原子分配保留 |
MIGRATE_CMA | "CMA" | 连续内存分配器区域 |
MIGRATE_ISOLATE | "Isolate" | 被隔离(热插拔/规整) |
这些值编码在 pageblock 的PB_migrate字段里,本函数就是读出这个编码。
五、典型用途
1. 伙伴系统分配
__rmqueue_smallest()根据迁移类型从对应空闲链表取页。取之前/之后可能需要读 pageblock 的迁移类型。
2. 释放页时
free_one_page()/__free_one_page()需要知道页块的迁移类型,决定放回哪个链表。尤其合并(merge)伙伴时,迁移类型影响合并策略。
3. 反碎片 fallback
__rmqueue_fallback():当目标迁移类型没有空闲块时,找其他类型的页块"偷"。需要读取各 pageblock 的迁移类型来判断/更新。
4. 内存规整(compaction)
判断哪些 pageblock 可移动,决定是否迁移。
5. 内存热插拔
设置/读取 pageblock 迁移类型为
MIGRATE_ISOLATE,隔离待下线内存。
6./proc/pagetypeinfo
展示各迁移类型的空闲页统计,底层会读这些信息。
六、与前面内容的呼应
| 前面出现过的 | 这里的关联 |
|---|---|
migratetype_names[] | enum migratetype的名字表 |
PB_migrate/PB_migrate_end/PB_migratetype_bits | 迁移类型字段的位置和位数 |
PB_migrate_mask | 本函数传给__get_pfnblock_flags_mask()的 mask |
__get_pfnblock_flags_mask() | 本函数的底层读取 |
is_standalone_pb_bit() | 本函数读的是非独立的迁移类型字段,正好与之相对 |
get_pfnblock_bit() | 读独立位;本函数读迁移类型字段,两者互补 |
page_group_by_mobility_disabled | 控制是否按迁移类型分组,影响本函数结果的使用 |
调用链:
get_pfnblock_migratetype(page, pfn) // 双参数,热路径 ├── __get_pfnblock_flags_mask(page, pfn, PB_migrate_mask) │ ├── get_pageblock_bitmap() │ └── pfn_to_bitidx() └── 移位 + 掩码 → enum migratetype get_pageblock_migratetype(page) // 单参数,便捷 └── get_pfnblock_migratetype(page, page_to_pfn(page))
七、小结表
| 项 | 说明 |
|---|---|
| 函数名 | get_pfnblock_migratetype |
| 作用 | 读取某 pageblock 的迁移类型 |
参数page | 用于定位位图 |
参数pfn | 目标页帧号 |
| 返回值 | enum migratetype |
| 关键优化 | __always_inline;已知 pfn 时省去page_to_pfn() |
| 实现依赖 | __get_pfnblock_flags_mask(page, pfn, PB_migrate_mask)+ 移位/掩码 |
| 单参数版 | get_pageblock_migratetype(page)= 本函数 +page_to_pfn() |
| 典型用途 | 伙伴系统分配/释放、反碎片 fallback、规整、热插拔、/proc/pagetypeinfo |
一句话总结
get_pfnblock_migratetype(page, pfn)是读取 pageblock迁移类型的强制内联接口:它通过__get_pfnblock_flags_mask(page, pfn, PB_migrate_mask)取出PB_migrate字段并按PB_migratetype_bits解码成enum migratetype;因为已知 pfn 可省去page_to_pfn(),它是伙伴系统、反碎片、规整、内存热插拔等热路径上的关键读取函数,与读取独立位的get_pfnblock_bit()互补。