【昇腾踩坑】双卡 TP=2 一跑就整机挂死?从 RCU Stall 一路查到 HCCL 链路选错 目录【昇腾踩坑】双卡 TP2 一跑就整机挂死从 RCU Stall 一路查到 HCCL 链路选错一、背景二、环境信息三、排查过程逐层排除两个关键手段强烈推荐探针第一次运行 → 卡死整机四、根因五、解决方案六、验证结果6.1 探针加变量后6.2 稳定性阶梯6 档累计 1.5 小时6.3 性能说明七、踩坑清单血泪总结八、总结【昇腾踩坑】双卡 TP2 一跑就整机挂死从 RCU Stall 一路查到 HCCL 链路选错平台Phytium S5000C 2×Atlas 300I A2 框架vllm-ascend 模型Qwen3.6-35B-A3BBF16必须 TP2关键词昇腾 / Atlas 300I A2 / vllm-ascend / TP2 / 张量并行 / HCCL / 整机卡死 / RCU Stall一、背景一台**飞腾Phytium S5000C平台 2 张 Atlas 300I A2单卡 64 GB**的服务器要部署一个 70 GB 的 BF16 大模型 —— 单卡放不下必须上双卡张量并行TP2。结果单卡独占长时间稳定跑过 34.6 小时、19.7 小时两卡各跑独立实例零跨卡通信稳定双卡 TP2❌必挂最快单次请求就整机失联挂机后 SSH 全断只能到机房重启。串口刷的是Sending IPI failed to CPU 74 rcu: rcu_sched kthread starved for 23534 jiffies! rcu: Unless rcu_sched kthread gets sufficient CPU time, OOM is now expected behavior. Kernel panic - not syncing: RCU Stall SMP: failed to stop secondary CPUs 0-18,20-127看着像硬件坏了 —— 但最后发现其实只是 3 个环境变量的事。二、环境信息项值加速卡Atlas 300I A2 ×2单卡 64 GB主板Phytium S5000COSopenEuler 22.03 SP1kernel 5.10aarch64驱动 / CANN驱动 25.5.1宿主 CANN 8.5.0容器 CANN 9.1.0推理框架vllm-ascendv0.23.0.post1-openeuler模型35B / A3B MoEBF16约 70 GB三、排查过程逐层排除我的思路很简单用最少的实验一层层把硬件坏了这个可能性剥掉。#假设实验结论1两卡固件版本不一致统一固件排除但必须修2跨 socket 通信路径差把两卡移到同一 CPU/GPU 区拓扑SYS→PHB改善未解决3PCIe 带宽不足lspci -vv查链路x8能力 x16——是约束不是根因4ACS 阻断了 P2P查ACSCtl已全放行 →排除5供电/散热撑不住两卡两卡各跑独立实例并行 → 稳定排除6跨卡通信单卡稳 双卡独立实例稳唯一差异只剩跨卡通信锁定方向7是硬件还是配置脱离模型/框架的 20 行 all_reduce 探针定位到 HCCL 配置两个关键手段强烈推荐① 对照实验把变量隔离干净组 A单卡单实例 → 稳 组 B双卡各一个独立实例 → 稳 零跨卡通信 组 C双卡 TP2 协同 → 挂 有跨卡通信A/B 都稳、C 挂 →差异只剩跨卡通信。而且组 B 稳还顺带排除了供电/散热/主板这一整类嫌疑。② 写一个脱离框架的最小探针不要一上来就调 vLLM 参数。先写一个只做all_reduce的 20 行脚本不加载任何模型# hccl_probe.pyimportosimporttorchimporttorch_npu# noqa: F401importtorch.distributedasdist rankint(os.environ[RANK])worldint(os.environ[WORLD_SIZE])dist.init_process_group(hccl,init_methodenv://,world_sizeworld,rankrank)torch.npu.set_device(fnpu:{rank})xtorch.ones(2*1024*1024,dtypetorch.float32).npu()# 8 MiBforiinrange(20):dist.all_reduce(x,opdist.ReduceOp.SUM)ifrank0:print(fiter{i}ok x[0]{x[0].item()},flushTrue)dist.barrier()print(f[rank{rank}] ALL 20 ALLREDUCE OK,flushTrue)运行注意加--network host与 vLLM 环境保持一致dockerrun--rm--privileged--networkhost--ipchost\--device/dev/davinci0--device/dev/davinci1\--device/dev/davinci_manager--device/dev/devmm_svm--device/dev/hisi_hdc\-v/usr/local/dcmi:/usr/local/dcmi\-v/usr/local/Ascend/driver/lib64:/usr/local/Ascend/driver/lib64\-v/usr/local/Ascend/driver/version.info:/usr/local/Ascend/driver/version.info\-v/usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool\-v/usr/local/bin/npu-smi:/usr/local/bin/npu-smi\-v/etc/ascend_install.info:/etc/ascend_install.info\-v/home/bench/tools/hccl_probe.py:/probe/hccl_probe.py\-eASCEND_RT_VISIBLE_DEVICES0,1\quay.io/ascend/vllm-ascend:v0.23.0.post1-openeuler\bash-lcsource /usr/local/Ascend/cann-9.1.0/set_env.sh 2/dev/null || true; torchrun --nproc_per_node2 --master_port29501 /probe/hccl_probe.py这一个实验的价值极高如果它挂了就直接证明了与模型无关、与 vLLM 无关把问题从业务层压到通信库层。探针第一次运行 → 卡死整机顺便说探针第一次跑机器直接挂了 —— 这本身也是重要信息。四、根因恢复后开着串口落盘 dmesg 落盘再跑一次这次拿到了明确报错HCCL function error: hcclCommInitRootInfoConfig(numRanks, rootInfo, rank, config, (comm-hcclComm_)), error code is 7 Communication_Error_Bind_IP_Port(EI0020): Failed to enable listening for the NPU network adapter socket. Reason: The IP address x.x.x.199 and port 16666 have already been bound. Solution: Check whether the single-card multi-process scenario is used. If yes, configure the port number using the environment variable HCCL_NPU_SOCKET_PORT_RANGE.HCCL 建链bootstrap时默认走 NPU 网卡的 RoCE 路径而这块主板没有卡间高速互联无 HCCS那条路走不通 → 建链阶段死等 → 拖垮 CPU → 整机失联。用一句话概括HCCL 默认去试了网卡这条路走进了死胡同PCIe 才是这块板上唯一可用的卡间通道。五、解决方案在容器环境变量里加三项 ——不改代码、不改 BIOS、不用换硬件environment:-ASCEND_RT_VISIBLE_DEVICES0,1# ★ 本平台必需缺任一行TP2 会在 HCCL 建链阶段挂死整机 -HCCL_INTRA_PCIE_ENABLE1# 允许节点内走 PCIe本平台唯一可用的卡间通道-HCCL_INTRA_ROCE_ENABLE0# 禁止节点内走 RoCE该路径不可用-HCCL_CONNECT_TIMEOUT600# 放宽建链超时无 HCCS 时建链较慢变量作用HCCL_INTRA_PCIE_ENABLE1显式允许节点内用 PCIe 做集合通信HCCL_INTRA_ROCE_ENABLE0禁止节点内走 RoCE这条最可能是关键HCCL_CONNECT_TIMEOUT600把建链超时放宽无 HCCS 时建链慢默认超时可能不够六、验证结果6.1 探针加变量后iter 0 ok x[0]2.0 iter 1 ok x[0]4.0 ... iter 19 ok x[0]1048576.0 ← 2^20两卡数据确实参与了每次归约数值正确 [rank 0] ALL 20 ALLREDUCE OK [rank 1] ALL 20 ALLREDUCE OK6.2 稳定性阶梯6 档累计 1.5 小时档负载TTFT p50TPOT p50输出 tok/s总 tok/sfailedb1512/128 并发 11.1 s34.8 ms21.91110b2512/128 并发 3238.0 s41.0 ms92.04670b3512/128 并发 6448.3 s120.2 ms99.15040b48192/512 并发 3227.8 s193.0 ms107.618310b5129024/512 并发 4419 s156.0 ms4.110380b6512/128 ×2000 并发 6452.2 s240.6 ms93.14730结论6 档全过失败请求 0内核故障计数全程 0IPI failed/rcu_sched/phytium pcie error/AER一次都没出现b6 以 2000 请求 并发 64 连续跑 46 分钟未挂——首次超过历史最快挂机时间约 45 分钟短请求稳态延迟1.5 秒修复前 6.7~7.3 s/it快了 4 倍多6.3 性能说明满并发下 TTFT 偏高p50 ~48 s、126K 单次约 7 分钟 —— 因为两卡无 HCCS、PCIe 仅 x8实际链路宽度只有能力的一半跨卡通信只能经主机内存中转。这是平台物理条件决定的不是缺陷短请求体验正常。七、踩坑清单血泪总结不要用watch -n 1 npu-smi info高频轮询会压垮驱动/监控通道是整机失联的常见诱因。要用请 ≥60s 或脚本采样。硬 panicnot syncing会把日志一起丢掉—— 必须做证据四件套# ① RCU 卡死自动重启避免只能去机房断电sysctl-wkernel.panic_on_rcu_stall1kernel.panic30# ② journald 持久化否则重启后查不到上次启动的日志mkdir-p/var/log/journalsed-is/^#\?Storage.*/Storagepersistent//etc/systemd/journald.conf systemctl restart systemd-journald# ③ 串口 Log Session 落盘 ④ dmesg -wT 持续落盘单写者dmesg采集必须单写者—— 同一分钟起两条会撞同名文件、互相覆盖最后只剩一条。/tmp重启会清空—— 脚本一律放持久目录如/home/bench/。“敲了命令” ≠ “在跑”—— 采集类进程要验证pgrep有 pid 输出文件 mtime 在涨。nohup setsid xxx 后 shell 报[1] 已完成是正常的setsid 立刻 fork 退出—— 验证永远看pgrep。hccl_test在新版 CANN 里是源码包只有.cc/.h Makefile不是现成可执行文件 —— 不如直接用torch_npu写个all_reduce探针快。HCCL 崩溃后可能残留 socket 占端口→ 可用HCCL_NPU_SOCKET_PORT_RANGE60000-60015换端口范围。双实例并存时docker compose项目名必须不同默认取目录名—— 否则down会互删对方容器。显式写name:或用-p。排查顺序建议先做对照实验隔离变量再写最小复现不要一上来就调框架参数。八、总结一个看起来像硬件故障的整机挂死通过对照实验 最小复现最终定位成可配置绕过的通信库适配问题不用换硬件加三个环境变量解决沉淀出一套可复用的平台问题排查方法对照实验隔离变量 → 脱离框架的最小探针 → 证据保全四件套。如果这篇帮你省了几个小时欢迎点赞收藏 有遇到类似问题的评论区交流。