unreserve_highatomic_pageblock()是reserve_highatomic_pageblock()的逆操作,用于在内存分配即将失败(甚至面临 OOM)时,释放之前保留的 MIGRATE_HIGHATOMIC pageblock,将其归还给常规分配使用。
核心作用与背景
reserve_highatomic_pageblock():它在高阶原子分配失败时,从普通页块中“征用”一块作为MIGRATE_HIGHATOMIC保留区。
unreserve_highatomic_pageblock()则是在内存压力极大时,把这些保留的“救命粮”拿出来救急。它的设计意图是:失败的原子分配比 OOM 更容易恢复,所以宁可暂时牺牲高阶原子分配的可靠性,也要避免 OOM。
参数解析
| 参数 | 含义 |
|---|---|
ac | 分配上下文(包含 zonelist、highest_zoneidx、nodemask、migratetype 等) |
force | 是否强制解除保留,即使保留量低于一个 pageblock |
核心逻辑
根据内核源码,它的流程如下:
1. 遍历 zonelist 中的每个 zone
for_each_zone_zonelist_nodemask(zone, z, zonelist, ac->highest_zoneidx, ac->nodemask) {2. 保留量检查(除非force)
if (!force && zone->nr_reserved_highatomic <= pageblock_nr_pages) continue;
非强制模式下,如果该 zone 的保留量不超过一个 pageblock,保留它,不解除。
这是“保底”策略:至少给高阶原子分配留一点余地。
3. 持锁扫描所有 order
spin_lock_irqsave(&zone->lock, flags); for (order = 0; order < MAX_ORDER; order++) { struct free_area *area = &(zone->free_area[order]); page = get_page_from_free_area(area, MIGRATE_HIGHATOMIC); if (!page) continue;从低阶到高阶扫描
MIGRATE_HIGHATOMIC链表,找到第一个有空闲页的 order。
4. 更新保留计数(处理竞态)
if (is_migrate_highatomic_page(page)) { zone->nr_reserved_highatomic -= min(pageblock_nr_pages, zone->nr_reserved_highatomic); }注释解释了竞态来源:在页释放路径中,迁移类型的改变是竞态的,可能导致同一个 pageblock 的页被多次计入
MIGRATE_HIGHATOMIC链表。这里用is_migrate_highatomic_page()判断,确保只减一次nr_reserved_highatomic。使用
min()防止下溢。
5. 转换迁移类型并移动页块
set_pageblock_migratetype(page, ac->migratetype); ret = move_freepages_block(zone, page, ac->migratetype, NULL); if (ret) { spin_unlock_irqrestore(&zone->lock, flags); return ret; }把该 pageblock 的迁移类型改为请求类型(
ac->migratetype),而不是MIGRATE_UNMOVABLE或其他。注释解释了原因:如果总是转成UNMOVABLE,会增加“无法完全释放的 pageblock”数量,加剧碎片化。move_freepages_block()将该 pageblock 内的空闲页移到新类型的链表。成功移动页数
> 0则返回,表示解除成功。
历史演进
2016 年 Minchan Kim 的修复:
旧代码只要找到
MIGRATE_HIGHATOMIC的 pageblock 就bail out,即使没有真正移动任何页。修复后,只有
move_freepages_block()实际移动了页(ret > 0)才返回成功。同时增加了
is_migrate_highatomic_page()检查,避免竞态导致的重复计数。
2026 年的锁守卫重构:
用
guard(spinlock_irqsave)(&zone->lock)替代显式的spin_lock_irqsave/spin_unlock_irqrestore,简化代码并避免遗漏解锁。
调用场景
它被__alloc_pages_slowpath()在内存分配即将失败、准备触发 OOM 之前调用,作为最后的挽救手段:
__alloc_pages_slowpath() └── if (gfp_mask & __GFP_DIRECT_RECLAIM) └── unreserve_highatomic_pageblock(ac, false) // 先尝试非强制 └── if (仍然失败) unreserve_highatomic_pageblock(ac, true) // 强制解除 └── retry reclaim once more
总结
unreserve_highatomic_pageblock()是内存分配器的紧急储备释放机制:它在分配即将失败时,遍历 zonelist 中的 zone,将MIGRATE_HIGHATOMIC保留的 pageblock 转为请求类型并移走空闲页,以换取分配成功、避免 OOM。它通过force参数控制是否保留最后一个 pageblock,通过is_migrate_highatomic_page()处理竞态计数,并选择转换为ac->migratetype而非UNMOVABLE以最小化碎片化。