Skip to content

[Draft] EP support for rail isolated topology through Host based RDMA - #558

Draft
itej89 wants to merge 110 commits into
ROCm:mainfrom
itej89:feat/ep-rdma-sharing
Draft

[Draft] EP support for rail isolated topology through Host based RDMA#558
itej89 wants to merge 110 commits into
ROCm:mainfrom
itej89:feat/ep-rdma-sharing

Conversation

@itej89

@itej89 itej89 commented Aug 13, 2026

Copy link
Copy Markdown

Motivation

Enable Expert Parallelism (EP) on rail-isolated clusters where Cross NIC communication isn't possible and GPU-initiated IBGDA doorbells won't work.

Technical Details

Adds TransportType::PROXY = 3 to the existing dispatch framework. EP kernels route through DISPATCH_TRANSPORT_TYPE → template specializations in shmem_proxy_kernels.hpp, leaving shmem_ibgda_kernels.hpp untouched. Proxy code compiles only when -DMORI_PROXY_ENABLED is set through JIT pipeline.

Per-NIC CPU threads poll host-pinned ring buffers (64K slots × 128B, hipHostRegister mapped). GPU warps write ProxyCmd descriptors; CPU threads convert to ibv_post_send with per-NIC lkey/rkey override for rail-isolated MR registration. Signal delivery uses RDMA_WRITE for data signals and SEND_WITH_IMM + CPU-side __atomic_fetch_add for barrier atomics (ionic). CX7 uses native IBV_WR_ATOMIC_FETCH_AND_ADD for barrier atomics.

Test Plan

MORI EP benchmark tests

Test Result

TBD

Submission Checklist

Tej Kiran and others added 30 commits August 11, 2026 18:53
Add proxy infrastructure for EP on AINIC where GPU-initiated IBGDA WQE
posting doesn't work (NIC rejects GPU-written WQEs). The proxy approach:
GPU writes command descriptors to a shared ring, CPU thread calls
ibv_post_send, GPU polls per-slot status for completion.

New files:
- proxy_types.hpp: ProxyCmd, ProxyRing shared GPU+CPU types (128-byte aligned)
- proxy_device_primitives.hpp: GPU __device__ functions (ProxyPostWrite,
  ProxyPostWriteInline, ProxyPostAtomic, ProxyQuiet)
- proxy_thread.hpp/.cpp: CPU proxy thread (polls ring, ibv_post_send, ibv_poll_cq)
- gpu_proxy_rdma_repro.cpp: standalone 2-node reproducer

Modified:
- internal.hpp: add useProxy + proxyRing to GpuStates
- shmem_ibgda_kernels.hpp: add proxy path to ShmemPutMemNbi and ShmemQuietPsd

Test results (MI350X + AINIC, loopback):
- Level 1 (types): PASS
- Level 2 (CPU proxy thread, 1000 ops): PASS, 2.3M ops/s
- Level 3 (GPU proxy, 5000 ops with ring wrap): PASS, 107K ops/s, 9.4 us/op

Refs: #2

Co-Authored-By: Claude <noreply@anthropic.com>
Wire the proxy infrastructure into the MORI build and runtime:

- Add proxy paths to all 6 PSD SHMEM functions in shmem_ibgda_kernels.hpp:
  ShmemPutMemNbi, ShmemPutSizeImmNbi, ShmemPutMemNbiSignal,
  ShmemAtomicSizeNonFetch, ShmemAtomicTypeFetch, ShmemQuietPsd
- Each function checks globalGpuStates->useProxy and redirects to
  ProxyPost* primitives, bypassing IBGDA WQE/doorbell/CQ entirely
- Allocate ProxyRing in GpuStateInit when MORI_USE_IBGDA_PROXY=1
- Start/shutdown ProxyThread with QP handles from endpoint setup
- Add proxy_thread.cpp to application CMakeLists.txt
- Forward-declare ProxyThread in internal.hpp (avoids <atomic> in HIP)
- Add host-only guard to proxy_thread.hpp

Env var: MORI_USE_IBGDA_PROXY=1 enables proxy mode for ionic AINIC.
Default (unset or 0) uses native IBGDA — no behavior change.

Co-Authored-By: Claude <noreply@anthropic.com>
The ionic IBGDA provider creates ibv_qp via ibv_create_qp_ex but never
populated RdmaEndpoint.ibvHandle. The proxy thread needs these handles
to call ibv_post_send/ibv_poll_cq. Set endpoint.ibvHandle.qp and .cq
from the IonicQpContainer's ibv handles.

Also add stderr debug prints to track proxy activation in shmem init.

EP test status: proxy activates (32 QPs found, proxy thread started),
GPU kernel takes proxy path (no IBGDA WQE posting, no GPU fault), but
RDMA fails with remote access error (status=10) — this is a rail
topology issue (MR registered on wrong PD/NIC), not a proxy issue.
Next: fix rail-isolated topology routing (Task 4).

Co-Authored-By: Claude <noreply@anthropic.com>
Standalone test proving any ionic NIC can DMA any GPU's VRAM across
XGMI on MI350X. All three scenarios pass:
- ionic_0 → GPU 0 (same rail): PASS
- ionic_3 → GPU 0 (cross-NIC): PASS
- ionic_0 → GPU 5 (reverse cross-NIC): PASS

This validates the send-side routing approach: GPU 0 can send to
remote GPU 3 by posting through ionic_3, which reads GPU 0's buffer
across XGMI and sends to remote ionic_3.

Co-Authored-By: Claude <noreply@anthropic.com>
Cherry-pick rail-affinity QP distribution from fix/ep-rail-affinity-ainic:
- Add allRdmaDeviceContexts (one per ionic NIC)
- Create QP[qp] from allRdmaDeviceContexts[qp % N]
- Each QP's GID matches its rail for same-rail routing

Add lkey_override to ProxyQpHandle so proxy thread can use per-NIC lkeys
when posting through a QP on a different NIC than the buffer's owner.

Cross-NIC DMA validated: ionic_3 can DMA GPU 0's VRAM across XGMI (PASS).

Still needed: per-NIC MR registration in symmetric memory init.

Co-Authored-By: Claude <noreply@anthropic.com>
Add per-NIC MR registration and rkey Allgather for send-side routing:
- symmetric_memory.cpp: register heap on all NIC PDs, Allgather rkeys per NIC
- init.cpp: wire per-NIC lkeys + rkeys into ProxyQpHandle
- proxy_thread: override lkey AND rkey when posting

Current status: QP index mapping bug — epIndex from GPU kernel doesn't
match the proxy's flat QP array. All posts go to qp_idx=0 instead of
the correct per-NIC QP. Transport retry errors from wrong routing.

Fix needed: preserve epIndex→QP mapping (include null slots for non-RDMA
peers, or add an index translation table).

Co-Authored-By: Claude <noreply@anthropic.com>
The GPU kernel passes epIndex (pe * numQpPerPe + qp) as qp_idx to the
proxy. The proxy QP array must match this indexing. Changed from a
compact push_back array to a full-size indexed array with null entries
for non-RDMA peer slots.

Current status: SIGSEGV during multi-rail QP creation — ionic's
CreateRdmaEndpoint tries to map GPU doorbells via
rocm_memory_lock_to_fine_grain when creating QPs on non-affinity NICs.
In proxy mode this mapping is unnecessary (CPU calls ibv_post_send,
not GPU). Next: skip doorbell mapping in proxy mode, or use plain
ibv_create_qp instead of the IBGDA parent domain path.

Co-Authored-By: Claude <noreply@anthropic.com>
Status: proxy infrastructure works (Level 1-3 pass), per-NIC MR/rkey
exchange works, multi-rail QP creation attempted but SIGSEGV during
EP init — likely from IonicQpContainer GPU doorbell mapping when
creating QPs on non-affinity NICs.

Need to: add debug fprintf traces to find exact crash point,
then create plain QPs (no IBGDA parent domain) in proxy mode.

Reverted ionic.cpp to clean state to re-apply changes with proper
debug tracing rather than guessing.

Co-Authored-By: Claude <noreply@anthropic.com>
PYTHONFAULTHANDLER reveals crash at test_dispatch_combine_internode.py:345:
  self.rng = torch.Generator(device=self.device)

The SIGSEGV is in torch.Generator device init AFTER shmem init completes.
The GPU's HIP runtime is in a bad state — likely from cross-NIC QP
creation touching GPU state on non-affinity devices.

In proxy mode, plain QP creation (ibv_create_qp on base PD) avoids
GPU VRAM allocation and doorbell mapping. But the IBGDA QPs for the
PRIMARY NIC (qpPool path) still go through IonicQpContainer which
calls rocm_memory_lock_to_fine_grain and hipGetDevice.

The crash varies by process (3, 6, 7) — non-deterministic, suggests
a race in GPU device context across the 8 multiprocessing.spawn
processes sharing the same GPU set.

Need to investigate: does IonicQpContainer corrupt GPU state when
called from multiprocessing.spawn child processes?

Co-Authored-By: Claude <noreply@anthropic.com>
IonicDeviceContext constructor creates GPU parent domains with custom
allocators (hipExtMallocWithFlags) which corrupt GPU state when called
from forked multiprocessing.spawn child processes. In proxy mode, skip
create_parent_domain entirely — plain QPs don't need GPU VRAM for SQ/CQ.

Also skip parent domain for allRdmaDeviceContexts (all 8 NICs).

SIGSEGV now occurs at line 343 (shmem_mype()) instead of line 345
(torch.Generator). The crash shifted from GPU device init to MORI
shmem global state access. Need to investigate shmem_mype() C extension.

Co-Authored-By: Claude <noreply@anthropic.com>
Root cause of SIGSEGV: stale JIT cache (.mori/jit/) compiled with old
GpuStates layout (without useProxy/proxyRing fields). hipMemcpy of the
larger struct overwrote adjacent GPU globals, corrupting shmem state.

Fix: clear JIT cache (rm -rf ~/.mori/jit/) when GpuStates layout changes.
The JIT hash should include struct sizes to auto-invalidate, but currently
doesn't — needs follow-up.

Also: use posix_memalign instead of hipHostMalloc for proxy ring to avoid
GPU context corruption in multiprocessing.spawn child processes.
hipHostMallocCoherent modifies GPU device state which conflicts with
concurrent child processes using the same GPU set.

Co-Authored-By: Claude <noreply@anthropic.com>
Re-applied all proxy ionic changes after JIT cache fix:
- Skip create_parent_domain in proxy mode
- Create plain QPs via ibv_create_qp (no GPU VRAM SQ/CQ)
- ConnectEndpoint handles proxyQpPool
- Proxy thread starts with per-NIC lkey/rkey overrides

Status: no more JIT cache SIGSEGV, but per-NIC ibv_reg_mr
in proxy thread start still causes SIGSEGV in some child
processes. The ibv_reg_mr on ionic for GPU VRAM from a
non-affinity GPU process may corrupt GPU device context.

Next: move per-NIC MR registration to symmetric_memory.cpp
(which already does ibv_reg_mr safely) instead of doing it
in the proxy thread start. Or defer multi-NIC until single-NIC
proxy works end-to-end.

Co-Authored-By: Claude <noreply@anthropic.com>
…crash

- Move per-NIC MR registration from init.cpp to symmetric_memory.cpp
  where ibv_reg_mr already runs safely
- Store perNicLkeys in SymmMemManager, read from init.cpp
- Don't start proxy thread when qpCount==0 (no RDMA peers)
- Add barrier between per-NIC MR registrations
- Add finalize debug traces

Confirmed: crash is from proxy thread start (pthread_create + main loop)
in multiprocessing.spawn child processes. Without proxy thread, all 8
processes init successfully and reach dispatch (hangs at IBGDA as expected).
With proxy thread, random child processes SIGSEGV.

Proxy thread issue: ibv_poll_cq in the thread's main loop may access
invalid verbs state in forked/spawned child processes. Need to
investigate CQ/QP handle validity across process boundaries.

Co-Authored-By: Claude <noreply@anthropic.com>
Two fixes that resolve the SIGSEGV in multiprocessing.spawn child processes:

1. Lazy CQ polling: only poll CQ after ops_posted > 0.
   Previously the thread called ibv_poll_cq immediately on start,
   before any QP transitions completed across all processes.

2. Use posix_memalign + hipHostRegister instead of hipHostMalloc.
   hipHostMallocCoherent corrupts GPU device state in child processes.
   posix_memalign + hipHostRegister(Mapped|Portable) achieves the same
   GPU accessibility without touching GPU device context at allocation.

Result: EP test runs with proxy enabled — no SIGSEGV, all 8 GPU
processes init successfully, proxy threads start, GPU kernel writes
to proxy ring, CPU thread posts via ibv_post_send, CQE completions
received. CQE errors (transport retry) are expected with single NIC
due to rail-isolated routing — needs multi-NIC for full EP.

Co-Authored-By: Claude <noreply@anthropic.com>
Pensando AINIC has three issues beyond the known IBGDA doorbell rejection:
1. RDMA atomics return CQE success but don't modify remote memory
2. QP connections must use matching NIC on both sides (rail isolation)
3. Traffic class TC must be set on proxy QPs for AINIC routing

Fixes applied:
- Agreed-rail mapping: both sides use max(myLocalGpu, peerLocalGpu) so
  QP connections are always on the same NIC index
- Proxy QP setup matches non-proxy: active MTU, SL, TC, max_rd_atomic=15,
  IBV_ACCESS_REMOTE_ATOMIC
- Atomic internal buffer (ibuf) allocated and registered per proxy QP for
  atomic result DMA; atomics use ibuf's own lkey instead of perNic override
- IBV_SEND_FENCE on atomics for write-before-atomic ordering
- Emulate RDMA atomics via IBV_WR_SEND_WITH_IMM: sender posts inline
  [dst_addr, add_value] payload; receiver proxy DrainCq detects IBV_WC_RECV,
  reads payload, does CPU __atomic_fetch_add on GPU memory, re-posts recv WR
- Pre-post 128 recv WRs per QP after RTS transition (Pensando rejects
  post_recv in RESET state)
- Recv buffer info plumbed: IonicDeviceContext::proxyRecvInfo map →
  IBVerbsHandle::recvBuf/recvLkey/recvCount → ProxyQpHandle
- NCCL_IB_DISABLE=1 required (NCCL also hits IBGDA doorbell issue)

Status: EP dispatch/combine runs end-to-end, 127/128 tokens pass correctness.
Token 0 fails due to CPU-GPU atomic coherency race when both intra-node
(GPU atomic via P2P) and inter-node (CPU __atomic_fetch_add via proxy)
write to the same signal counter.

Co-Authored-By: Claude <noreply@anthropic.com>
Two fixes for DispatchInterNodeRecv data corruption on Pensando AINIC:

1. hipDeviceMallocUncached for proxy mode symmetric memory: NIC RDMA
   writes go directly to GPU VRAM, bypassing GPU L2 cache. Without this,
   the GPU reads stale L2 cache data while NIC has written fresh data to
   VRAM, corrupting dispatch metadata (expert indices).

2. PCIe read fence before signal counter atomic: RDMA_WRITE (data) and
   SEND_WITH_IMM (signal) take different paths to the receiver — data
   goes NIC→GPU VRAM via DMA, signal goes NIC→CPU recv buffer→proxy
   thread. A CPU read from GPU VRAM forces PCIe posted write ordering,
   ensuring the data DMA completes before the signal counter is
   incremented. Combined with clflush+sfence for CPU→GPU write visibility.

Result: Node 1 fully passes (Dispatch + Combine). Node 0 dispatch passes
with correct token counts. 126/128 tokens pass data correctness. 2 tokens
(token 0 with duplicate PE routing) fail — likely an EP algorithm edge
case, not a proxy issue.

Co-Authored-By: Claude <noreply@anthropic.com>
128 recv WRs was insufficient for higher-traffic routing patterns,
causing combine phase hangs when recv WRs were exhausted before
the proxy thread could re-post them.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Proxy pthreads default to GPU 0 — 7/8 threads need hipSetDevice to
access the correct GPU's VRAM for __atomic_fetch_add signal counters.
Use hipGetDevice to check first, only call hipSetDevice when needed.

Co-Authored-By: Claude <noreply@anthropic.com>
hipSetDevice is called once at thread start. The conditional check
was unnecessary noise — all proxy pthreads default to GPU 0 and
need to be set to their correct GPU.

The 2-token data corruption on node 0 is pre-existing — it occurred
in early tests before hipSetDevice was introduced. Not caused by
the proxy's HIP context setup.

Co-Authored-By: Claude <noreply@anthropic.com>
Root cause of 2-token data corruption: SEND_WITH_IMM signal and
RDMA_WRITE data travel through different PCIe initiators (CPU vs NIC).
PCIe does not order writes from different sources. The GPU sees the
CPU-written signal before the NIC's data DMA completes, reads stale
staging data, and gets garbage expert indices.

Fix: convert PROXY_ATOMIC_FETCH_ADD to IBV_WR_RDMA_WRITE instead of
IBV_WR_SEND_WITH_IMM. Both data and signal now go through the NIC,
same PCIe initiator. IBV_SEND_FENCE on the same QP guarantees the
data write completes before the signal write. No CPU writes to GPU
VRAM, no hipSetDevice, no recv WRs needed.

This is safe because there is zero contention on signal entries:
each receiver GPU's chunkFlag/nodeRecvTokenNum is written by exactly
one sender GPU (proxyPe = destNode * gpuPerNode + rank % gpuPerNode,
blockFlagCounter is per-GPU with unique flagSlotIds via atomicAdd).

Removes: SEND_WITH_IMM recv handling, hipSetDevice, recv WR posting,
ProxyRecvInfo, IBVerbsHandle recv fields, proxy uncached heap hack.
Net -120 lines.

Co-Authored-By: Claude <noreply@anthropic.com>
…_IMM for barriers

Two types of atomic operations in EP:
1. Signal writes (chunkFlag, nodeRecvTokenNum) — paired with data,
   need same NIC→PCIe→VRAM path for ordering. No contention (one
   sender per entry). New PROXY_SIGNAL_WRITE op → IBV_WR_RDMA_WRITE.
2. Barrier atomics (crossDeviceBarrier) — multiple QPs add to same
   counter, need real addition. No data ordering requirement.
   PROXY_ATOMIC_FETCH_ADD → IBV_WR_SEND_WITH_IMM + CPU atomic.

GPU kernel uses ProxyPostSignalWrite() for ShmemPutMemNbiSignalThread
(data+signal) and ProxyPostAtomicNonFetch() for standalone barrier
atomics.

Co-Authored-By: Claude <noreply@anthropic.com>
1024 slots was insufficient for 4096+ tokens — each token generates
~2 proxy commands (data write + signal write). With 4096 tokens per
GPU, ~8000 commands needed. Ring exhaustion caused the GPU to wait
for free slots while the proxy couldn't drain fast enough.

8192 slots handles up to ~4096 tokens per GPU. For bench mode
(26K tokens), may need further increase.

Co-Authored-By: Claude <noreply@anthropic.com>
65536 slots handles bench mode (~26K tokens, ~50K+ proxy commands).
Dispatch passes at all scales. Combine hangs at bench scale due to
SEND_WITH_IMM barrier atomics — some CQEs go missing under high load.

Test passes: 128, 256, 1024, 4096 tokens (dispatch + combine).
Bench (26K tokens): dispatch passes, combine stalls.

Co-Authored-By: Claude <noreply@anthropic.com>
IBV_SEND_FENCE on signal writes caused Pensando NIC to stall CQE
delivery when many signals queued on the same QP. RC QP guarantees
responder-side ordering without FENCE — data RDMA_WRITE completes
before signal RDMA_WRITE at the remote GPU.

With this fix:
- test (128-4096 tokens): Dispatch Pass + Combine Pass, all rounds
- bench (26K tokens): Dispatch Pass + Combine Pass, 10 rounds
  Dispatch: avg 9.24 GB/s RDMA, avg 13.4ms latency
  Combine: avg 34.56 GB/s RDMA, avg 3.6ms latency

Co-Authored-By: Claude <noreply@anthropic.com>
Strip post traces and stall diagnostics for clean bench runs.

Co-Authored-By: Claude <noreply@anthropic.com>
Strip all [MoRI-PROXY] debug prints from init, ionic, symmetric_memory.
Keep only error prints (CQE error, ibv_post_send failed, RECV CQE error).

Co-Authored-By: Claude <noreply@anthropic.com>
The sed-based fprintf removal left orphaned format argument lines
that caused compile errors. Clean them up properly.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
RegisterSymmMemObj was doing 8 barriers + 8 Allgathers across all PEs
for EVERY shmem_malloc call. With vLLM DeepSeek-V4 (61 MoE layers,
~31 shmem_malloc per layer = ~1900 calls), this meant ~15,000 barriers
across 16 PEs, causing MORI SHMEM init to hang for 15+ minutes.

Sub-allocations within the StaticHeap share the heap's MR — the per-NIC
rkeys from sub-allocation registrations overwrote the heap's keys and
were never used by the proxy thread (which reads perNicLkeys from the
SymmMemManager, set only by the heap registration).

Fix: gate per-NIC MR registration on heap_begin=true. Only the initial
heap allocation does the 8-NIC barrier+Allgather dance. Sub-allocations
skip it entirely.

Co-Authored-By: Claude <noreply@anthropic.com>
Tej Kiran and others added 30 commits August 12, 2026 17:15
Co-Authored-By: Claude <noreply@anthropic.com>
Warp-scope and Block-scope proxy kernel specializations were calling
the Thread-scope implementation from ALL lanes/threads, causing 64x
duplicate proxy ring posts per warp-scope call.

Match v3's behavior: WarpKernel gates on laneId==0, BlockKernel
gates on FlatBlockThreadId()==0.

Co-Authored-By: Claude <noreply@anthropic.com>
Match v3's WarpKernelImpl/BlockKernelImpl pattern: only lane 0 (Warp)
or thread 0 (Block) enters the Thread-scope proxy post. Without this,
all 64 lanes post duplicate commands — 64x more SEND_WITH_IMM atomics
flooding remote proxy threads with CQE draining, causing 20-40x
slowdown.

Co-Authored-By: Claude <noreply@anthropic.com>
Replace max(myGpu, peerGpu) % N with (myGpu + peerGpu) % N.

max() is asymmetric: rank 7 funnels all traffic through NIC 7 (1
thread), while rank 0 uses all 8 NICs (7 threads). Addition is
commutative (both sides agree) and distributes peers evenly — every
rank uses 7 of 8 NICs.

Co-Authored-By: Claude <noreply@anthropic.com>
Remove [MoRI-DBG] fprintf traces from init.cpp, runtime.cpp,
proxy_thread.cpp, symmetric_memory.cpp. Keep CQE error prints
(useful for production debugging). Restore test script to original.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Add useProxy, proxyRings[], proxyQuietHead[], numProxyRings,
numNics, localGpuIdx back into GpuStates alongside ProxyGpuStates.
First step to match v3 behavior for contention investigation.

Co-Authored-By: Claude <noreply@anthropic.com>
Proxy kernels now read rings/quietHead/numNics/localGpuIdx from
GpuStates directly instead of ProxyGpuStates. init.cpp populates
both structs. This makes proxy fields travel via the same hipMemcpy
as GpuStates (no separate symbol lookup).

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Remove ProxyGpuStates struct, globalProxyState device symbol, separate
hipMemcpy path, and all references. Proxy fields (rings, quietHead,
numProxyRings, numNics, localGpuIdx) are in GpuStates and travel via
the single existing GpuStates hipMemcpy. This eliminates the two-symbol
contention issue that caused uneven per-rank bandwidth under NIC load.

Also simplify ShmemQuietThread() no-arg to use numProxyRings > 0 check
instead of PE loop (single device memory read vs 16).

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Reference old GpuStates fields that no longer exist.

Co-Authored-By: Claude <noreply@anthropic.com>
Add proxy QP creation and connection to Mlx5DeviceContext, mirroring
the existing IonicDeviceContext proxy path. When MORI_EP_OVER_RDMA=1:

- CreateRdmaEndpoint uses ibv_create_cq + ibv_create_qp (plain verbs,
  not DevX) and stores in proxyQpPool
- ConnectEndpoint uses ibv_modify_qp RST→INIT→RTR→RTS and pre-posts
  512 recv WRs for SEND_WITH_IMM atomic emulation
- context.cpp dynamic_cast handles both IonicDeviceContext and
  Mlx5DeviceContext for GetProxyRecvInfo

CX7 on MI300x has 8 mlx5 NICs — same topology as AINIC (8 proxy
threads, 8 rings, agreedRail spreads QPs across all NICs).

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Traces in proxy_thread.cpp (commands posted, errors, null QP) and
init.cpp (per-NIC QP count, total threads).

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…during hang

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
CX7 supports IBV_WR_ATOMIC_FETCH_AND_ADD — NIC hardware does the atomic
directly to GPU VRAM. Ionic doesn't, so it keeps SEND_WITH_IMM emulation
where CPU does __atomic_fetch_add.

Add use_native_atomics flag to ProxyQpHandle, set true for Mellanox
vendor, false for Pensando. BuildWr checks the flag per QP.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
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.

1 participant