rmqueue_pcplist()是 PCP(Per-CPU Pages)缓存分配路径的锁封装入口,负责在持有 PCP 锁的前提下,从当前 CPU 的 PCP 链表中取出一个页块。
核心作用与定位
它是__rmqueue_pcplist()的外层封装。两者的分工非常明确:
| 函数 | 职责 |
|---|---|
rmqueue_pcplist() | 获取 PCP 锁、计算链表指针、调用__rmqueue_pcplist()、释放锁 |
__rmqueue_pcplist() | 在持锁状态下执行实际的取页逻辑 |
核心逻辑
static struct page *rmqueue_pcplist(struct zone *preferred_zone, struct zone *zone, unsigned int order, int migratetype, unsigned int alloc_flags) { struct per_cpu_pages *pcp; struct list_head *list; struct page *page; unsigned long flags; /* 1. 获取当前 CPU 的 PCP 结构 */ pcp = pcp_spin_lock_irqsave(zone->per_cpu_pageset); /* 2. 计算目标链表指针 */ list = &pcp->lists[order_to_pindex(migratetype, order)]; /* 3. 调用内部函数取页 */ page = __rmqueue_pcplist(zone, order, migratetype, alloc_flags, pcp, list); /* 4. 释放 PCP 锁 */ pcp_spin_unlock_irqrestore(pcp, flags); return page; }逐步说明
1. 获取 PCP 锁
pcp = pcp_spin_lock_irqsave(zone->per_cpu_pageset);
pcp_spin_lock系列:先pcpu_task_pin()禁止迁移,再this_cpu_ptr()定位当前 CPU 的 PCP,最后加锁。用
irqsave版本同时保存中断状态,防止中断重入。
2. 计算目标链表
list = &pcp->lists[order_to_pindex(migratetype, order)];
order_to_pindex():把(migratetype, order)编码成 PCP 链表数组的扁平索引。
3. 调用内部函数
page = __rmqueue_pcplist(zone, order, migratetype, alloc_flags, pcp, list);
__rmqueue_pcplist():链表空则批量补充,否则从头部取页,并做坏页重试。
4. 释放 PCP 锁
pcp_spin_unlock_irqrestore(pcp, flags);
解锁并恢复中断状态(
pcp_spin_unlock+local_irq_restore)。
在分配路径中的位置
它是rmqueue()的首选路径:
rmqueue(preferred_zone, zone, order, gfp_flags, migratetype, alloc_flags) ├── if (pcp_allowed_order(order)) { │ page = rmqueue_pcplist(preferred_zone, zone, order, migratetype, alloc_flags); │ if (page) { │ zone_statistics(preferred_zone, zone, 1); │ return page; │ } │ } └── page = rmqueue_buddy(preferred_zone, zone, order, alloc_flags, migratetype);关于alloc_flags参数
虽然rmqueue_pcplist()接收alloc_flags并传递给__rmqueue_pcplist(),但__rmqueue_pcplist()本身并不直接使用它——它只负责从 PCP 链表取页。alloc_flags的实际用途在批量补充时:rmqueue_bulk()会把它传给__rmqueue(),影响伙伴系统的 fallback 策略(如ALLOC_NOFRAGMENT控制是否允许从低阶偷页)。
与pcp_allowed_order()的关系
rmqueue()在调用rmqueue_pcplist()之前会先用pcp_allowed_order(order)检查阶数是否允许走 PCP。只有order <= PAGE_ALLOC_COSTLY_ORDER(通常为 3)的请求才会进入 PCP 路径,更高阶的请求直接走rmqueue_buddy()。
总结
rmqueue_pcplist()是 PCP 分配路径的锁封装层:它通过pcp_spin_lock_irqsave()获取当前 CPU 的 PCP 锁,用order_to_pindex()定位目标链表,调用__rmqueue_pcplist()执行实际的取页和批量补充,最后释放锁并恢复中断。它与__rmqueue_pcplist()形成清晰的“锁封装 + 持锁执行”分工,是rmqueue()在 PCP 允许的阶数范围内的首选分配路径。