☰
page_alloc zone_spans_last_pfn
2026/10/21 2:30:41 网站建设 项目流程

zone_spans_last_pfn()是alloc_contig_pages()在扫描候选 PFN 范围时用于边界检查的辅助函数。它判断一个从start_pfn开始、长度为nr_pages的范围,其最后一页是否仍然落在指定的 zone 内。

核心实现

static bool zone_spans_last_pfn(const struct zone *zone, unsigned long start_pfn, unsigned long nr_pages) { unsigned long last_pfn = start_pfn + nr_pages - 1; return zone->zone_start_pfn <= last_pfn && last_pfn < zone_end_pfn(zone); }

逻辑解析

1. 计算最后一页的 PFN

unsigned long last_pfn = start_pfn + nr_pages - 1;
  • 范围是[start_pfn, start_pfn + nr_pages),所以最后一页的 PFN 是start_pfn + nr_pages - 1。

  • 减 1 是关键——nr_pages是页数,不是结束 PFN。

2. 判断最后一页是否在 zone 内

return zone->zone_start_pfn <= last_pfn && last_pfn < zone_end_pfn(zone);
  • zone->zone_start_pfn:zone 的起始 PFN(包含)。

  • zone_end_pfn(zone):zone 的结束 PFN(不包含),等于zone_start_pfn + spanned_pages。

  • 两个条件都满足,说明最后一页在 zone 内。

为什么只检查最后一页?

因为alloc_contig_pages()的扫描是从 zone 起始位置按nr_pages对齐向上推进的:

pfn = ALIGN(zone->zone_start_pfn, nr_pages); while (zone_spans_last_pfn(zone, pfn, nr_pages)) { if (pfn_range_valid_contig(zone, pfn, nr_pages)) { ... } pfn += nr_pages; }
  • pfn从对齐后的起始位置开始,每次递增nr_pages。

  • 只要第一页在 zone 内(由循环起点保证),且最后一页也在 zone 内,那么中间的页必然也在 zone 内(因为 zone 是连续 PFN 区间)。

  • 所以只检查最后一页就够了,无需遍历整个范围。这是一个性能优化。

与zone_spans_pfn()的关系

前面在get_pfnblock_bitmap_bitidx()的VM_BUG_ON_PAGE中见过zone_spans_pfn():

static inline bool zone_spans_pfn(const struct zone *zone, unsigned long pfn) { return zone->zone_start_pfn <= pfn && pfn < zone_end_pfn(zone); }

zone_spans_last_pfn()就是对范围最后一页调用zone_spans_pfn()的逻辑,只是没有直接调用它,而是内联了相同的判断,并多做了一步last_pfn的计算。

在调用链中的位置

alloc_contig_pages(nr_pages, gfp_mask, nid, nodemask) └── for_each_zone_zonelist_nodemask(zone, ...): ├── pfn = ALIGN(zone->zone_start_pfn, nr_pages) └── while (zone_spans_last_pfn(zone, pfn, nr_pages)): // ← 这里 ├── if (pfn_range_valid_contig(zone, pfn, nr_pages)) │ └── __alloc_contig_pages(pfn, nr_pages, gfp_mask) └── pfn += nr_pages

为什么需要它?

alloc_contig_pages()需要确保候选范围不跨越 zone 边界——因为alloc_contig_range()要求所有页属于同一个 zone(pfn_range_valid_contig()也会检查page_zone(page) != z)。

zone_spans_last_pfn()作为循环条件,当范围超出 zone 边界时自动终止扫描,避免对无效范围调用pfn_range_valid_contig()。

边界情况

  • nr_pages = 0:last_pfn = start_pfn - 1,可能下溢或落在 zone 外,但alloc_contig_pages()不会用 0 调用它。

  • 范围恰好到 zone 末尾:last_pfn = zone_end_pfn(zone) - 1,满足条件,返回true。

  • 范围恰好超出 zone 末尾:last_pfn = zone_end_pfn(zone),不满足< zone_end_pfn(zone),返回false,循环终止。

总结

zone_spans_last_pfn()是alloc_contig_pages()扫描循环的边界守门人:它计算候选范围的最后一页 PFN,判断它是否仍在 zone 的[zone_start_pfn, zone_end_pfn)区间内。由于扫描是按nr_pages对齐递增的,只需检查最后一页即可保证整个范围都在 zone 内。当范围超出 zone 边界时返回false,循环自动终止,避免对无效范围做后续检查。

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

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

立即咨询