nbcon 专题Linux v7.3 · 6.12.113

05 · 物理视图:落到 ARM64 硬件上

结论:在 ARM64 上,nbcon 的正确性和效果取决于四处硬件或平台特性。① CPU 分区:pr/* 打印线程和 irq_work/N 落在哪颗核。② 原子指令与 NMI:arm64 选了 ARCH_HAVE_NMI_SAFE_CMPXCHG,nbcon_state 的 cmpxchg 在 pseudo-NMI 里也安全;但停核时只有开了 pseudo-NMI 才有 IPI_CPU_STOP_NMI 兜底。③ UART IP 与驱动:pl011(6.15)、i.MX(6.13)、Tegra264 UTC(6.15)、8250 系与 Qualcomm GENI(7.3)是 nbcon;Samsung、Meson、MSM、LPUART、SCI、Tegra TCU 到 7.3 仍是 legacy。④ 时钟框架:clk_enable() 在 RT 上可能睡眠,write_atomic() 不能碰,pl011 到 7.3 才修正。选内核版本时:6.12 LTS 在 ARM64 上没有任何串口是 nbcon;6.18 LTS 有 pl011 / i.MX / Tegra264;7.3 覆盖面最大。

物理视图 ①:CPU 分区、GIC / pseudo-NMI、UART IP 与驱动、时钟
物理视图 ①:CPU 分区、GIC / pseudo-NMI、UART IP 与驱动、时钟

① CPU 分区:打印线程该放哪

ps -eLo pid,psr,cls,rtprio,ni,comm | grep -E ' pr/|irq_work/'     # 打印线程与 irq_work 线程的位置与策略
taskset -pc 0-3 $(pgrep -x 'pr/ttyAMA0')                          # 需要时手动钉住
chrt -f -p 1 $(pgrep -x 'pr/ttyAMA0')                             # 想让日志更及时:提到低 RT 优先级(会与同核 RT 任务竞争)

② 原子指令、pseudo-NMI 与停核

项目 ARM64 现状 对 nbcon 的意义
NMI 安全的 cmpxchg select ARCH_HAVE_NMI_SAFE_CMPXCHG(6.12.113 arch/arm64/Kconfig:58,v7.3 :62);LSE 的 CAS 或 LL/SC nbcon_state_try_cmpxchg() 可以在 pseudo-NMI 里执行
PREEMPT_RT select ARCH_SUPPORTS_RT(6.12.113 :107,v7.3 :86),由 6.12 的 d8fccd9ca5f9 加入 提交说明把 printk 列为「最后一个已知障碍」
pseudo-NMI ARM64_PSEUDO_NMI(v7.3 :2409),默认关;命令行 irqchip.gicv3_pseudo_nmi=1 启用,依赖 GICv3 优先级屏蔽 NMI 回溯(IPI_CPU_BACKTRACE)与 IPI_CPU_STOP_NMI 只有启用后才是真 NMI(6.12.113 arch/arm64/kernel/smp.c:1016-1029)
停核流程 smp_send_stop()(6.12.113 smp.c:1133):先普通 IPI_CPU_STOP,等 1 s,仍有在线核才发 NMI 版本 关中断自旋的核(例如在 nbcon_reacquire_nobuf() 里等所有权)收不到普通 IPI

无 pseudo-NMI 时的坑:非 RT 内核里,打印线程的 device_lock() 是 spin_lock_irqsave(port->lock),会关中断。如果它被 panic CPU 抢走所有权后在 nbcon_reacquire_nobuf() 自旋,普通 IPI 就停不下它,后果是 kdump 内核起不来。6.17 的 571c1ea91a73 允许非 panic CPU 在 panic 期间用 direct 方式 reacquire,让它能退出自旋并响应停核;提交说明原话是 "platforms that do not support a true NMI"。RT 内核里 spin_lock_irqsave 不关硬中断,这个问题不那么尖锐,但修复对两者都适用。

③ UART IP、驱动与 nbcon 状态

物理视图 ②:ARM64 常见控制台驱动在 6.12 LTS / 6.18 LTS / 7.3 上的 nbcon 状态
物理视图 ②:ARM64 常见控制台驱动在 6.12 LTS / 6.18 LTS / 7.3 上的 nbcon 状态
IP / 驱动 典型 ARM64 平台 nbcon 起始 实现要点
ARM PL011 · amba-pl011.c · ttyAMA SBSA / ACPI 服务器(Ampere、A64FX 等)、QEMU virt、树莓派 6.15(2eb2608618ce) 线程逐字节检查所有权,atomic 整串持有;作者在 A64FX 上实测:串口竞争下 sysrq / NMI panic 时 legacy 丢部分或全部消息,nbcon 无丢失
8250 系 · 8250_core/port.c · ttyS Rockchip(DW-APB)、TI K3(OMAP)、MediaTek 等 7.3(d3539347022a;6.14 首次合入后回退) 逐字节交接;modem control 用 irq_work 延后(7.3-rc6 改 LAZY)
i.MX UART · imx.c · ttymxc i.MX8 / i.MX9 6.13(70acca67bdd3) 保留 CONFIG_SERIAL_IMX_LEGACY_CONSOLE 回退开关
Qualcomm GENI · qcom_geni_serial.c · ttyMSM 骁龙 / QCS 平台 7.3(74658868da2a) 线程按 32 字节分批提交;atomic 可以取消正在进行的 TX,再用 irq_work 恢复
Tegra264 UTC · tegra-utc.c Thor 6.15(eb07e3a94679,新驱动直接写成 nbcon) 多个固件客户端共享一个物理 UART,每个客户端有 128 字符 FIFO
Tegra TCU · tegra-tcu.c · ttyTCU Orin / Xavier(经 mailbox 复用调试口) legacy(到 7.3) RT 上靠 pr/legacy;panic 时只能走 legacy 路径
Samsung / Meson / MSM / LPUART / SCI Exynos、Amlogic、旧高通、NXP Layerscape、Renesas legacy(到 7.3) 同上

对 性能分析 14 中 Orin / Thor 配置的含义:Thor 的 UTC 从 6.15 起就是 nbcon。Orin 如果用 TCU 做控制台,到 7.3 仍是 legacy;如果改用 8250 兼容的 UART(ttyS*),7.3 起是 nbcon。用哪个口取决于 bootloader 和 DT 里的 console=。

④ 时钟与电源

⑤ ARM64 上的实测证据(来自主线提交)

现象 平台 修复
串口竞争下 panic 消息部分或全部丢失(legacy),nbcon 无丢失 A64FX 物理机 + ARM64 QEMU 6.15 pl011 转 nbcon(2eb2608618ce)
kexec 重启时 syscore 关机刷出,pl011_console_write_atomic() 长时间关中断,arm-smmu-v3 CMD_SYNC timeout、CSD 锁无响应 ARM64(栈中有 __arm64_sys_reboot、el0_svc) 6.19 原子刷出逐条恢复中断(9bd18e1262c0)
原子上下文 printk 时 clk_enable 在 RT 上睡眠 ARM32 / ARM64 DT SoC 7.3 时钟常开(c0e8cfef7546)
8250 nbcon 在 cpuidle 入口排 irq_work 导致挂死 Tegra20 / Tegra30(ARM32) 7.3-rc3 / rc6 改 LAZY(560f4deda327、37aba16e7943)

选型建议(ARM64 + PREEMPT_RT)

需求 建议
留在 6.12 LTS 接受 ARM64 串口都是 legacy:普通输出靠 pr/legacy,panic 输出不保证。6.12.113 已回合 LAZY irq_work、panic reacquire 等核心修复,但没有驱动转换
需要 panic 时的可靠串口输出 6.18 LTS(pl011 / i.MX / Tegra264),并确认 pl011 时钟修复已在所用 stable 里(CVE-2026-90338 记录称 6.18.52 已含)
8250 系或骁龙平台 7.3 起
Orin(TCU)/ Exynos / Amlogic 到 7.3 仍是 legacy:考虑换用已是 nbcon 的 UART 做控制台,或依赖 pstore / ramoops 保存 panic 现场
cat /proc/consoles          # 标志列:E=enabled C=consdev B=boot … N=nbcon(fs/proc/consoles.c:24)
cat /sys/class/tty/console/active
dmesg | grep -i 'printing thread\|legacy console\|console \['