☰
【昇腾踩坑】双卡 TP=2 一跑就整机挂死?从 RCU Stall 一路查到 HCCL 链路选错
2026/11/6 3:46:07 网站建设 项目流程

目录

  • 【昇腾踩坑】双卡 TP=2 一跑就整机挂死?从 RCU Stall 一路查到 HCCL 链路选错
    • 一、背景
    • 二、环境信息
    • 三、排查过程(逐层排除)
      • 两个关键手段(强烈推荐)
      • 探针第一次运行 → 卡死整机
    • 四、根因
    • 五、解决方案
    • 六、验证结果
      • 6.1 探针(加变量后)
      • 6.2 稳定性阶梯(6 档,累计 1.5 小时)
      • 6.3 性能说明
    • 七、踩坑清单(血泪总结)
    • 八、总结

【昇腾踩坑】双卡 TP=2 一跑就整机挂死?从 RCU Stall 一路查到 HCCL 链路选错

平台:Phytium S5000C + 2×Atlas 300I A2 | 框架:vllm-ascend | 模型:Qwen3.6-35B-A3B(BF16,必须 TP=2)
关键词:昇腾 / Atlas 300I A2 / vllm-ascend / TP=2 / 张量并行 / HCCL / 整机卡死 / RCU Stall

一、背景

一台**飞腾(Phytium S5000C)平台 + 2 张 Atlas 300I A2(单卡 64 GB)**的服务器,要部署一个 70 GB 的 BF16 大模型 —— 单卡放不下,必须上双卡张量并行(TP=2)。

结果:

  • 单卡独占:长时间稳定(跑过 34.6 小时、19.7 小时)
  • 两卡各跑独立实例(零跨卡通信):稳定
  • 双卡 TP=2:❌必挂,最快单次请求就整机失联

挂机后 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 S5000C
OSopenEuler 22.03 SP1(kernel 5.10,aarch64)
驱动 / CANN驱动 25.5.1;宿主 CANN 8.5.0;容器 CANN 9.1.0
推理框架vllm-ascendv0.23.0.post1-openeuler
模型35B / A3B MoE,BF16,约 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:双卡 TP=2 协同 → 挂 (有跨卡通信)

A/B 都稳、C 挂 →差异只剩"跨卡通信"。而且组 B 稳,还顺带排除了供电/散热/主板这一整类嫌疑。

② 写一个脱离框架的最小探针

不要一上来就调 vLLM 参数。先写一个只做all_reduce的 20 行脚本,不加载任何模型:

# hccl_probe.pyimportosimporttorchimporttorch_npu# noqa: F401importtorch.distributedasdist rank=int(os.environ["RANK"])world=int(os.environ["WORLD_SIZE"])dist.init_process_group("hccl",init_method="env://",world_size=world,rank=rank)torch.npu.set_device(f"npu:{rank}")x=torch.ones(2*1024*1024,dtype=torch.float32).npu()# 8 MiBforiinrange(20):dist.all_reduce(x,op=dist.ReduceOp.SUM)ifrank==0:print(f"iter{i}ok x[0]={x[0].item()}",flush=True)dist.barrier()print(f"[rank{rank}] ALL 20 ALLREDUCE OK",flush=True)

运行(注意加--network host,与 vLLM 环境保持一致):

dockerrun--rm--privileged--networkhost--ipc=host\--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_DEVICES=0,1\quay.io/ascend/vllm-ascend:v0.23.0.post1-openeuler\bash-lc'source /usr/local/Ascend/cann-9.1.0/set_env.sh 2>/dev/null || true; torchrun --nproc_per_node=2 --master_port=29501 /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_DEVICES=0,1# ===== ★ 本平台必需:缺任一行,TP=2 会在 HCCL 建链阶段挂死整机 =====-HCCL_INTRA_PCIE_ENABLE=1# 允许节点内走 PCIe(本平台唯一可用的卡间通道)-HCCL_INTRA_ROCE_ENABLE=0# 禁止节点内走 RoCE(该路径不可用)-HCCL_CONNECT_TIMEOUT=600# 放宽建链超时(无 HCCS 时建链较慢)
变量作用
HCCL_INTRA_PCIE_ENABLE=1显式允许节点内用 PCIe 做集合通信
HCCL_INTRA_ROCE_ENABLE=0禁止节点内走 RoCE(这条最可能是关键)
HCCL_CONNECT_TIMEOUT=600把建链超时放宽(无 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 OK

6.2 稳定性阶梯(6 档,累计 1.5 小时)

档负载TTFT p50TPOT p50输出 tok/s总 tok/sfailed
b1512/128 @ 并发 11.1 s34.8 ms21.91110
b2512/128 @ 并发 3238.0 s41.0 ms92.04670
b3512/128 @ 并发 6448.3 s120.2 ms99.15040
b48192/512 @ 并发 3227.8 s193.0 ms107.618310
b5129024/512 @ 并发 4419 s156.0 ms4.110380
b6512/128 ×2000 @ 并发 6452.2 s240.6 ms93.14730

结论:

  • 6 档全过,失败请求 0,内核故障计数全程 0(IPI 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(实际链路宽度只有能力的一半),跨卡通信只能经主机内存中转。这是平台物理条件决定的,不是缺陷;短请求体验正常。


七、踩坑清单(血泪总结)

  1. 不要用watch -n 1 npu-smi info!
    高频轮询会压垮驱动/监控通道,是"整机失联"的常见诱因。要用请 ≥60s 或脚本采样。

  2. 硬 panic(not syncing)会把日志一起丢掉—— 必须做"证据四件套":

    # ① RCU 卡死自动重启(避免只能去机房断电)sysctl-wkernel.panic_on_rcu_stall=1kernel.panic=30# ② journald 持久化(否则重启后查不到上次启动的日志)mkdir-p/var/log/journalsed-i's/^#\?Storage=.*/Storage=persistent/'/etc/systemd/journald.conf systemctl restart systemd-journald# ③ 串口 Log Session 落盘 ④ dmesg -wT 持续落盘(单写者!)
  3. dmesg采集必须"单写者"—— 同一分钟起两条会撞同名文件、互相覆盖,最后只剩一条。

  4. /tmp重启会清空—— 脚本一律放持久目录(如/home/bench/)。

  5. “敲了命令” ≠ “在跑”—— 采集类进程要验证pgrep有 pid + 输出文件 mtime 在涨。

  6. nohup setsid xxx &后 shell 报[1]+ 已完成是正常的(setsid 立刻 fork 退出)—— 验证永远看pgrep。

  7. hccl_test在新版 CANN 里是源码包(只有.cc/.h+ Makefile),不是现成可执行文件 —— 不如直接用torch_npu写个all_reduce探针快。

  8. HCCL 崩溃后可能残留 socket 占端口→ 可用HCCL_NPU_SOCKET_PORT_RANGE=60000-60015换端口范围。

  9. 双实例并存时,docker compose项目名必须不同(默认取目录名)—— 否则down会互删对方容器。显式写name:或用-p。

  10. 排查顺序建议:先做对照实验隔离变量,再写最小复现;不要一上来就调框架参数。


八、总结

  • 一个看起来像硬件故障的整机挂死,通过对照实验 + 最小复现,最终定位成可配置绕过的通信库适配问题;
  • 不用换硬件,加三个环境变量解决;
  • 沉淀出一套可复用的平台问题排查方法:对照实验隔离变量 → 脱离框架的最小探针 → 证据保全四件套。

如果这篇帮你省了几个小时,欢迎点赞收藏 👍 有遇到类似问题的,评论区交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询