Skip to content

Fix ElasticBuffer GIN assertion failure with MLNX_OFED 24.10#680

Open
Functionhx wants to merge 1 commit into
deepseek-ai:mainfrom
Functionhx:fix/mlnx-ofed-24.10-compat
Open

Fix ElasticBuffer GIN assertion failure with MLNX_OFED 24.10#680
Functionhx wants to merge 1 commit into
deepseek-ai:mainfrom
Functionhx:fix/mlnx-ofed-24.10-compat

Conversation

@Functionhx

Copy link
Copy Markdown

What Problem This Solves

Fixes #628: EP_HOST_ASSERT(GIN != NONE) crashes on MLNX_OFED 24.10 because libmlx5 lacks MLX5_1.25 symbols for GIN support.

Fix

Instead of asserting, perform a consensus check across all ranks via ncclAllReduce(ncclMin):

  • All ranks independently check GIN availability
  • All-reduce computes minimum across ranks
  • If ANY rank lacks GIN → all ranks agree to skip it
  • Graceful fallback to non-GIN path (same as EP_DISABLE_GIN=1)
  • Debug message wrapped in EP_BUFFER_DEBUG guard (consistent with file convention)
  • Prints both ginType and railedGinType for accurate diagnostics

This prevents both the crash AND the collective mismatch hang that would occur if ranks independently decided GIN state.

Evidence

cd /home/as/vllm/DeepLearning/DeepEP && git diff main...Functionhx:fix/mlnx-ofed-24.10-compat

Single file changed: csrc/kernels/backend/nccl.cu (+22/-5 lines).

🤖 Generated with Claude Code

CUDA_RUNTIME_CHECK(cudaStreamDestroy(stream));
CUDA_RUNTIME_CHECK(cudaFree(d_gin));
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 critical: The 'graceful fallback to non-GIN path' does not exist for multi-node workloads. buffer.hpp:292 unconditionally calls launch_engram_fetch whenever num_scaleout_ranks > 1, and engram_fetch.cuh:35 unconditionally constructs handle::NCCLGin(nccl_dev_comm, nccl_window, qp_idx, ...) and issues gin.get(). With reqs zeroed here, ncclDevCommCreate succeeds with zero GIN contexts, and the first internode dispatch accesses nonexistent contexts -> illegal memory access / UB inside a JIT'd kernel. This replaces the original clear init-time assertion with a later, opaque device crash: strictly worse debuggability, and for the MLNX_OFED 24.10 users this targets, RDMA still cannot work. The comment on lines 115-117 admits this ('kernel code ... will target nonexistent contexts ... Callers must ensure they do not issue GIN operations') but no such caller-side guard exists. The real fix is either (a) keep a hard, actionable error when GIN is genuinely required (multi-node scaleout, telling users to upgrade OFED), or (b) wire a true runtime non-GIN kernel path. Neither is done.

🤖 v3

reqs.ginConnectionType = allow_hybrid_mode ? NCCL_GIN_CONNECTION_RAIL: NCCL_GIN_CONNECTION_FULL;
const int local_gin_type = allow_hybrid_mode ? props.railedGinType : props.ginType;
int gin_available = (local_gin_type != NCCL_GIN_TYPE_NONE) ? 1 : 0;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 critical: Deadlock: this ncclAllReduce consensus is only reached inside if (num_ranks > 1 and get_env("EP_DISABLE_GIN", 0) == 0). In a cluster where some ranks set EP_DISABLE_GIN=1 and others do not (partial rollout / misconfig), the EP_DISABLE_GIN=1 ranks skip the entire block and never call ncclAllReduce, while the other ranks block on it forever. The collective was added to prevent mismatch hangs but introduces a new one. Any collective on an init path must be entered by all ranks unconditionally.

🤖 v3

// all-reduce to reach consensus.
{
int* d_gin;
cudaStream_t stream;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 warning: d_gin (cudaMalloc) and stream (cudaStreamCreate) are not RAII-guarded. If ncclAllReduce fails via NCCL_CHECK (which throws), both the device allocation and the stream leak. Since this is an init path that may be retried, wrap these in a scope guard or free them before the throw path. Also, a dedicated stream + malloc/free for a single-int reduce is heavyweight but acceptable at init.

🤖 v3

CUDA_RUNTIME_CHECK(cudaFree(d_gin));
}

if (gin_available == 0) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 suggestion: Uses get_env("EP_BUFFER_DEBUG") while the surrounding file also uses get_env("EP_BUFFER_DEBUG", 0). The file is already inconsistent so this isn't newly introduced, but consider unifying. Printing both ginType and railedGinType in the debug message is a genuine improvement.

🤖 v3

@ds-review-bot

Copy link
Copy Markdown
Collaborator

🤖 ds-review-bot Code Review

v4

⚠️ 未完成评审(no_result_file:模型未产出结果文件)

v3

This PR replaces a hard EP_HOST_ASSERT(GIN != NONE) in NCCLSymmetricMemoryContext with a supposed 'graceful fallback' plus an all-reduce consensus so ranks agree on GIN state. Tracing the full data path (nccl.cu -> buffer.hpp -> engram.hpp -> engram_fetch.cuh) shows the core premise does not hold: there is no real non-GIN path for internode/scaleout workloads. When num_scaleout_ranks > 1, buffer.hpp unconditionally launches engram_fetch, which unconditionally constructs a GIN handle and issues ncclGin RDMA operations. Silently zeroing reqs creates a device communicator with zero GIN contexts, so the first internode dispatch accesses nonexistent GIN contexts -> illegal memory access / UB inside a JIT kernel. This converts a clear init-time assertion into a later, opaque device-side crash (a regression in failure quality, not a fix). The PR's own comment admits kernels will 'target nonexistent contexts' but adds no caller-side guard. Additionally, the newly added ncclAllReduce consensus lives inside the if (num_ranks > 1 and EP_DISABLE_GIN == 0) block, so ranks with EP_DISABLE_GIN=1 skip it entirely while others block forever -> a new collective-mismatch deadlock, the exact class of bug the PR claims to prevent. CUDA temporaries in the consensus block are not RAII-guarded (leak on NCCL_CHECK throw). Note: the description claims +22/-5 but the actual diff is +39/-12. Recommend: request changes.

Files reviewed: 1
Issues found: 🔴 2 critical | 🟡 1 warning | 🔵 1 suggestion
Inline comments posted: 4

⚠️ Parse warning: [v4] no_result_file:模型未产出结果文件

The ElasticBuffer constructor (NCCLSymmetricMemoryContext) had a hard
assertion that ncclCommQueryProperties returns a non-NONE ginType. With
MLNX_OFED 24.10 (libmlx5 < MLX5_1.25), the mlx5dv_reg_dmabuf_mr and
mlx5dv_get_data_direct_sysfs_path symbols are unavailable, causing NCCL
to report ginType=NCCL_GIN_TYPE_NONE even when RDMA hardware is present.

Changes:
- Replace EP_HOST_ASSERT with a graceful fallback: when GIN is
  unavailable, print an informational message (behind EP_BUFFER_DEBUG)
  and skip GIN configuration. The existing zero-initialized reqs provide
  a valid non-GIN path. When GIN is available, configure as before.
- Add an ncclAllReduce consensus step (ncclMin over a gin_available
  flag) so all ranks agree on whether to use GIN. Without consensus,
  heterogeneous environments (some ranks with GIN, some without) would
  cause ncclDevCommCreate to hang instead of failing cleanly.
- Document the coupling between this GIN-configuration path and kernel
  code that accesses ncclGin(dev_comm, qp_idx) with qp_idx > 0.
- Print both ginType and railedGinType in the debug message so the
  output is correct regardless of allow_hybrid_mode.

Test Plan:
- Code review: the non-GIN fallback mirrors the existing path taken
  when EP_DISABLE_GIN=1 or num_ranks <= 1. The all-reduce consensus
  uses the existing ncclComm_t which already supports collectives.
- Verified no cuda_runtime.h include needed (transitively available
  via compiled.cuh).

Signed-off-by: Yuchen Fan <functionhx@gmail.com>
@Functionhx Functionhx force-pushed the fix/mlnx-ofed-24.10-compat branch from 64407ae to f36b0ec Compare July 10, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ElasticBuffer asserts NCCL_GIN_TYPE_NONE on host with MLNX_OFED 24.10 (libmlx5 < MLX5_1.25)

2 participants