在两张 Radeon 上溯源至 hidden_hostcall_buffer;经十二个排除的假设、四版 RCCL 发行构建与一行虚拟机配置对照验证
两张 Radeon 启用 --tensor-parallel-size 2 启动运行,随即遭遇了一句把所有人引向错误排查层次的报错:the operation cannot be performed in the present state。调查过程中逐一排除了十三个假设中的十二个。而对于虚拟机,最终的修复只需一行配置,根本无须重新编译库。
在缺失 PCIe AtomicOps 的机器上,所有 RCCL 集合通信都会在 dispatch 阶段报错 hipErrorIllegalState。故障根因位于 RCCL 下方第四层。自 ROCm 7.2 起,官方发布的设备端内核会声明一个 hostcall buffer。ROCr 仅在 PCIe 链路具备 AtomicOps 支持时才分配该缓冲区,否则便在任何集合通信执行前直接拒绝 dispatch。
整条因果链可精简为 五十七行 HIP 代码,完全无须依赖 RCCL、PyTorch 或 vLLM:两个内核共享完全相同的启动路径,区别仅在于第二个内核调用了设备端 printf。普通内核顺利执行,而请求 hostcall 的内核则失败,抛出的正是生产环境下的那句报错。
我们先后测试并排除了十二个竞争性解释。其中两个曾由本项目公开发布,随后才被推翻:其一是误认为 RCCL 出现了版本回退,其二是误认为 ROCm 运行时在客户机内拦截了内核提交。
本仓库最初验证了一种临时变通方案:在编译设备端代码时传入 NDEBUG 并重建 RCCL。更简单的修法是后来才找到的。在直通客户机中,这项缺失的能力通常是 一行虚拟机配置自身所致。QEMU 原本会自动通告 AtomicOp completer 支持,唯独在显卡以多功能设备直通时予以关闭,而这恰恰是 Proxmox 的默认配置。
两张 GPU、张量并行,引擎启动时:
rocvirtual.cpp:4151 ShaderName : ncclDevKernel_Generic_4(ncclDevKernelArgsStorage<4096ul>) rocvirtual.cpp:4208 Pcie atomics not enabled, hostcall not supported rocvirtual.cpp:4636 AQL dispatch failed! hip_module.cpp:605 hipModuleLaunchKernel: Returned hipErrorIllegalState
报错浮现在 RCCL 的 enqueue.cc 中,因此排查者通常会顺着调试 RCCL、继而调试 ROCm,最后再去翻操作系统内核。然而,直接道明真正诱因的输出恰好紧挨在失败行的上方。那一行对该通信库与集合通信只字未提,且只有把日志级别设为 4 时才会现身。
整条因果链由五个环节扣合而成。每一环所依据的证据,均独立于其下游环节。
docs/root-cause.md §1 提取,
所以它不可能与背后的文档漂移。
第 4 环是整条论证的关键支柱,因为它彻底将该故障与周遭的所有软件隔离开来。两个内核共享完全相同的启动路径,唯一的差别在于第二个内核调用了设备端 printf,因而需要一个 hostcall 缓冲区。
--- dev0 --- [plain ] launch:no error sync:no error [hostcall ] launch:no error sync:the operation cannot be performed in the present state
仅凭单张 GPU 上的五十七行 HIP 代码,便可在半秒内完整复现完全相同的故障。这一步将问题从「通信库版本缺陷」的叙事彻底转向了「平台硬件能力限制」:该故障与集合通信毫无干系。任何请求 hostcall 的内核都会触发此错误;而 RCCL 的设备端内核之所以需要 hostcall,纯粹是因为代码中包含了设备端 assert()。
社区给出的临时解决方案是安装 ROCm 7.1.1 构建的 librccl。此方法确实凑效,而且它恰恰为此处论证的根本原因提供了有力佐证,而非反证。只需统计官方发布的设备镜像中包含多少 hostcall 声明,原因便一目了然:
librccl 跑 llvm-readelf --notes,
统计 ncclDevKernel_Generic* 元数据里的 hidden_hostcall_buffer。
四行的平台完全相同。变的是库的 kernel 是否要求一个这台机器没有的能力。
这份 readelf 原始输出不在仓库里;这四行是从写作稿转录的。
由此可见,新版 RCCL 并不是普遍地坏了,它只是要求一项这批机器自始至终都不具备的平台能力。这一解释把虚拟化环境下的报告与裸机上的事故统一了起来:倘若那些裸机平台确实支持 AtomicOps 路由,hostcall 的先决条件本该得到满足,降级也就无从生效。降级库版本之所以奏效,恰恰直接证明了那些机器同样缺失 atomics 支持。
NDEBUG 传递至设备端编译阶段。起决定作用的控制变量始终是 hostcall,而非版本号本身。这是本项目此前公开发表、随后又自行推翻的两个结论中的第一个。
排查过程中我们先后调查了十三个假设,其中十二个被证实为错误推断。将它们悉数记录于此,既是为了避免后来者做无谓的重复劳动,也是因为其中两个错误论断正是我们自身早先做出的。
iommu=pt 只是一个字符串检查,会警告但不会拦截;两代 ROCm 用的都是普通的
DisableTiming 事件 —— 这样更便宜,结论一样确定。
由 docs/root-cause.md §3 提取。
diagnose/hipgate.cpp 中,原样调用附带完成事件、4 KB 内核参数(kernarg)、跨流等待及细粒度主机内存的 hipExtLaunchKernel 时,在 7.14 下能够完全顺利通过。内核提交运转一切正常,遭到拒绝的唯独是那些带有 hostcall 缓冲区的内核。
最初得到验证的解决方案是重新编译:在编译 RCCL 设备端代码时传入 NDEBUG,以消除引入 hostcall 的 assert() 语句。这一方案在 RCCL 2.27.7 上切实有效(端到端验证即基于该版本完成),并且一度被视作全部答案。然而在 2.30.4 上,该方法已无法奏效:即便构建出的镜像中已不再包含任何调用 hostcall 的指令,该元数据声明依然会残留下来(详见第 8 节)。
然而对于虚拟机而言,重新编译走错了第一步。自 v8.1.0 起,QEMU 便已支持通过 vfio_pci_enable_rp_atomics() 在模拟根端口上自动通告 AtomicOp completer 支持,仅在极少数受限条件下才会放弃。其中最关键的限制是:一旦设备以多功能(multifunction)模式直通,该特性便被关闭。Proxmox 默认的 hostpciX 配置正是将显卡与其配套音频功能捆绑直通,恰好触发了这一分支。
all_reduce 和
all_gather_into_tensor,逐元素对照构造出来的真值,三种 dtype 两种尺寸共十二个用例。
对于硬件本身确实无法提供 AtomicOps 的系统而言,重新编译 RCCL 依旧不可或缺:
在上述情形中,底层机理并无二致,通过 NDEBUG 重建即可妥善解决。真正改变的是排查诊断的顺序:在虚拟机内部,应当在着手重编软件之前,先行检查虚拟机监视器的配置。
dmesg | grep "PCIE atomic" —— 只要有输出即表明缺失 atomics;而无输出并不意味着支持完备,仅代表未明确报错,因而须进一步借助 lspci 进行核实。请务必精确 grep 该完整短语;若仅使用宽泛的 grep -i atomic,则会混入系统引导初期打印的无关信息 DMA: preallocated ... pool for atomic allocations。如果在客户机内受到此问题困扰,在编译任何软件之前,请先检查显卡上方根端口的 lspci -vv。
将显卡以单功能形式直通,会使其配套的音频功能被阻隔在客户机之外。能否将二者分别挂载到各自独立的模拟根端口上、从而让客户机在享用 atomics 的同时保留音频设备,此方案在本实验中未做验证。
上游未来是否仍保留对 hostcall 的依赖,在 ROCm#6520 中仍属悬而未决的问题。在 RCCL 2.30.4 上,即便 NDEBUG 清除了全部 __ockl_* 符号,该元数据声明依然顽固残留;因此在当前的新版本上,单纯剔除设备端的 assert() 已不足以解决问题。
本研究中的配置 A/B 对照仅基于单台宿主机、单一 QEMU 版本与单一主板。尽管 QEMU 的分支逻辑已在源码层面得到核实,但在其他主板上的具体表现,这台机器上并未做实测。
diagnose/hipgate3.cpp 实为 57 行(非空 54 行,除去注释 39 行),
没有任何一种数法得出三十。正文已改为五十七行,即 wc -l 的结果。
自行复现。用于判别的五十七行独立测试代码位于
diagnose/hipgate3.cpp,
完全无须依赖 RCCL 或 PyTorch。平台环境核对脚本见
diagnose/check-platform.sh。
完整的排查证据链与假设排除矩阵参见
docs/root-cause.md;
虚拟机部分的专门分析参见
docs/vfio-atomics.md。
本问题已作为 ROCm#6520 提交至上游, @adderek 在禁用 IOMMU 的裸机环境下独立完成了复现,@bkvargyas 则从反向补充了「中间交换芯片与 atomics 无法兼得」的制约机制。 相关问题:ROCm#6074、 vllm#38587。