find_suitable_fallback()是伙伴系统fallback(降级)路径上的核心决策函数。当请求的迁移类型(migratetype)在指定order的空闲链表耗尽时,它负责在其他迁移类型中寻找一个合适的页块来源。
核心作用与参数
它的职责是:给定一个free_area(某个阶数的空闲区域)、请求阶数、起始迁移类型以及一个claimable标志,返回一个可用的 fallback 迁移类型,或者表示失败/不应认领。
| 参数 | 含义 |
|---|---|
area | 某个 order 的free_area(zone->free_area[order]) |
order | 当前尝试的分配阶数 |
migratetype | 请求的迁移类型(起始类型) |
claimable | 是否只接受“值得认领整个 pageblock”的 fallback |
历史实现与返回值演进
在经典实现中(如 4.x 内核),它使用带内编码的返回值:成功返回fallback_mt(非负),无可用块返回-1,若claimable为真但“认领整个 block 不划算”则返回 。
一个典型的经典实现如下:
int find_suitable_fallback(struct free_area *area, unsigned int order, int migratetype, bool only_stealable, bool *can_steal) { int i; int fallback_mt; if (area->nr_free == 0) return -1; *can_steal = false; for (i = 0;; i++) { fallback_mt = fallbacks[migratetype][i]; if (fallback_mt == MIGRATE_TYPES) break; if (free_area_empty(area, fallback_mt)) continue; if (can_steal_fallback(order, migratetype)) *can_steal = true; if (!only_stealable) return fallback_mt; if (*can_steal) return fallback_mt; } return -1; }2026 年的重构:显式枚举替代魔法数字
2026 年的补丁系列对find_suitable_fallback()进行了接口清理,将原来的“负数带内编码”改为显式的枚举返回值,并移除了对migratetype参数的“过载”使用。
重构后的签名变为:
enum fallback_result find_suitable_fallback(struct free_area *area, unsigned int order, int migratetype, bool claimable, int *mt_out);
返回的枚举fallback_result清晰地区分了三种状态:
| 返回值 | 含义 |
|---|---|
FALLBACK_FOUND | 找到合适的 fallback 类型,*mt_out有效 |
FALLBACK_EMPTY | 该 order 下没有可用的 fallback 块 |
FALLBACK_NOCLAIM | 传入了claimable=true,但认领整个 block 不划算 |
这一变化的核心动机是消除魔法数字-2的歧义。在旧实现中,-2既表示“不应认领”,又需要调用方区分它与-1(无块)的行为差异:__rmqueue_claim()遇到-2时应停止向下扫描(因为更低的 order 更不值得认领),而__rmqueue_steal()根本不关心认领逻辑。
调用场景
find_suitable_fallback()被多个路径调用,它们对“认领”的需求不同:
__rmqueue_claim():从高 order 向下扫描,寻找值得认领整个 pageblock的 fallback。遇到FALLBACK_NOCLAIM时停止(更低的 order 更不值得认领)。__rmqueue_steal():只关心偷单个页块,不关心认领,因此传入claimable=false,且永远不会收到FALLBACK_NOCLAIM。__compact_finished()(内存规整):向上扫描 order,直到找到可认领的块。
总结
find_suitable_fallback()是 fallback 路径的核心查找函数:它按照fallbacks[][]定义的优先级顺序,在free_area的其他迁移类型中寻找空闲块。2026 年的重构将其从“返回负数编码状态”改为“返回显式枚举”,使FALLBACK_FOUND/FALLBACK_EMPTY/FALLBACK_NOCLAIM的语义一目了然,消除了调用方对魔法数字-2的隐式依赖,也让should_try_claim_block()的职责回归到它该在的位置——由真正关心“认领”的调用方自行判断。