☰
page_alloc __rmqueue_pcplist
2026/10/11 1:47:35 网站建设 项目流程

__rmqueue_pcplist()是 PCP(Per-CPU Pages)缓存分配路径的核心内部函数,负责从当前 CPU 的 PCP 链表中取页,并在链表为空时触发批量补充。

核心作用与定位

它是rmqueue_pcplist()的下游函数,调用方必须已经持有 PCP 锁。它的职责是在 PCP 缓存中查找可用页块,如果目标链表为空,则从伙伴系统批量补充。

核心逻辑

static inline struct page * __rmqueue_pcplist(struct zone *zone, unsigned int order, int migratetype, unsigned int alloc_flags, struct per_cpu_pages *pcp, struct list_head *list) { struct page *page; do { if (list_empty(list)) { int batch = nr_pcp_alloc(pcp, zone, order); int alloced; alloced = rmqueue_bulk(zone, order, batch, list, migratetype, alloc_flags); pcp->count += alloced << order; if (unlikely(list_empty(list))) return NULL; } page = list_first_entry(list, struct page, lru); list_del(&page->lru); pcp->count -= 1 << order; } while (check_new_pcp(page, order)); return page; }

逐步说明

1. 链表为空时批量补充

if (list_empty(list)) { int batch = nr_pcp_alloc(pcp, zone, order); int alloced = rmqueue_bulk(zone, order, batch, list, migratetype, alloc_flags); pcp->count += alloced << order; if (unlikely(list_empty(list))) return NULL; }
  • nr_pcp_alloc()动态计算批量大小。

  • rmqueue_bulk()从伙伴系统批量取页,填入 PCP 链表。

  • 更新pcp->count(页数,非块数)。

  • 若补充后链表仍为空(伙伴系统无页),直接返回NULL。

2. 从链表头部取页

page = list_first_entry(list, struct page, lru); list_del(&page->lru); pcp->count -= 1 << order;
  • 从链表头部取页(LIFO),优先复用最近释放的页。

  • 更新 PCP 计数。

3. 坏页检查与重试

} while (check_new_pcp(page, order));
  • 调试配置下,用check_new_pcp()校验取出的页。

  • 若发现坏页,循环重试,避免把损坏的页交给调用方。

2022 年的计数溢出修复

2022 年 Chen Wandun 发现了一个问题:当目标 order 的链表为空,而其他 order 的链表非空时,rmqueue_bulk()可能补充过多页,导致pcp->count超过pcp->high。

修复方案是在__rmqueue_pcplist()中增加一行钳制:

batch = min(batch, (high - pcp->count) >> order);

确保补充后不会超过高水位。这个修复后来被整合进nr_pcp_alloc()的逻辑中。

在分配路径中的位置

rmqueue_pcplist() └── pcp_spin_lock(...) // 获取 PCP 锁 └── __rmqueue_pcplist(zone, order, migratetype, alloc_flags, pcp, list) // ← 这里 ├── if (list_empty) rmqueue_bulk(...) // 批量补充 ├── list_del(...) // 从链表取页 └── while (check_new_pcp(...)) // 坏页重试

总结

__rmqueue_pcplist()是 PCP 缓存的取页执行器:它在持有 PCP 锁的前提下,检查目标链表是否为空,为空则通过nr_pcp_alloc()和rmqueue_bulk()从伙伴系统批量补充;随后从链表头部取页并更新计数,调试配置下通过check_new_pcp()重试坏页。它是rmqueue_pcplist()的核心实现,与nr_pcp_alloc()、rmqueue_bulk()紧密协作,构成 PCP 分配路径的完整链路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询