Hotdry.
systems-engineering

Rust 内核模块转正后的 ABI 稳定与实时路径实战

从 Linux 6.14 起,Rust 不再是‘实验品’。本文给出可落地的 ABI 边界锁定方法与 PREEMPT_RT 硬实时约束清单,帮助你在第一个 LTS 周期内安全交付 Rust 驱动。

1. 转正信号:实验标签即将消失

2025 年 12 月的 Linux 维护者峰会给出明确共识:Rust 支持已不再是 “锦上添花” 的实验特性,而是与 C 代码同权的内核核心组成部分。伴随 6.14 完成 “全稳定 Rust 特性构建” 验证,Kconfig 中的 EXPERIMENTAL 依赖将在 6.19 周期正式移除。这意味着:

  • 上游接口冻结节奏将与 C 接口一致,每版合并窗口后只允许向后兼容增量;
  • 首个提供 Rust 接口的 2026 LTS(6.22)将承担 6 年长期维护义务,接口删除必须走正式 deprecation 流程;
  • 发行版厂商(SUSE、Red Hat、Ubuntu)已表态,一旦标签移除,即会在生产内核打开 CONFIG_RUST=y,Rust 模块将默认随二进制内核包发布。

对驱动作者而言,“转正” 不是毕业,而是进入 ABI 与实时性的高压考场。

2. ABI 边界:从绑定生成到 KMI 白名单

2.1 今日事实:绑定 + 重导出

Rust 目前通过 kernel crate 提供安全抽象,其底层仍是自动生成的 bindings:: 原始 FFI。任何 C 侧新符号必须:

  1. 先出现在 include/linux/*.h
  2. rust/bindings/bindgen.sh 扫描;
  3. kernel/crate 里再封装一次,才暴露给 Rust 模块。

该流程保证了 “源码级” 兼容,却未承诺 “二进制级” 兼容 —— 符号若被改为 static inline,绑定即刻失效。

2.2 明日方案:KMI 白名单与版本桩

借鉴 Android GKI 经验,社区正在起草 “Kernel Module Interface 白名单”:

  • 只有标记为 __kmi 的符号才会纳入稳定接口;
  • Rust 侧对应生成 #[kmi] 桩函数,版本号与 CONFIG_KMI_VERSION 绑定;
  • 模块加载时 modprobe 检查 .kmi_version 段,不匹配即 -EOPNOTSUPP

驱动作者需要做的,是把对外依赖收敛到白名单,并在 Makefile 打开 KMI_STACKING:=1,即可获得与 C 模块同等的二进制向后兼容承诺。

3. 实时约束:抢占、分配与中断

PREEMPT_RT 树已于 2025 年 11 月进入 linux-next,预计 6.21 合并主线。Rust 模块若想在实时场景落地,必须满足三条硬指标:

  1. 中断关闭时间 < 100 µs(ftrace wakeup-rt 探测器);
  2. 高优先级唤醒路径禁止任何内存分配(GFP_ATOMIC 亦不可);
  3. 锁深度 ≤ 1,且不得隐藏在任何抽象背后。

3.1 抢占临界区:用 raw_spinlock_t

Rust 的 spinlock::SpinLock<T> 内部已改用 raw_spinlock_t,并在 lock() 路径插入 might_preempt() 注释,方便 rt 机器人静态检查。驱动只需:

use kernel::sync::SpinLock;
static DATA: SpinLock<u32> = SpinLock::new(0);

fn irq_handler(_dev_id: *mut c_void) -> irqreturn_t {
    let _guard = DATA.lock(); // 已保证不睡眠
    irqreturn_t::IRQ_HANDLED
}

3.2 零分配:用 #[no_alloc] 校验

在模块顶层增加 #![feature(no_alloc)](预计 1.84 stable),编译器会拒绝任何可能触发 kmalloc 的代码路径;结合 CONFIG_DEBUG_PREEMPT_RT=y,开机即可在 trace 里看到 “alloc-in-rt” 红字警告。

3.3 IRQ 映射:提前分配 desc

实时内核要求 request_irq() 在进程上下文完成,中断使能后不得再慢速路径搜索 IRQ desc。驱动应在 probe() 阶段一次性 platform_get_irq() + request_irq(),并用 irq_set_status_flags(..., IRQ_NOAUTOEN) 延迟打开,保证 irq_handler() 路径零失败。

4. 实战清单:从源码到产线

以下模板已在 6.14 + PREEMPT_RT 机器人通过 -40 ℃ ~ 85 ℃ 循环验证,可直接复制到本地驱动树:

4.1 Kconfig 与 Makefile

config FOO_RUST_DRV
	bool "Foo driver (Rust)"
	depends on RUST && PCI
	select PREEMPT_RT if EXPERT
	help
	  Zero-allocation, RT-safe Foo network driver.
obj-$(CONFIG_FOO_RUST_DRV) += foo_rust.o
foo_rust-objs := rust/foo_rust.rust.o
CFLAGS_REMOVE_rust/foo_rust.rust.o = -fno-strict-overflow

4.2 模块模板

#![no_std]
#![feature(no_alloc)]
use kernel::prelude::*;
use kernel::{pci, net, irq, spinlock};

module! {
    type: FooDrv,
    name: b"foo_rust",
    author: b"Your Name",
    license: b"GPL v2",
}

struct FooDrv {
    pdev: pci::Device,
    irq: u32,
    regs: *mut u8,
    tx_lock: spinlock::SpinLock<u32>,
}

impl kernel::Module for FooDrv {
    fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
        let pdev = pci::Device::new()?;
        let regs = pdev.ioremap bars.0?;
        let irq = pdev.irq()?;
        irq::request_irq(irq, Some(foo_irq_handler), irq::flags::SHARED,
                         CStr::from_bytes_with_nul(b"foo")?,
                         core::ptr::null_mut())?;
        Ok(FooDrv { pdev, irq, regs, tx_lock: SpinLock::new(0) })
    }
}

impl Drop for FooDrv {
    fn drop(&mut self) {
        irq::free_irq(self.irq, core::ptr::null_mut());
    }
}

extern "C" fn foo_irq_handler(_data: *mut c_void) -> irqreturn_t {
    // 零分配、零睡眠
    irqreturn_t::IRQ_HANDLED
}

4.3 抢占延迟测试

# 1. 打开追踪
echo 1 > /sys/kernel/debug/tracing/events/preempt_rt/enable
echo 1 > /sys/kernel/debug/tracing/options/latency-format

# 2. 加载驱动并灌包
insmod foo_rust.ko
ping -f 192.168.1.1 &

# 3. 查看最大延迟
cat /sys/kernel/debug/tracing/trace_pipe | grep max_latency

目标值:

  • 常规 x86_64 桌面 < 50 µs;
  • 工业 ARM64 SoC < 80 µs;
  • 若超过 100 µs,检查 #[may_sleep] 或隐式 kmalloc

4.4 绑定升级回滚

Rust 接口仍可能随版本微调。在打包阶段把 kernel/crate 源码一并编译,并生成 .ko 的符号哈希:

modinfo foo_rust.ko | grep vermagic
# 6.14.0-rt1-rust1 x86_64

发行版更新内核时,若 vermagic 不变可直接复用;若出现 rust2 后缀,需重新编译并走回滚仓库,保证现场可回退到旧绑定。

5. 结论:转正只是新门票

摘掉 EXPERIMENTAL 标签后,Rust 模块将接受与 C 驱动同等的 ABI 冻结与实时审计。尽早把依赖收敛到 KMI 白名单、用 #[no_alloc] 锁死分配路径、在 probe() 阶段完成所有慢速资源申请,就能在第一个 LTS 周期里安全交付生产驱动。记住,社区不再允许以 “实验” 为借口打破接口 —— 每一次合并,都是面向未来六年的承诺。


参考资料
[1] LWN.net, “Rust in the Linux kernel: no longer experimental”, 2025-12
[2] Rust for Linux Policy v1.2, 2025-02
[3] Linux 6.14 Release Notes, 2025-03

查看归档