是order_to_pindex()的逆函数,用于在PCP(Per-CPU Pages)批量管理中,把"链表索引 pindex"反解回"阶数 order"。
一、函数签名
static inline int pindex_to_order(unsigned int pindex)
参数
| 参数 | 类型 | 含义 |
|---|---|---|
pindex | unsigned int | PCP 中空闲链表的索引(pindex) |
返回值
int:对应的阶数order。
static inline
文件内私有且强制内联(或建议内联),因为这属于热路径上的简单计算。
二、背景:PCP 的pindex与order
1. PCP 缓存的结构
每个 CPU、每个 zone 有一个per_cpu_pages(简称 PCP),里面维护若干空闲页链表:
struct per_cpu_pages { spinlock_t lock; int count; /* 总页数 */ int high; /* 高水位 */ int batch; /* 批量补充/归还大小 */ struct list_head lists[NR_PCP_LISTS]; /* 多个链表 */ };lists[]的索引就是pindex。每个链表对应某种迁移类型 + 某个阶数的组合。
2. 为什么需要pindex而不是直接用(migratetype, order)?
PCP 需要为不同迁移类型、不同阶数分别维护链表。
但用二维数组
lists[MIGRATE_TYPES][MAX_ORDER]会:浪费空间(很多组合实际不用);
索引计算复杂。
内核改用一维数组 + 编码:
把
(migratetype, order)编码成一个整数pindex;用
order_to_pindex()编码,pindex_to_order()解码。
3. 编码方式
static inline unsigned int order_to_pindex(int migratetype, int order) { ... return (order << MIGRATE_PCPTYPES_LOG2) | migratetype; /* 或类似:低几位存 migratetype,高位存 order */ }低位存迁移类型(
migratetype),因为迁移类型数量较少(通常 < 8)。高位存阶数
order。
于是pindex_to_order()就是取出高位:
static inline int pindex_to_order(unsigned int pindex) { return pindex >> MIGRATE_PCPTYPES_LOG2; /* 或 pindex >> ilog2(MIGRATE_PCPTYPES) */ }4. 另一种编码:order 直接映射
不同内核版本实现不同。有的版本把 PCP 链表按 order 分组,迁移类型在同一 order 内再分:
/* 每个 order 占 NR_PCP_LISTS_PER_ORDER 个 pindex */ #define NR_PCP_LISTS_PER_ORDER ... static inline int pindex_to_order(unsigned int pindex) { return pindex / NR_PCP_LISTS_PER_ORDER; }或用移位:
return pindex >> NR_PCP_LISTS_PER_ORDER_SHIFT;
关键:pindex_to_order()必须与order_to_pindex()严格互逆。
三、与order_to_pindex()的对称关系
| 函数 | 方向 | 输入 | 输出 |
|---|---|---|---|
order_to_pindex(migratetype, order) | 编码 | 迁移类型 + 阶数 | pindex |
pindex_to_order(pindex) | 解码 | pindex | 阶数order |
编码:
pindex = f(migratetype, order)解码阶数:
order = pindex_to_order(pindex)通常还有一个
pindex_to_migratetype(pindex)(或类似)解码迁移类型。
为什么只需要pindex_to_order()而不常需要解码迁移类型?
在很多路径中,阶数是关键信息(决定页块大小、伙伴合并等);
迁移类型有时不需要从 pindex 反解(调用方本来就知道,或通过其他方式获取);
所以
pindex_to_order()更常用,被单独定义。
四、典型使用场景
1.free_pcppages_bulk()—— 批量归还 PCP 页到伙伴系统
static void free_pcppages_bulk(struct zone *zone, int count, struct per_cpu_pages *pcp, ...) { ... for (pindex = 0; pindex < NR_PCP_LISTS; pindex++) { struct list_head *list = &pcp->lists[pindex]; ... order = pindex_to_order(pindex); /* 用 order 调用 __free_one_page() 归还 */ ... } }遍历 PCP 各链表,从
pindex反解order,才能正确释放对应大小的页块。
2.rmqueue_pcplist()/rmqueue_bulk()—— 从 PCP 分配
分配时需要知道从哪个链表取、页块多大。
可能用
order_to_pindex()定位链表,用pindex_to_order()确认阶数。