Skip to content

[https://nvbugs/6278399][fix] Add x86_64 path using CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR with… - #15129

Merged
Kefeng-Duan merged 4 commits into
NVIDIA:mainfrom
tensorrt-cicd:repair-bot-bug6278399
Jun 11, 2026
Merged

[https://nvbugs/6278399][fix] Add x86_64 path using CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR with…#15129
Kefeng-Duan merged 4 commits into
NVIDIA:mainfrom
tensorrt-cicd:repair-bot-bug6278399

Conversation

@tensorrt-cicd

@tensorrt-cicd tensorrt-cicd commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Root cause: DWDPTransport always allocated peer-shareable handles with CU_MEM_HANDLE_TYPE_FABRIC; on x86_64 B200 hosts without an IMEX channel cuMemCreate(FABRIC) returns CUDA_ERROR_NOT_PERMITTED (800).
  • Fix: Add x86_64 path using CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR with pidfd_open/pidfd_getfd-based FD exchange between sibling DWDP MPI workers; aarch64 keeps FABRIC. Plumb local-export and dup-ed peer FDs through release/cleanup. Removed obsolete nvbugs/6276923 SKIP entries.
  • Automated fix generated by repair-bot

Test plan

  • Verify fix on the same GPU type as the original failure
  • Check for regressions in related tests

Links

Summary by CodeRabbit

  • Bug Fixes

    • Resolved distributed training accuracy issues on DeepSeek V3 Lite configurations.
  • Chores

    • Enhanced file descriptor-based memory sharing for distributed training pipelines.
    • Implemented architecture-aware optimizations for cross-GPU memory management.
    • Improved resource cleanup procedures in distributed training sessions.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR extends DWDP transport to support POSIX file descriptor handles alongside FABRIC handles, with architecture-aware type selection and comprehensive FD lifecycle management including pidfd-based duplication, allocation tracking, and explicit cleanup on success and failure paths.

Changes

DWDP POSIX File Descriptor Support

Layer / File(s) Summary
Architecture-based Handle Type Selection
tensorrt_llm/_torch/modules/dwdp/vmm.py
Adds peer_handle_type() helper that selects CUDA memory handle type from platform architecture (FABRIC on aarch64, POSIX_FILE_DESCRIPTOR otherwise) and updates create_fabric_handle() to use fabric_only=False for architecture-driven selection.
Transport FD Infrastructure & Helpers
tensorrt_llm/_torch/modules/dwdp/transport.py
Introduces Linux pidfd helpers (_pidfd_open, _pidfd_getfd) and generalized _export_handle / _import_handle functions supporting both FABRIC bytes and POSIX FD payloads.
Transport Class State & Constructor
tensorrt_llm/_torch/modules/dwdp/transport.py
Updates DWDPTransport __slots__, __init__ signature, and initialization to accept and track new FD list parameters (local_export_fds, imported_local_fds).
Transport.create() Handle Exchange Flow
tensorrt_llm/_torch/modules/dwdp/transport.py
Implements complete three-phase handle exchange: Phase 1 detects handle type, opens pidfds for peers, and exports handles; Phase 2 imports handles via pidfd_getfd duplication (POSIX) or direct import (FABRIC); Phase 3 closes peer pidfds and finalizes constructor with FD lists.
Error Handling & Resource Cleanup
tensorrt_llm/_torch/modules/dwdp/transport.py
Extends exception cleanup path to close remaining pidfds, updates release() to clear FD lists, and extends _cleanup_resources() to close imported dup'd FDs and local export FDs in addition to existing CUDA resource cleanup.
Test Waiver Updates
tests/integration/test_lists/waives.txt
Removes waiver entries for three DWDP DeepSeek V3 Lite accuracy tests, indicating those tests now pass.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • NVIDIA/TensorRT-LLM#14453: Main PR's DWDP transport/VMM handle exchange changes (adding pidfd-based POSIX FD export/import) build on the underlying CUDA VMM and MNNVL transport layer refactor introduced in that PR.

Suggested reviewers

  • bo-nv
  • liji-nv
  • xinhe-nv
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding x86_64 support using CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR for DWDP transport, addressing the bug on x86_64 B200 hosts.
Description check ✅ Passed The PR description provides root cause, fix details, test plan confirmation, and bug link; however, it lacks a detailed description section in the template format.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tensorrt_llm/_torch/modules/dwdp/transport.py (2)

74-81: 💤 Low value

Clarify comment about syscall number applicability.

The comment states these syscall numbers are shared by x86_64/aarch64, but the POSIX_FD code path is never taken on aarch64 (since peer_handle_type() returns FABRIC there). Consider updating the comment to clarify that these syscalls are only exercised on x86_64.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/modules/dwdp/transport.py` around lines 74 - 81, Comment
clarification: update the block around _SYS_pidfd_open and _SYS_pidfd_getfd to
state these syscall numbers are defined for both x86_64 and aarch64 but are only
exercised on x86_64 because peer_handle_type() returns FABRIC on aarch64, so the
POSIX_FILE_DESCRIPTOR path (and therefore pidfd_open/pidfd_getfd) is not taken
on aarch64; mention POSIX_FILE_DESCRIPTOR, FABRIC, and functions/classes
MnnvlMemory.open_mnnvl_memory and peer_handle_type() to help locate the relevant
logic and make the platform-specific behavior explicit.

188-189: 💤 Low value

Missing docstring entries for new parameters.

The constructor docstring (lines 191-207) doesn't document local_export_fds and imported_local_fds. Consider adding entries describing their purpose for completeness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_torch/modules/dwdp/transport.py` around lines 188 - 189, The
__init__ constructor in tensorrt_llm._torch.modules.dwdp.transport (the class
taking parameters local_export_fds and imported_local_fds) is missing docstring
entries for these two new parameters; update the constructor docstring to add
clear descriptions for local_export_fds (e.g., a list of file descriptor
integers that this side will export/share for local IPC) and imported_local_fds
(e.g., a list of file descriptor integers imported from the peer to be used
locally), including expected types and semantics so the purpose and usage of
each parameter is documented alongside the existing parameter docs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tensorrt_llm/_torch/modules/dwdp/transport.py`:
- Around line 74-81: Comment clarification: update the block around
_SYS_pidfd_open and _SYS_pidfd_getfd to state these syscall numbers are defined
for both x86_64 and aarch64 but are only exercised on x86_64 because
peer_handle_type() returns FABRIC on aarch64, so the POSIX_FILE_DESCRIPTOR path
(and therefore pidfd_open/pidfd_getfd) is not taken on aarch64; mention
POSIX_FILE_DESCRIPTOR, FABRIC, and functions/classes
MnnvlMemory.open_mnnvl_memory and peer_handle_type() to help locate the relevant
logic and make the platform-specific behavior explicit.
- Around line 188-189: The __init__ constructor in
tensorrt_llm._torch.modules.dwdp.transport (the class taking parameters
local_export_fds and imported_local_fds) is missing docstring entries for these
two new parameters; update the constructor docstring to add clear descriptions
for local_export_fds (e.g., a list of file descriptor integers that this side
will export/share for local IPC) and imported_local_fds (e.g., a list of file
descriptor integers imported from the peer to be used locally), including
expected types and semantics so the purpose and usage of each parameter is
documented alongside the existing parameter docs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f10d5c08-e89e-4c28-bcd5-6834f547e61d

📥 Commits

Reviewing files that changed from the base of the PR and between c1e9b00 and b071c0b.

📒 Files selected for processing (3)
  • tensorrt_llm/_torch/modules/dwdp/transport.py
  • tensorrt_llm/_torch/modules/dwdp/vmm.py
  • tests/integration/test_lists/waives.txt
💤 Files with no reviewable changes (1)
  • tests/integration/test_lists/waives.txt

@tianyuz-nv

Copy link
Copy Markdown
Collaborator

[blocking] POSIX_FD path holds too many file descriptors — risks EMFILE / RLIMIT_NOFILE exhaustion

On the POSIX_FD path both imported_local_fds and local_export_fds are retained until release(). Because DWDP allocates one handle per (layer_idx, weight_name), the peak FD count is roughly:

  • imported_local_fds(dwdp_size - 1) × num_handles
  • local_export_fdsnum_handles

For a DSv3-class model (~58 MoE layers × several weights) at dwdp_size=8 on a single x86 B200 node, this can exceed the default ulimit -n (1024) and fail mid-setup.

Per the CUDA driver semantics, neither needs to be held that long:

  • cuMemImportFromShareableHandle(POSIX_FILE_DESCRIPTOR) dups the fd internally; the local fd can be closed immediately after the import call returns.
  • The exported fd only needs to outlive every peer's pidfd_getfd, i.e. until the Phase 3 comm.Barrier().

Requested changes:

  1. Close each dup-ed peer fd immediately after _import_handle(...) returns in Phase 2, instead of accumulating it in imported_local_fds. This removes the (dwdp_size - 1)× multiplier — the dominant term.
  2. Close local_export_fds right after the Phase 3 comm.Barrier() (where the per-peer pidfds are already being closed), not at release().
    • ⚠️ Ordering matters: the export fds must stay open until after the Barrier. Do not close them right after comm.allgather(...) in Phase 1 — peers call pidfd_getfd on them during Phase 2, and closing early would make those calls fail.
  3. Fix the now-incorrect docstring in _cleanup_resources: "... must stay open as long as that handle is in use" is not true for POSIX_FD — the imported CUDA handle is independent of the fd once cuMemImportFromShareableHandle returns.

This drops peak fd usage from ~dwdp_size × num_handles to a transient ~num_handles. The error-path cleanup should still defensively close whatever fds are tracked at the moment of failure.

(Note: this is entirely on the x86 POSIX_FD path — is_posix_fd is False on aarch64, so these lists stay empty there and behavior is unchanged on GB200/FABRIC.)

@tianyuz-nv

Copy link
Copy Markdown
Collaborator

[maintainability] Two independent arch checks decide the handle type — they can silently diverge

The peer-shareable handle type is currently decided in two places:

  • vmm.py::peer_handle_type() — used by transport.py to pick the export/import handle type.
  • vmm.py::get_allocation_prop(..., fabric_only=False) — used by create_fabric_handle() to pick the type the handle is actually created with.

Both branch on "aarch64" in platform.machine(), so they agree today. But if a future change updates one heuristic and not the other, we'd create a handle of one type and try to export it as another, which fails at runtime rather than at review time.

Requested change: make get_allocation_prop's non-fabric_only branch delegate to peer_handle_type() (or have both read a single shared helper), so there is exactly one place that maps arch → CUmemAllocationHandleType.

(Resolved type is identical on aarch64 either way — FABRIC — so this is a no-op on GB200/FABRIC; it only guards the x86 path against a future regression.)

DWDPTransport unconditionally used CU_MEM_HANDLE_TYPE_FABRIC for the
peer-shareable expert weight handles. On x86_64 hosts without an IMEX
channel (any non-GB200 Blackwell box, including the QA's B200 cluster),
cuMemCreate(FABRIC) is denied with CUDA_ERROR_NOT_PERMITTED (800) and
executor init aborts.

Mirror the existing arch-aware pattern from MnnvlMemory in
tensorrt_llm/_mnnvl_utils.py:

- aarch64 (GB200): keep CU_MEM_HANDLE_TYPE_FABRIC.
- x86_64: use CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR; transfer the FDs
  between sibling DWDP MPI workers via pidfd_open + pidfd_getfd.

DWDP semantics (composite VA, peer_views, scatter, prefetch) are
preserved -- only the IPC primitive used to transfer the handle changes.
The peer expert memory remains P2P-mappable across sibling processes.

Also remove the now-obsolete nvbugs/6276923 SKIP entries for
TestDwdpDeepSeekV3Lite tests in waives.txt.

Verified: GSM8K accuracy 63.99 (threshold 61.54) on 4xB200 x86_64.
Signed-off-by: tensorrt-cicd <90828364+tensorrt-cicd@users.noreply.github.com>
…-time fd usage

Previously both `imported_local_fds` and `local_export_fds` were retained
until `release()`, peaking at roughly `dwdp_size × num_handles` open FDs
during setup -- enough to exceed the default `ulimit -n` of 1024 on a
DSv3-class model at `dwdp_size=8`.

Per the CUDA driver semantics, neither needs to live that long:

- `cuMemImportFromShareableHandle(POSIX_FILE_DESCRIPTOR)` dups the FD
  internally, so the local copy can be closed immediately after the
  import call returns.  Drop the `imported_local_fds` list entirely and
  close inline in Phase 2.
- The exported FD only needs to outlive every peer's `pidfd_getfd`, i.e.
  until the Phase 3 `comm.Barrier()`.  Close `local_export_fds` right
  after the Barrier (alongside the existing pidfd cleanup) instead of at
  `release()`.

Peak FD usage drops from ~`dwdp_size × num_handles` to a transient
~`num_handles`.  The error-path caller of `_cleanup_resources` still
defensively closes whatever FDs are tracked at the moment of failure --
it just sees an empty list on the success path.

Also fix the now-stale docstring in `_cleanup_resources" that claimed
imported FDs must "stay open as long as that handle is in use" -- not
true for POSIX_FD (the imported CUDA handle is independent of the FD
once `cuMemImportFromShareableHandle` returns).

Behavior on aarch64 / FABRIC is unchanged (`is_posix_fd` is `False`, so
`local_export_fds` stays empty and no inline close runs).

Signed-off-by: tensorrt-cicd <90828364+tensorrt-cicd@users.noreply.github.com>
…gh peer_handle_type()

`vmm.py::get_allocation_prop(fabric_only=False)` and
`vmm.py::peer_handle_type()` both used to encode the same arch ->
`CUmemAllocationHandleType` table independently.  They agreed today
(both branched on `platform.machine() == "aarch64"`) but a future change
to one and not the other would create a handle of one type and then try
to export it as another -- a runtime failure instead of a review-time
catch.

Make the non-`fabric_only` branch of `get_allocation_prop` delegate to
`peer_handle_type()` so there is exactly one place that maps arch to
handle type.  Resolved values are unchanged on every supported arch
(aarch64 -> FABRIC, x86_64 -> POSIX_FILE_DESCRIPTOR), so this is a
no-op on GB200/FABRIC and only guards the x86 path against a future
regression.  The granularity-query path (`fabric_only=True`) is
untouched -- the handle type there is never actually consumed.

Signed-off-by: tensorrt-cicd <90828364+tensorrt-cicd@users.noreply.github.com>
@tensorrt-cicd
tensorrt-cicd force-pushed the repair-bot-bug6278399 branch from b071c0b to e6c8892 Compare June 9, 2026 08:49
@tianyuz-nv

Copy link
Copy Markdown
Collaborator

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53028 [ run ] triggered by Bot. Commit: e6c8892 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53028 [ run ] completed with state SUCCESS. Commit: e6c8892
/LLM/main/L0_MergeRequest_PR pipeline #42250 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@tianyuz-nv

Copy link
Copy Markdown
Collaborator

/bot run --disable-fail-fast --extra-stage "GB200-4_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-2"

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53073 [ run ] triggered by Bot. Commit: e6c8892 Link to invocation

@tianyuz-nv

Copy link
Copy Markdown
Collaborator

/bot run --disable-fail-fast --extra-stage "GB200-4_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-2"

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53223 [ run ] triggered by Bot. Commit: f41e258 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53073 [ run ] completed with state ABORTED. Commit: e6c8892

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53223 [ run ] completed with state SUCCESS. Commit: f41e258
/LLM/main/L0_MergeRequest_PR pipeline #42420 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@tianyuz-nv

Copy link
Copy Markdown
Collaborator

/bot run --disable-fail-fast --extra-stage "GB200-4_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-2"

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53282 [ run ] triggered by Bot. Commit: f41e258 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator Author

PR_Github #53282 [ run ] completed with state SUCCESS. Commit: f41e258
/LLM/main/L0_MergeRequest_PR pipeline #42472 completed with status: 'SUCCESS'

CI Report

Link to invocation

@Kefeng-Duan
Kefeng-Duan merged commit 205920d into NVIDIA:main Jun 11, 2026
7 checks passed
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.

4 participants