Skip to content

R9700 (Thunderbolt eGPU) as hip:0 source: hipMemcpyPeer SIGSEGV, blocks dual-GPU DS4 expert parallelism on Strix Halo pairing #681

Description

@chrisjohnson

Summary

hipMemcpyPeer segfaults (SIGSEGV, exit 139) specifically when an R9700 attached via a Thunderbolt/USB4 eGPU dock is the source device (hip:0) copying to an integrated Strix Halo APU (hip:1). The reverse direction (Strix Halo as source) works cleanly every time. This blocks the dual-GPU asymmetric expert parallelism DS4 path (dflash_server with DFLASH_DS4_MOE_TP_INPROC=1), which requires the discrete GPU as the initiating hip:0 device.

Isolated with a minimal ~90-line standalone HIP program — zero Lucebox code — so this looks like a box/driver-level HIP peer-access defect specific to this eGPU/iGPU pairing, not an application bug. Filing here first since the repro was found while qualifying this exact hardware pairing against DS4.md's documented setup, and to check whether your own reference R9700+Strix Halo machine uses a native PCIe slot or an eGPU dock — that topology difference may be the actual variable.

Environment

  • AMD Ryzen AI Max+ 395 (Strix Halo), gfx1151, 128GB unified memory
  • AMD Radeon AI PRO R9700, gfx1201, 32GB GDDR6, attached via a Thunderbolt/USB4 eGPU dock (not a native PCIe slot)
  • ROCm 6.4.1, HIP backend, ghcr.io/luce-org/lucebox-hub:rocm image (also reproduced with a bare hipcc-compiled standalone program, no container-specific dependency)
  • Dual-arch dflash_server build: -DDFLASH27B_HIP_ARCHITECTURES='gfx1151;gfx1201' -DGGML_HIP_GRAPHS=ON -DCMAKE_BUILD_TYPE=Release
  • Models: DeepSeek-V4-Flash-ROCMFP2-STRIX.gguf (102.3GB) + DeepSeek-V4-Flash-DSpark-draft-Q4RMFP4-denseF16.gguf (11.3GB), matching DS4.md's documented "qualified R9700 + Strix Halo machine" setup exactly

What happens with dflash_server

With container HIP device ordering corrected so the R9700 lands on hip:0 (HIP_VISIBLE_DEVICES=1,0 — see note below on why this isn't 0,1), the server gets well into real placement logic:

[deepseek4] hybrid placement: gpu_total=31.86 GiB gpu_free=28.14 GiB core=3.72 GiB kv=0.89 GiB
  warm=0.25 GiB safety=0.50 GiB expert_budget=11.43 GiB hot/layer=32
[deepseek4] placement expert_memory: total=91.38 GiB uniform_round=365.50 MiB hot=1376 11.42 GiB
  cold=9632 79.95 GiB
[deepseek4-moe-tp] peer access hip:0 <-> hip:1: enabled

— then segfaults immediately after peer access is reported enabled, before any cross-device data transfer completes and before any measurable VRAM increase. Reproduced twice, identically: once via serve_ds4_dual_rocm_128k.sh, once invoking dflash_server directly with DS4.md's minimal-activation env vars (omitting --peer-access didn't help — DFLASH_DS4_MOE_TP_INPROC=1 triggers cross-device access internally regardless of that flag).

No stack trace on stdout (release build, no signal handler). coredumpctl shows SIGSEGV inaccessible for dflash_server at both crash timestamps. GPUs are fully healthy immediately after every crash (rocm-smi --showuse 0% busy both, no fault/IOMMU/DMAR entries in journalctl -k).

Minimal repro, no Lucebox code

To rule out an application bug, wrote a ~90-line standalone HIP C++ program: hipGetDeviceCount → per-device hipGetDevicePropertieshipDeviceCanAccessPeer both directions → hipSetDevice/hipDeviceEnablePeerAccess both directions → hipMalloc on each device → hipMemcpyPeer (1MB) → exit, with fflush after every step. Compiled with hipcc --offload-arch=gfx1151 --offload-arch=gfx1201.

Each ordering run twice (4 runs total), 100% reproducible both times:

  • HIP_VISIBLE_DEVICES=1,0 (R9700 on hip:0, Strix on hip:1): crashes, SIGSEGV, both runs. Device enumeration, hipDeviceCanAccessPeer (both directions report 1), hipSetDevice, and both hipDeviceEnablePeerAccess calls (both report success) all print cleanly. Crash happens inside/immediately around hipMemcpyPeer(d1 <- d0, 1MB) — the last line printed both times was Attempting hipMemcpyPeer(d1 <- d0, 1MB)...; the result line never appears.
  • HIP_VISIBLE_DEVICES=0,1 (Strix on hip:0, R9700 on hip:1): succeeds cleanly, exit 0, both runs. hipMemcpyPeer -> no error and TEST COMPLETED WITHOUT CRASHING. both print.

Both orderings report hipDeviceCanAccessPeer = 1 in both directions and both report hipDeviceEnablePeerAccess succeeding in both directions — the crash isn't caught by either capability check, only by an actual hipMemcpyPeer data transfer, and only when the R9700 initiates it.

coredumpctl confirms the identical SIGSEGV inaccessible signature as the three dflash_server crashes above, at the exact timestamps of the two HIP_VISIBLE_DEVICES=1,0 runs.

Note: container HIP device ordering is inverted from host rocm-smi ordering (unrelated but adjacent gotcha)

Host-level rocm-smi --showproductname shows GPU[0] = R9700, GPU[1] = Strix Halo. Inside a container, KFD topology node numbering puts Strix Halo at HIP device 0 and R9700 at HIP device 1, regardless of --device bind-mount order — this is a kernel-global property. So HIP_VISIBLE_DEVICES=0,1 (matching host-level GPU numbering) actually puts the 128GB unified Strix Halo card on hip:0, not the R9700 the DS4 script expects there — worth calling out in DS4.md if it isn't already, since it produces an immediate, different, and more confusing crash (right after model metadata, before real placement logic) than the peer-DMA bug above.

Question for maintainers

Is your own qualified R9700 + Strix Halo reference machine using a native PCIe slot for the R9700, or an eGPU dock (Thunderbolt/USB4/OCuLink)? DS4.md doesn't say. My working hypothesis is that peer DMA between a Thunderbolt-tunneled discrete GPU and an integrated GPU is an architecturally unusual path that may not be exercised by your own qualification hardware if it's native PCIe — happy to help narrow this down further (e.g. testing with DFLASH_DS4_MOE_TP_INPROC disabled, or with a smaller allocation) if useful, or to move the R9700 to a native M.2 slot if one becomes available, to confirm whether topology is the actual variable.

Full writeup with additional log excerpts and methodology available on request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions