The IOMMU Epiphany: Diagnosing GPU P2P DMA Failure Under SEV-SNP

Message excerpt (assistant, global index 6209): "Now I understand the full picture. The critical change is: IOMMU is now in full translation mode (amd_iommu=on, not iommu=pt) because SEV-SNP requires it. Previously it was likely in passthrough mode. The IO_PAGE_FAULTs are the GPUs trying to do P2P DMA through the IOMMU and failing."

In the course of a long and technically intricate debugging session spanning dozens of messages, one brief message stands out as the turning point. At index 6209, the assistant utters a short but decisive statement: "Now I understand the full picture." This single message represents the moment of diagnostic synthesis — the point at which scattered clues, failed experiments, and user-provided context coalesced into a correct causal model of a system failure. The message is only a few lines long, but it carries the weight of an entire debugging arc. This article examines that message in depth: why it was written, what reasoning produced it, what knowledge it required and generated, and how it reshaped the trajectory of the session.

The Debugging Saga: Context Leading to the Epiphany

To understand message 6209, one must first appreciate the dead end the assistant had been trapped in. The session's broader context (segment 40) involved reconfiguring a Proxmox host's GPU topology. The machine — an ASUS ESC8000A-E13 with dual AMD EPYC 9335 CPUs and 8× NVIDIA RTX PRO 6000 Blackwell GPUs — had been split so that 4 GPUs remained bound to the NVIDIA driver for an LXC container running SGLang, while the other 4 were moved to vfio-pci for passthrough to an SEV-SNP confidential VM.

The assistant had updated the SGLang service from TP=8 to TP=4 and swapped the model from Qwen3.5-397B NVFP4 to Qwen3.5-122B-A10B BF16. But every attempt to start the SGLang server with tensor parallelism across 4 GPUs resulted in the same failure: all four TP workers would log "Init torch distributed begin" and then hang indefinitely. The assistant tried a barrage of fixes: clearing NCCL environment variables, switching NCCL protocols, disabling custom all-reduce, pulling the latest SGLang source, reinstalling from scratch, checking TCP socket connectivity between ranks, and examining strace output. Every approach failed. The workers were alive, their TCP connections were established, but they would never complete distributed initialization.

The user then provided two critical pieces of information. First, they shared IO_PAGE_FAULT logs from the Proxmox host's kernel — AMD-Vi events showing the GPUs attempting DMA operations that the IOMMU was blocking. Second, they pointed the assistant to setup documentation at ~/kpro6-uefi/, specifically files named v1-uefi-settings.md and snp-pxm8-v1.md. The assistant read these files in the immediately preceding messages (6207–6208) and learned that the system was configured for SEV-SNP confidential computing, which requires full IOMMU translation.

The Message Itself

The message at index 6209 is deceptively short. It contains:

  1. A declarative statement of understanding: "Now I understand the full picture."
  2. The critical diagnosis: IOMMU is in full translation mode (amd_iommu=on) rather than passthrough mode (iommu=pt), and this is required by SEV-SNP.
  3. A causal explanation: the IO_PAGE_FAULTs are the GPUs attempting P2P DMA through the IOMMU and failing.
  4. A diagnostic action: running cat /proc/cmdline on the Proxmox host to verify the kernel boot parameters. The bash command and its output confirm the diagnosis. The kernel command line includes amd_iommu=on, mem_encrypt=on, and kvm_amd.sev=1 — all consistent with an SEV-SNP configuration. Notably absent is any iommu=pt (passthrough) option, which would have allowed devices to bypass IOMMU translation.

The Critical Insight: Why Full IOMMU Translation Breaks P2P DMA

The assistant's insight rests on a nuanced understanding of how GPU P2P (peer-to-peer) DMA interacts with IOMMU translation. When multiple GPUs are connected via PCIe, they can transfer data directly between each other's memory using P2P DMA. This works by having one GPU write to the PCIe BAR (Base Address Register) address of another GPU. In passthrough mode (iommu=pt), the IOMMU does not translate these addresses — it simply passes them through to the PCIe bus, allowing direct GPU-to-GPU transfers at hardware speed.

However, SEV-SNP (Secure Encrypted Virtualization - Secure Nested Paging) requires full IOMMU translation for security. The IOMMU must translate every DMA address to enforce memory encryption and isolation boundaries. When a GPU attempts a P2P DMA write to another GPU's BAR address, the IOMMU tries to translate that physical address through its page tables. But BAR addresses are not normal system memory — they are memory-mapped I/O regions that map to PCIe device memory. The IOMMU has no mapping for these addresses, so it raises an IO_PAGE_FAULT. The DMA transaction fails, data becomes corrupted, and NCCL — which relies on P2P transfers for its all-reduce and broadcast operations — hangs as it waits for data that will never arrive.

This explains why the SGLang server consistently hung at Init torch distributed begin. The init_process_group call in PyTorch distributed uses NCCL as its backend, and NCCL attempts to establish P2P communication channels between GPU pairs. When these P2P attempts fail due to IO_PAGE_FAULTs, NCCL enters a deadlock state — it cannot complete initialization, but it also cannot cleanly abort.## The Reasoning Process: How the Assistant Got There

The assistant's path to this insight was not straightforward. Looking at the preceding messages (6162–6208), we can trace the evolution of its mental model. Initially, the assistant assumed the problem was a configuration issue within the SGLang or NCCL stack. It tried overriding NCCL environment variables, disabling custom all-reduce, and even updating SGLang to the latest commit. Each attempt failed because the root cause was not in software configuration but in the hardware virtualization layer.

The strace output from message 6172 was particularly revealing: all four TP workers were blocked on futex and restart_syscall calls — the classic signature of a deadlock in a distributed synchronization primitive. The assistant correctly inferred that init_process_group had completed (the TCP store was connected), but something in the NCCL initialization after that was failing. It was looking in the right direction — NCCL — but at the wrong layer.

The turning point came when the user shared the IO_PAGE_FAULT logs. These logs showed AMD-Vi (the AMD IOMMU implementation) reporting page faults from the NVIDIA GPUs. The flags value 0x0030 is particularly informative: it indicates a write transaction that the IOMMU could not translate. The assistant immediately recognized these as P2P DMA failures, as evidenced by its response at message 6193: "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."

However, even at that point, the assistant did not yet have the full picture. It knew the IOMMU was blocking P2P, but it did not know why the IOMMU was configured that way. The answer came from reading the setup documentation. The snp-pxm8-v1.md file described the SEV-SNP configuration, which mandates full IOMMU translation. The v1-uefi-settings.md file documented the BIOS settings. Together, they provided the causal chain: SEV-SNP → full IOMMU translation → P2P DMA failure → NCCL hang.

Assumptions Made and Corrected

Several assumptions were implicit in the assistant's earlier debugging attempts, and message 6209 represents a correction of those assumptions:

Assumption 1: The problem is in software configuration. The assistant spent considerable effort tweaking NCCL environment variables, trying different NCCL protocols (Simple, Ring), and disabling custom all-reduce. These attempts assumed that NCCL's behavior could be modified through its standard configuration knobs. The reality was that NCCL was behaving correctly — it was attempting P2P DMA, and the hardware layer was failing. No amount of NCCL configuration could fix an IOMMU page fault.

Assumption 2: The GPU-to-GPU connectivity is intact. The assistant knew that P2P transfers worked before the host reconfiguration. It assumed that the hardware topology had not changed and therefore P2P should still work. What had changed was not the hardware but the IOMMU configuration — a layer invisible to most diagnostic tools.

Assumption 3: The hang is in PyTorch distributed initialization. While technically true (the hang occurred during init_process_group), this was a symptom, not a cause. The assistant initially treated it as a PyTorch-level issue, checking TCP connectivity and store synchronization. The actual failure was in NCCL's P2P channel establishment, which happens inside the NCCL library during ncclCommInitRank.

Knowledge Required and Knowledge Created

To understand message 6209, one needs knowledge spanning several domains: GPU architecture (P2P DMA over PCIe, BAR addressing), virtualization technology (IOMMU, SEV-SNP, DMA remapping), distributed computing (NCCL, all-reduce algorithms, tensor parallelism), and system administration (kernel boot parameters, Proxmox configuration). The assistant synthesized these domains into a coherent diagnosis.

The message creates new knowledge in several forms. First, it establishes a causal relationship between SEV-SNP IOMMU configuration and GPU P2P DMA failures — a relationship that was not previously documented in the session. Second, it provides a verifiable diagnostic: checking /proc/cmdline for amd_iommu=on vs iommu=pt. Third, it implicitly defines the solution space: either disable P2P DMA in NCCL (via NCCL_P2P_DISABLE=1) or reconfigure the IOMMU (which would break SEV-SNP). The assistant would go on to implement the first option, adding NCCL_P2P_DISABLE=1 to the SGLang service environment, which forced NCCL to use shared memory (SHM) transport instead of P2P DMA. This fixed the hang and allowed the server to load successfully.

The Broader Significance

Message 6209 exemplifies a pattern common in complex system debugging: the moment when scattered observations snap into a coherent model. The IO_PAGE_FAULTs, the SEV-SNP documentation, the NCCL hang, and the kernel command line were all pieces of a puzzle that only made sense when viewed together. The assistant's insight was not just that IOMMU was blocking P2P — it was why the IOMMU was configured that way, and why changing it would break other requirements.

This message also highlights the importance of cross-layer reasoning in debugging modern systems. The failure manifested at the application layer (SGLang hanging), but its root cause was in the virtualization layer (IOMMU configuration), triggered by a security requirement (SEV-SNP). The assistant had to trace the failure across four layers of abstraction — application, distributed framework, GPU driver, and hypervisor — to find the root cause. This kind of multi-layer debugging is increasingly essential as systems grow more complex.

The message also demonstrates the value of reading documentation. The assistant's breakthrough came not from running another experiment but from reading the setup notes that the user had pointed to. In the rush to try fixes, it is easy to overlook existing knowledge. The assistant's willingness to pause its experimental loop and absorb the documentation was what ultimately led to the correct diagnosis.