彻底搞懂 KHO:Linux 跨内核热重载的落地阵痛与重构之路

在 Linux 内核演进中,KHO (Kexec HandOver) 是近来备受瞩目的技术方案。它的核心目标是:在利用 kexec 实现内核热重载(Live Update / Quick Reboot)时,将旧内核中的关键数据结构、内存状态甚至 DMA 分配跨版本“无缝移交”给新内核,从而大幅缩短高可用场景下的业务中断时间。

然而,从理论设计到落地内核主线,KHO 遇到了诸多极为棘手的工程痛点。本文整理了近期 KHO 开发者在 Linux 内核邮件列表(LKML)上的多轮讨论与重构补丁,带你一探这项黑科技背后的阵痛与演进。

痛点一:语义模糊与内存分类混乱 (Memblock & NOPRSRV)

在早期实现中,系统启动阶段的内存管理器 memblock 在处理 KHO 暂存内存(Scratch Memory)时存在严重混淆。

旧逻辑:[从 KHO 拿到的暂存内存]  == 标记为 MEMBLOCK_KHO_SCRATCH == [启动过程中新发现的暂存内存]
                                (初始化逻辑混淆、语义模糊)

新逻辑:[从 KHO 拿到的暂存内存]  ----------> 区分管理
       [启动过程中新发现的暂存内存] ----------> 标记为 NOPRSRV (Non-Preserved)

乱象剖析

  • 混淆来源memblock 会将两类完全不同的内存统一打上 MEMBLOCK_KHO_SCRATCH 标签:

     
    1. 上个内核通过 KHO 传递过来的“既有暂存内存”;

    2. 新内核在本次 Boot 过程中自己识别并划出的“新暂存内存”。

  • 严重后果:这两类内存在后期的初始化逻辑中必须被差异化对待。标签混用直接导致代码可读性极差(难以被开发者 grok),且极易埋下初始化逻辑错乱的隐患。

解决方案与重构

开发者决定将相关标志重命名为 NOPRSRV (Non-Preserved)。通过明确表示“非保留/可释放”的物理特性,消除了歧义,使 memblock 能精确掌握各类内存的属性并施加正确的初始化行为。

痛点二:命令行参数重命名与社区“无破坏原则”的博弈

为了进一步清除技术债,开发者尝试将 kho_scratch= 这一命令行参数重命名。但这触碰了 Linux 内核最敏感的红线之一——不可破坏既有接口(Don't break userspace / ABI)

争议与反驳

  • 维护者的质疑:随意重命名启动参数可能导致现有生产环境的引导脚本失效。

  • 开发者的破局逻辑

     
    1. 引用历史先例 (Prior Art):列举了内核历史上的多个提交(如 c5bfece2d612nohz_extended 改名为 nohz_full9406415f46f6sched_debug 改名为 sched_verbose),证明社区一直允许将“晦涩模糊”的参数重命名为更准确的名称。

    2. 评估真实影响kho=on 本身已经能极其出色地自动计算并分配尺寸,99% 的用户根本不需要手动敲入 kho_scratch=。手动设置仅适用于极少数高级调试场景。

    3. 澄清技术本质:KHO 本质上是 内核到内核(Kernel-to-Kernel) 的内部数据传递机制,没有任何用户态 API (uAPI)。修改它绝不会破坏用户空间程序的运行。

折中方案:虽然合理性充足,但开发者依然承诺:若未来确实有真实用户反馈影响,将第一时间补齐对旧参数的向下兼容支撑。

痛点三:kdump 内存惨遭“挤爆”引发内核 Panic

在 KHO 与崩溃转储内核(kdump / crashkernel)叠加使用的场景中,曾触发过一个致命的 OOM 崩溃 Bug。

Bug 发生的连锁反应

   kdump 内核启动 (运行在极小的 crashkernel 预留空间,如 1GB 内)
                          │
                          ▼
    未接收到 handover FDT (因为此前提交已禁止向 crash kimage 添加元数据)
                          │
                          ▼
    触发默认降级逻辑:调用 kho_reserve_scratch() 强制预留 200% 内存
                          │
                          ▼
  内存耗尽:176MB 基础占用 + 650MB 强制暂存预留 = crashkernel 空间直接瘫痪!
                          │
                          ▼
   Panic 报错:bio: can't create integrity buf pool (崩溃转储彻底失败)

根本原因与修复

kdump 内核是一个“临终工具”,它的使命只有一条:把死掉的旧内核内存镜像转储到磁盘,然后重启系统。它既不需要继承任何 KHO 状态,更不需要将自身状态 Handover 给下一个内核。

修复措施:直接在 kdump 内核中彻底禁用 KHO 机制。无需预留任何暂存内存,成功释放了数百兆极其宝贵的 crashkernel 内存空间。

痛点四:伙伴分配器高阶页(High-order Pages)恢复引发内存泄漏

在推进“跨 KHO 保持 DMA 内存分配”时,开发者遇到了 KHO 恢复逻辑与伙伴分配器(Buddy Allocator)物理现实之间的底层冲突。

经典物理现实的错位

概念实际物理/内核状态 (如 dma_alloc_coherent)旧版 KHO Restore 逻辑的行为
内存结构高阶非复合页 (High-order Non-compound Page)误判为一串独立的 4KB 页 (Split Pages)
引用计数 (Refcount)

Head Page = 1

 

Tail Pages = 0

Head Page = 1

 

Tail Pages 强制被赋予 1

导致的严重后果

当新内核后续试图释放这块 DMA 内存时,__free_pages_prepare() 会调用检查函数。系统在发现原本应该为 0 的尾页引用计数竟然是 1 时,会立刻判定内存状态损坏

  • 内核被污染 (Kernel Tainted):触发 VM Debug 警告。

  • 内存永久泄漏 (Memory Leak):由于不敢回收状态异常的页面,伙伴分配器直接放弃回收这块物理内存。

重构方案:重构显式 API

放弃了不可靠的“自动类型检测”,重构并推出了明确分工的持久化 / 恢复 API:

/* 旧 API:仅用于独立、分散的 4KB 页面集合 */
kho_preserve_pages(...) / kho_restore_pages(...)

/* 新 API:专为高阶页/DMA 内存设计,精准维持 Head=1, Tail=0 的物理状态 */
kho_preserve_page(...)  / kho_restore_page(...)

底层同时引入了 kho_init_high_order_page() 辅助函数,将元数据校验、状态清理与 managed page 统计统一收拢,完美解决了跨内核重载后 DMA 高阶连续内存恢复导致内核污染的问题。

总结

KHO(Kexec HandOver)作为 Linux 内核实现无感升级的关键技术,其演进过程完美展示了 Linux 社区对底层代码质量、内存物理真实性以及系统可靠性的苛刻要求。从消除语义模糊、理顺参数变更,到修复 kdump 内存挤爆与高阶页 refcount 错乱,这一系列重构正在为 KHO 迈向大规模生产环境铺平道路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kernel_RDMA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值