设置某个 pageblock 的迁移类型。它是反碎片机制的核心写入接口,与get_pfnblock_migratetype()读配对。
一、函数签名
static void set_pageblock_migratetype(struct page *page, enum migratetype migratetype)
参数
| 参数 | 类型 | 含义 |
|---|---|---|
page | struct page * | 目标 pageblock 内的某一页(用于定位 zone/位图) |
migratetype | enum migratetype | 要设置的迁移类型 |
返回值
void:无返回值。
static
文件内私有,不导出。仅在
page_alloc.c内部使用。对外可能通过其他封装(如
set_pageblock_migratetype_nolock())或宏提供。
注释
/** * set_pageblock_migratetype - Set the migratetype of a pageblock * @page: The page within the block of interest * @migratetype: migratetype to set */
"Set the migratetype of a pageblock":设置一个 pageblock 的迁移类型。
注意:不是 page,而是 pageblock——整个
2^pageblock_order页的属性。
二、与读函数对比
| 函数 | 方向 | 参数 | 返回 |
|---|---|---|---|
get_pfnblock_migratetype(page, pfn) | 读 | page, pfn | enum migratetype |
set_pageblock_migratetype(page, migratetype) | 写 | page, migratetype | void |
参数差异:写函数为什么没有pfn?
读函数
get_pfnblock_migratetype(page, pfn)同时给page和pfn,是为了省去page_to_pfn()(性能优化)。写函数
set_pageblock_migratetype(page, migratetype)只给page,内部自己算 pfn:unsigned long pfn = page_to_pfn(page);
原因:写操作本身就是慢路径(分配/释放/规整时调用),
page_to_pfn()的开销可忽略,API 简洁更重要。若调用方已有 pfn,可能内部有另一个接口(如
set_pfnblock_migratetype(page, migratetype, pfn))来复用。
三、典型实现
static void set_pageblock_migratetype(struct page *page, enum migratetype migratetype) { if (unlikely(page_group_by_mobility_disabled)) return; set_pfnblock_flags_mask(page, page_to_pfn(page), (unsigned long)migratetype, PB_migrate_mask); }或更直接地编码到字段位置:
static void set_pageblock_migratetype(struct page *page, enum migratetype migratetype) { unsigned long pfn = page_to_pfn(page); if (unlikely(page_group_by_mobility_disabled)) return; /* * 把 migratetype 编码到 PB_migrate 字段: * (migratetype << PB_migrate) & PB_migrate_mask */ set_pfnblock_flags_mask(page, pfn, (unsigned long)migratetype << PB_migrate, PB_migrate_mask); }逐步说明
1. 禁用分组的快速返回
if (unlikely(page_group_by_mobility_disabled)) return;
page_group_by_mobility_disabled:当系统内存太小、分组收益不大时,内核禁用按迁移性分组。
禁用后,pageblock 的迁移类型不再有意义(所有页混在一起管理),设置它没有用处。
直接
return,跳过写入,省开销。unlikely():因为运行期绝大多数系统不会禁用分组(只有小内存系统才禁用),所以偏向"不返回"。
2. 把迁移类型编码到字段
(unsigned long)migratetype << PB_migrate
PB_migrate:迁移类型字段的起始位偏移。把
migratetype值左移到该字段位置。例如
PB_migrate = 0、migratetype = MIGRATE_MOVABLE (1)→ 编码为1。
3. 用 mask 限定只改迁移类型字段
PB_migrate_mask
覆盖
PB_migrate ~ PB_migrate_end的掩码。保证只改迁移类型字段,其他独立位(如
PB_migrate_skip)保持不变。
4. 调用set_pfnblock_flags_mask()
set_pfnblock_flags_mask(page, pfn, 编码值, PB_migrate_mask);
这是
__set_pfnblock_flags_mask()的对外包装(带VM_BUG_ON等检查)。底层做读-改-写:
(旧值 & ~mask) | (新值 & mask),只更新迁移类型字段。
完整语义
set_pageblock_migratetype(page, MIGRATE_MOVABLE) ↓ if (分组禁用) return; // 小内存优化 ↓ pfn = page_to_pfn(page) ↓ set_pfnblock_flags_mask(page, pfn, MIGRATE_MOVABLE << PB_migrate, // 新值 PB_migrate_mask) // 只改这几位 ↓ 底层读-改-写,其他位不动
四、并发与锁
这个函数本身不加锁,正确性依赖调用方:
常见调用点在持
zone->lock或pgdat->node_size_lock等锁的情况下;或者操作的是当前 CPU 独有的页块(不冲突);
多 CPU 并发修改同一 pageblock的迁移类型需要外部同步。
这也是它为什么是static且注释简洁——它是内部工具,同步责任在调用方。
五、典型用途
1. 伙伴系统初始化
启动时把各 pageblock 的迁移类型设为初始值(通常是
MIGRATE_MOVABLE或MIGRATE_UNMOVABLE)。
2. 分配时改变迁移类型
当从某迁移类型 fallback 到另一类型取页时,可能需要更新 pageblock 的迁移类型,反映实际使用情况。
例如
__rmqueue_fallback()中"偷"了别的类型的页块后,可能把该 pageblock 标记为新类型,避免后续继续误用。
3. 内存规整(compaction)
规整前把页块标记为
MIGRATE_ISOLATE(隔离),规整后恢复。通过
set_pageblock_migratetype(page, MIGRATE_ISOLATE)实现。
4. CMA 区域
CMA 预留的内存页块迁移类型设为
MIGRATE_CMA,表示"这块给 CMA 用"。分配时 CMA 类型优先满足 CMA 请求,非 CMA 请求可临时借用。
5. 内存热插拔
下线内存前,把页块迁移类型设为
MIGRATE_ISOLATE,隔离后迁移走页,再下线。上线时恢复迁移类型。
6.ZONE_MOVABLE
MOVABLE zone 的页块迁移类型通常是
MIGRATE_MOVABLE。
六、与前面内容的呼应
| 前面出现过的 | 这里的关联 |
|---|---|
migratetype_names[] | enum migratetype的名字表 |
get_pfnblock_migratetype() | 读配对 |
PB_migrate/PB_migrate_end/PB_migrate_mask | 编码与掩码 |
__set_pfnblock_flags_mask()/set_pfnblock_flags_mask() | 底层写入实现 |
page_group_by_mobility_disabled | 分组禁用时直接 return |
MIGRATE_ISOLATE/MIGRATE_CMA | 特殊迁移类型,用此函数设置 |
PB_migrate_skip(独立位) | 不受本函数影响,因为 mask 只覆盖迁移类型字段 |
调用链:
set_pageblock_migratetype(page, migratetype) ├── if (page_group_by_mobility_disabled) return; // 分组禁用 ├── pfn = page_to_pfn(page) └── set_pfnblock_flags_mask(page, pfn, migratetype << PB_migrate, PB_migrate_mask) └── __set_pfnblock_flags_mask(...) // 读-改-写 ├── get_pageblock_bitmap() ├── pfn_to_bitidx() └── 更新位
七、小结表
| 项 | 说明 |
|---|---|
| 函数名 | set_pageblock_migratetype |
| 作用 | 设置某 pageblock 的迁移类型 |
参数page | 用于定位 zone/位图,内部转 pfn |
参数migratetype | 要设置的迁移类型 |
| 返回值 | void |
| 可见性 | static,文件内私有 |
| 关键优化 | 分组禁用(page_group_by_mobility_disabled)时直接返回 |
| 实现依赖 | set_pfnblock_flags_mask(page, pfn, migratetype << PB_migrate, PB_migrate_mask) |
| 并发 | 不加锁,正确性依赖调用方同步 |