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 分区:打印线程该放哪
pr/<name><idx> 与 pr/legacy 都是 SCHED_OTHER nice -20,没有 CPU 亲和约束。它们由 kthreadd 派生,亲和被设为 housekeeping_cpumask(HK_TYPE_KTHREAD)(6.12.113 kernel/kthread.c:371)。所以配置了 nohz_full=(或 isolcpus=nohz,…)时,默认只在 housekeeping 核上运行;只配 isolcpus=domain 不会限制它们(见参数深解 01 )。
隔离核上的 printk 仍有本核成本 :在 RT 上,irq_work 进入本核 lazy_list,由本核 irq_work/N(SCHED_FIFO 低优先级)执行唤醒。tick 已停时,LAZY 也会立刻自发 IPI(kernel/irq_work.c __irq_work_queue_local)。所以 RT 热路径里应该用 trace 事件代替 printk。
WARN / oops 发生在哪颗核,原子输出就在哪颗核关中断进行,包括隔离核 。这是 nbcon 有意的选择:紧急信息优先于延迟。
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 状态
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=。
④ 时钟与电源
pl011 在 6.15–7.2 的 write_atomic() / write_thread() 每次都 clk_enable() / clk_disable()(v6.15 amba-pl011.c:2527, 2559)。common clk 的 enable_lock 先 spin_trylock_irqsave(),竞争时退回 spin_lock_irqsave(),在 RT 上是可睡眠锁 ,于是原子上下文 printk 会触发 "BUG: sleeping function called from invalid context"。
7.3 的 c0e8cfef7546 改为:pl011_console_setup() 里 clk_prepare_enable()(v7.3 :2546),pl011_console_exit() 里释放(:2575);suspend 时只有端口真正挂起才放掉时钟引用。提交说明特别指出:ARM64 SBSA / ACPI 平台没有 clk,clk_enable(NULL) 直接返回,不受影响 ;受影响的是 DT 平台。代价是 UART 时钟(以及可能的父时钟)在控制台可用期间不能门控。
8250 在 suspend / resume 期间不使用 irq_work,因为某些硬件会因此卡住挂起(d3539347022a 说明)。
⑤ 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 \['