09 · E4 驱动转换(6.13 → 6.18):ARM64 串口第一次真正用上 nbcon
结论:6.12 的框架要有驱动才有收益。6.13 → 6.18 是驱动转换期:i.MX(6.13) 第一个转换;8250(6.14) 合入约两周后被回退;drm_log(6.14) 是只有线程路径的图形日志控制台;pl011(6.15) 对 ARM64 最关键,覆盖 SBSA 服务器、QEMU virt、树莓派;同期 Tegra264 UTC(6.15) 作为新驱动直接写成 nbcon。核心侧有两处修补:6.17 让第一个 nbcon 控制台注册时也立即用线程(否则慢串口会同步重放整个日志、卡住启动),并允许 panic 期间非 panic CPU 用 direct 方式 reacquire(否则没有真 NMI 的平台停不下这些核)。pl011 的作者在 A64FX 上实测:串口竞争下 sysrq / NMI 触发 panic,legacy 控制台丢失部分或全部崩溃信息,nbcon 无丢失。
要解决的问题
| # | 问题 | 出处 |
|---|---|---|
| P1 | 6.12 时 ARM64 上没有 nbcon 驱动:RT 下所有串口输出挤在 pr/legacy,panic 仍走 legacy 的 trylock |
6.12.113 serial_core.h:632 __uart_port_using_nbcon() 对所有驱动为假 |
| P2 | legacy 控制台在竞争下 panic 时丢消息 | 2eb2608618ce:"the legacy console resulted in the loss of some or all crash messages. However, using the NBCON console, no message loss occurred." |
| P3 | 第一个 nbcon 控制台注册时同步重放整个日志 | cf5543870186:"the entire log is replayed synchronously … It might block the boot for a long time with a slow serial console." |
| P4 | panic 中被打断的打印者永远拿不回所有权,在 reacquire 里死循环;没有真 NMI 的平台停不下它 | 571c1ea91a73:"platforms that do not support a true NMI to interrupt the quiesced CPU will not be able to shutdown that CPU from within panic(). This then causes problems for such as being unable to load and run a kdump kernel." |
方案与提交
| 版本 | 提交 | 内容 |
|---|---|---|
| 6.13 | 70acca67bdd3 serial: imx: Switch to nbcon console |
实现 write_atomic / write_thread / device_lock(说明里仍写作 driver_enter / driver_exit),保留 CONFIG_SERIAL_IMX_LEGACY_CONSOLE 回退 |
| 6.14 | b63e6f60eab4 → f79b163c4231 |
8250 转 nbcon(2025-01-07),约两周后(01-22)因 kernel test robot 报告被回退 |
| 6.14 | f7b42442c4ac drm/log |
新的启动日志绘制客户端,只有 write_thread |
| 6.15 | 2eb2608618ce serial: amba-pl011: Implement nbcon console |
Toshiyuki Sato(富士通),参照 8250 与 imx;在 ARM64 QEMU 与 A64FX 上验证 |
| 6.15 | eb07e3a94679 serial: tegra-utc |
Tegra264 UART Trace Controller,新驱动直接用 nbcon |
| 6.16 | 66f5f70ce07a serial: sifive |
RISC-V,略 |
| 6.17 | cf5543870186 |
首个 nbcon 控制台注册时也立即建线程,并置 printk_kthreads_running |
| 6.17 | 571c1ea91a73 |
nbcon_context_try_acquire(ctxt, is_reacquire):panic 中只放行 reacquire |
| 6.18 | d0d9c7235548 等 |
panic_on_this_cpu() / panic_on_other_cpu() 帮助函数,nbcon 跟进 |
架构图:之前 / 之后
调用栈
之前 · 6.12.113 上的 pl011(RT):
── 普通 printk ──
vprintk_emit() → ft.legacy_offload → defer_console_output() printk.c:2403
pr/legacy: legacy_kthread_func() printk.c:3619
console_lock(); __console_flush_and_unlock() → console_flush_all()
console_emit_next_record() → con->write() = pl011_console_write() amba-pl011.c:2311
clk_enable(uap->clk)
uart_port_lock_irqsave(&uap->port, &flags) (RT:rt_spin_lock,可睡眠)
__uart_port_nbcon_acquire() → __uart_port_using_nbcon() = false serial_core.h:632
uart_console_write() …… 一次刷完所有积压
── panic ──
console_flush_on_panic() → console_flush_all() → pl011_console_write()
oops_in_progress → uart_port_trylock_irqsave(),失败也照写 amba-pl011.c:2320-2321
之后 · 6.15 的 pl011(行号为 v6.15):
── 普通 printk(打印线程)──
nbcon_kthread_func() nbcon.c:1167
nbcon_emit_one(&wctxt, false) :1087
pl011_console_device_lock() → __uart_port_lock_irqsave() amba-pl011.c:2595
nbcon_context_try_acquire(NORMAL) nbcon.c:571
nbcon_emit_next_record(wctxt, false) :940
pl011_console_write_thread() amba-pl011.c:2551
clk_enable() :2559 ← 7.3 移除(见 E5)
逐字节:nbcon_enter_unsafe → putchar → nbcon_exit_unsafe
被抢:while (!nbcon_enter_unsafe()) nbcon_reacquire_nobuf()
── emergency / panic(调用者上下文)──
nbcon_atomic_flush_pending() nbcon.c:1607
__nbcon_atomic_flush_pending_con() :1477
nbcon_context_try_acquire(EMERGENCY|PANIC)
nbcon_emit_next_record(wctxt, true)
pl011_console_write_atomic() amba-pl011.c:2519
clk_enable() :2527 ← RT 上的隐患(见 E5)
nbcon_enter_unsafe → 整串 → 等 TX 空 → nbcon_exit_unsafe
── tty 写串口 ──
uart_port_lock_irqsave() serial_core.h:713
__uart_port_nbcon_acquire() :670
__uart_port_using_nbcon() = true(CON_NBCON 且有 write_atomic) :636
while (!nbcon_device_try_acquire(up->cons)) cpu_relax() nbcon.c:1755
uart_port_unlock_irqrestore() → nbcon_device_release() nbcon.c:1779
6.17 的 reacquire 修补(v7.3 行号):
nbcon_reacquire_nobuf(wctxt) nbcon.c:938
while (!nbcon_context_try_acquire(ctxt, true)) // is_reacquire = true
cpu_relax();
nbcon_context_try_acquire_direct(ctxt, cur, is_reacquire) :243
if (panic_on_other_cpu() && (!is_reacquire || cur->unsafe_takeover))
return -EPERM; // 只放行「未被 unsafe 强夺」的 reacquire
8250 第一次转换为什么失败
b63e6f60eab4 的设计(write_atomic / write_thread / device_lock、逐字节交接、irq_work 延后 modem control)与 7.3 最终合入的 take 2 基本一致。回退说明只有一句:"kernel test robot has found problems with this commit so revert it for now."。7.3 take 2 的系列说明称,这些问题最终查明是 8250 驱动自身的问题,修好之后才重新提交。take 2 前,主线先合入了 8250 的控制台流控(5e6dfb87b191、39d307074f91,7.2)与 suspend 时忽略流控(302fbbb4fcbd,7.2-rc3)等前置修复。
4+1 增量
| 视图 | E4 带来的变化 |
|---|---|
| 场景 | ARM64 上 S2 / S3 / S5 第一次真正生效(pl011、i.MX、Tegra264);S6 启动不再同步重放 |
| 逻辑 | is_reacquire:panic 中的获取规则变细 |
| 进程 | 每个 nbcon 串口有了自己的 pr/ttyAMA0 / pr/ttymxc0,不再挤在 pr/legacy |
| 开发 | 驱动目录出现 CON_NBCON;驱动转换的样板(8250 / imx / pl011)确立 |
| 物理 | A64FX 实测验证;无真 NMI 平台的停核问题修复 |
留下的问题:原子刷出一次持有到底,引起其他 CPU 的 hard lockup;emergency 反复抢线程,把输出撕碎;只有线程能打印的驱动在 panic 时完全没有输出;pl011 的 clk_enable() 在 RT 原子上下文里有睡眠隐患。见 E5。