The Deceptive Promise of P2P: Diagnosing GPU Peer Access Under IOMMU Translation
Introduction
In the high-stakes world of large-scale ML inference deployment, few problems are as frustrating as a hang that offers no error message, no stack trace, and no obvious cause. Message 6215 of this opencode session captures a pivotal diagnostic moment: the assistant probes GPU peer-to-peer (P2P) access capability on a system where NCCL-based distributed initialization has been silently hanging for dozens of minutes. The results are superficially reassuring—every GPU reports it can access every other GPU—but this apparent success masks a deeper hardware-level incompatibility that has been corrupting every P2P DMA transfer. This article dissects this single message, exploring why it was written, what assumptions it carries, and how its seemingly positive result actually deepens the mystery rather than resolving it.
The Message
The assistant writes:
Single GPU works. Let me test P2P and NCCL separately: ``bash ssh root@10.1.230.174 '~/ml-env/bin/python3 -c " import torch n = torch.cuda.device_count() print(f\"GPUs: {n}\") for i in range(n): for j in range(n): if i != j: can = torch.cuda.can_device_access_peer(i, j) print(f\" GPU {i} -> GPU {j}: canAccessPeer={can}\") "' GPUs: 4 GPU 0 -> GPU 1: canAccessPeer=True GPU 0 -> GPU 2: canAccessPeer=True GPU 0 -> GPU 3: canAccessPeer=True GPU 1 -> GPU 0: canAccessPeer=True GPU 1 -> GPU 2: canAccessPeer=True GPU 1 -> GPU 3: canAccessPeer=True GPU 2 -> GPU 0: canAccessPeer=True GPU 2 -> GPU 1: canAccessPeer=True GPU 2 -> GPU 3: canAccessPeer=True GPU 3 -> GPU 0: canAccessPeer=True GPU 3 -> GPU 1: canAccessPeer=True GPU 3 -> GPU 2: canAccessPeer=True ``
The output is unambiguous: all 12 directed peer pairs report canAccessPeer=True. From CUDA's perspective, the four RTX PRO 6000 Blackwell GPUs are fully P2P-capable.
Why This Message Was Written
This message sits at a critical juncture in a multi-hour debugging session. The assistant had been attempting to launch an SGLang inference server serving the Qwen3.5-122B-A10B BF16 model across 4 GPUs using tensor parallelism (TP=4). Every attempt resulted in a hang at the init_torch_distributed phase—NCCL would start initializing and then freeze indefinitely, with all four TP worker processes blocked on futex and socket read syscalls ([msg 6172]).
The user had just provided two crucial pieces of information. First, they shared IO_PAGE_FAULT logs from the Proxmox host's kernel ([msg 6191]):
[73784.343781] nvidia 0000:01:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x002c address=0x24000000000 flags=0x0030]
These faults originate from the AMD IOMMU (AMD-Vi) and indicate that the GPUs are attempting DMA transfers to addresses that the IOMMU has not mapped or is blocking. Second, the user revealed that the host's IOMMU is in full translation mode (amd_iommu=on) rather than passthrough mode (iommu=pt), because SEV-SNP (Secure Encrypted Virtualization-Secure Nested Paging) requires full IOMMU translation for memory protection ([msg 6209]).
The assistant's reasoning chain is clear: if P2P DMA is triggering IO_PAGE_FAULTs, then NCCL's P2P transport—which relies on GPU direct memory access between peers—would be corrupting data or failing silently. But before concluding that P2P is broken, the assistant needs to establish a baseline: does CUDA itself think P2P is possible? The torch.cuda.can_device_access_peer() function queries the CUDA driver's P2P capability matrix, which reflects whether the GPUs are on the same PCIe root complex and have the necessary BAR mappings for peer access. If this returns False, the problem is at the PCIe topology level. If it returns True, the problem is subtler—the driver believes P2P should work, but something downstream (IOMMU translation) is breaking actual transfers.
This message is thus a diagnostic fork: it separates the question "can these GPUs theoretically do P2P?" from "does P2P actually work in practice?" The answer to the first question is a definitive yes, which forces the investigation deeper.## The Reasoning Process: From Hang to Hypothesis
To fully appreciate this message, one must trace the assistant's reasoning through the preceding messages. The hang was first observed in [msg 6171], where after 45 seconds the log showed only:
[2026-03-09 12:32:57 TP2] Mamba selective_state_update backend initialized: triton
[2026-03-09 12:32:57 TP2] Init torch distributed begin.
[2026-03-09 12:32:57 TP1] Mamba selective_state_update backend initialized: triton
[2026-03-09 12:32:57 TP1] Init torch distributed begin.
[2026-03-09 12:32:57 TP0] sglang is using nccl==2.29.3
The server never completed initialization. The assistant systematically eliminated possibilities: it checked that NCCL sockets were established ([msg 6173]), verified that the TCPStore port was listening and all ranks were connected ([msg 6178]), and even straced the processes to confirm they were blocked on futex and socket reads ([msg 6172]). The hang was not in NCCL's socket setup—it was after socket setup, during the actual distributed initialization that uses NCCL for collective communication.
When the user provided the IO_PAGE_FAULT logs, the pieces fell into place. The assistant immediately recognized the significance: "Those IO_PAGE_FAULTs from AMD-Vi (IOMMU) on the nvidia GPUs are a big red flag—the GPUs are trying to do P2P DMA but IOMMU is blocking it" ([msg 6193]). The subsequent investigation of the host's kernel command line confirmed amd_iommu=on with no iommu=pt override ([msg 6209]), meaning the IOMMU is performing full address translation for all DMA transactions.
The assistant then wrote a diagnostic script (gpu_diag.py, [msg 6211]) to test basic GPU functionality, P2P transfers, and NCCL collective operations. But the script didn't execute—the remote Python process produced no output ([msg 6213]). So the assistant fell back to a simpler inline test: first confirming single-GPU operations work ([msg 6214]), then testing P2P capability with can_device_access_peer.
The Critical Distinction: Capability vs. Reliability
The most important aspect of this message is the distinction between two very different questions:
- Does the CUDA driver believe P2P is possible? This is what
can_device_access_peeranswers. It checks whether the GPUs share a PCIe switch topology that allows direct BAR-to-BAR access without host memory involvement. On the ASUS ESC8000A-E13 motherboard with dual AMD EPYC 9335 CPUs, the four GPUs assigned to the container are connected through PCIe switches to a single root complex, so the answer is correctly True. - Does P2P DMA actually succeed when IOMMU translation is active? This is what the IO_PAGE_FAULTs answer: no. The IOMMU, when in full translation mode, intercepts every DMA transaction and translates the device's physical addresses through its I/O page tables. For P2P transfers, GPU A attempts to write directly to GPU B's BAR address. Under passthrough mode (
iommu=pt), the IOMMU allows this direct access. Under full translation (amd_iommu=on), the IOMMU expects all DMA addresses to be mapped through its page tables—and P2P BAR addresses are not mapped, causing page faults. The assistant's test reveals a dangerous gap: the driver reports P2P as available, but the IOMMU silently corrupts or blocks the actual transfers. NCCL, trusting the driver's capability matrix, attempts to use P2P transport and hangs when data never arrives or arrives corrupted.
Assumptions and Potential Pitfalls
This message makes several implicit assumptions that deserve scrutiny:
Assumption 1: can_device_access_peer is a reliable proxy for P2P functionality. The assistant is using this as a quick check before running a more expensive NCCL test. But as discussed above, this function only queries the driver's topology-based capability matrix. It does not actually perform a DMA transfer. The assistant's next planned step—"test P2P and NCCL separately"—acknowledges this limitation. The message is explicitly framed as a preliminary check before deeper testing.
Assumption 2: The IO_PAGE_FAULTs are caused by NCCL's P2P traffic. This is a strong hypothesis but not yet proven. The faults could theoretically come from other sources—GPU driver initialization, CUDA runtime setup, or even the vfio-pci devices bound to the VM. However, the addresses in the fault logs (e.g., 0x24000000000, 0x34000010000) are consistent with GPU BAR addresses, and the timing correlates with NCCL initialization attempts. The assistant is operating on a well-reasoned but unconfirmed hypothesis.
Assumption 3: The four container GPUs are the ones producing the faults. The IO_PAGE_FAULT logs reference PCIe addresses (0000:01:00.0, 0000:11:00.0, 0000:71:00.0). The assistant had previously split the 8 GPUs between the LXC container and a SEV-SNP VM ([chunk 40.0]), with GPUs on NUMA 0 bound to the nvidia driver for the container and GPUs on NUMA 1 bound to vfio-pci for the VM. The faulting addresses include both domains, suggesting the issue affects all GPUs regardless of binding.
Assumption 4: The fix will involve disabling P2P in NCCL. The assistant is already converging on NCCL_P2P_DISABLE=1 as the likely solution, based on the understanding that P2P DMA cannot work under full IOMMU translation. This assumption proves correct in the subsequent messages, where setting this environment variable allows the server to initialize successfully.
Input Knowledge Required
To understand this message, one needs knowledge of several domains:
- CUDA P2P topology: Understanding that
can_device_access_peerqueries the CUDA driver's peer mapping, which depends on PCIe topology (same root complex, PCIe switch connectivity) and BAR addressability. - IOMMU and DMA translation: The concept that an IOMMU can operate in passthrough mode (where device DMA addresses pass through untranslated) or full translation mode (where all DMA goes through I/O page tables). SEV-SNP requires full translation for memory encryption and isolation.
- NCCL transport selection: NCCL probes available transports (P2P, SHM, NVLink, IB, etc.) and selects the fastest one. P2P is typically the fastest for GPU-to-GPU on the same node, but it fails when IOMMU blocks direct BAR access.
- The SGLang initialization sequence: The server initializes NCCL via
init_torch_distributed, which callstorch.distributed.init_process_groupwith the NCCL backend. This in turn triggers NCCL's transport discovery and collective initialization. - The hardware topology: Four RTX PRO 6000 Blackwell GPUs on a dual-socket AMD EPYC Turin platform, connected via PCIe Gen5 x16 slots through PCIe switches, with AMD-Vi IOMMU in full translation mode for SEV-SNP support.
Output Knowledge Created
This message produces several valuable pieces of knowledge:
- Confirmation of P2P capability at the driver level: All 4 GPUs report peer access as available, confirming the PCIe topology supports direct GPU-to-GPU BAR access.
- Narrowing of the problem space: The hang is not caused by a topology limitation (e.g., GPUs on different root complexes) or a driver-level P2P disablement. The problem must be at a lower level—either in the IOMMU translation layer or in NCCL's transport implementation.
- A clear next diagnostic step: Having confirmed driver-level P2P capability, the assistant must now test actual P2P data transfer (e.g., using a simple CUDA kernel that writes from one GPU's memory to another's) to see if the transfers succeed or produce corruption/faults.
- Evidence for the IOMMU hypothesis: The combination of
can_device_access_peer=Truewith IO_PAGE_FAULTs in the host kernel log strongly supports the theory that IOMMU translation is the root cause. If P2P were broken at the PCIe level,can_device_access_peerwould return False. Since it returns True, the breakage must be in the DMA translation path.
The Broader Context: A System at the Edge of Compatibility
This message is a microcosm of the challenges faced when deploying cutting-edge ML hardware and software. The system involves:
- Latest-generation GPUs: NVIDIA RTX PRO 6000 Blackwell (SM120 architecture), released in late 2025
- Latest-generation CPUs: AMD EPYC 9335 (Zen 5 "Turin"), also recent
- Cutting-edge virtualization: SEV-SNP confidential computing with GPU passthrough
- Nightly software stacks: PyTorch 2.12.0.dev, SGLang main branch from days ago
- A complex multi-tenant setup: 8 GPUs split between a container and a confidential VM Each layer adds complexity and potential incompatibility. The IOMMU issue is a classic example of a "leaky abstraction"—the CUDA driver reports P2P as available because the PCIe hardware supports it, but the IOMMU layer beneath it silently breaks the actual transfers. The assistant must navigate these layers, testing each one independently to isolate the fault.
Conclusion
Message 6215 is a deceptively simple diagnostic step that reveals the depth of the debugging challenge. The assistant asks a straightforward question—"can these GPUs access each other?"—and gets a straightforward answer: yes. But this answer, rather than solving the mystery, deepens it. The GPUs can do P2P, yet NCCL hangs. The IOMMU is blocking DMA, yet the driver reports P2P as available. The contradiction forces the investigation to the next level: testing actual data transfer, not just capability queries.
This message exemplifies the systematic debugging approach that characterizes the entire session. The assistant doesn't jump to conclusions or apply random fixes. It formulates hypotheses, tests them with minimal experiments, and uses each result to narrow the search space. The can_device_access_peer test is a perfect example of a "cheap diagnostic"—a quick, low-cost check that eliminates one class of explanations and points toward the next. In the messages that follow, the assistant will confirm that actual P2P transfers produce corruption under IOMMU translation, and will ultimately fix the issue with NCCL_P2P_DISABLE=1, forcing NCCL to use the slower but reliable shared memory (SHM) transport. But at this moment, captured in message 6215, the problem is still a puzzle—and the assistant has just ruled out one wrong answer.