Repository navigation
[GSD-12874] [BMG / Arc Pro B70] Is cross-root-port P2P expected? L0 dev-to-dev copy returns 0x70000003; kernel refuses at P2PDMA (host bridge not whitelisted) — and can the capability be made queryable? #935
Description
Pre-submission Checklist
- I have searched existing issues and discussions for similar questions
- I have checked the documentation and FAQ / programmers-guide
Question Category
API / Development
Other Category
No response
Question
English isn't my first language, so I drafted this with AI help — but every result was measured by me on the hardware described. Full writeup +
reproducer: https://github.com/TSUMUGI-XE/b70-dual-tp2
Confound ruled out (control done). The test host normally runs pcie_acs_override=downstream,multifunction (for an unrelated VFIO passthrough),
which can make the kernel refuse peer P2P via pci_p2pdma_distance() and mimic this signature. I re-ran on a control boot with the override
removed: peer copy still returns 0x70000003, host-staged still passes — identical to baseline (control
log). The override is not the cause. On that boot the
kernel logged the reason: xe 0000:07:00.0: cannot be used for peer-to-peer DMA as the client and provider (0000:03:00.0) do not share an upstream bridge or whitelisted host bridge.
Summary
On a workstation with two Intel Arc Pro B70 (BMG), a Level-Zero direct device-to-device copy between the cards (single process, per-device L0
contexts) returns 0x70000003 (ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY) instead of doing the peer copy. Clean error, not a hang. A host-staged
copy of the same buffers (dev0 → host → dev1) succeeds — so allocations/queues are fine, only the cross-device peer path fails.
This may well be expected behavior. I ruled out my own host config (control above); the kernel then refuses because the two cards are on separate
root ports with no shared PCIe switch and the host bridge isn't on pci_p2pdma_whitelist. So I'm not claiming a clear bug — I'm asking: (1) should
BMG + a consumer Intel host bridge route P2P through the root complex (whitelist gap to fix, or topology limit to document)? and (2) regardless, can
the unsupported case be reported as a queryable capability instead of OUT_OF_DEVICE_MEMORY, so frameworks stop probing-by-failure?
Environment
- GPUs: 2× Intel Arc Pro B70 (BMG), separate PCIe root ports
- Kernel: Linux 7.x, xe driver — reproduced on two builds (stock Ubuntu HWE 7.0.0-22 and a self-built 7.1-rc)
- compute-runtime / Level-Zero: 1.15.x (NEO 26.x, IGC 2.34.x)
- Single process,
ONEAPI_DEVICE_SELECTOR=level_zero:*,ZES_ENABLE_SYSMAN=1; IOMMU/VT-d on, GPUs in separate IOMMU groups
Reproducer
~80-line single file: repro/b70_p2p_copy_probe.cpp. Build/run:
source /opt/intel/oneapi/setvars.sh
icpx -fsycl -O2 b70_p2p_copy_probe.cpp -lze_loader -o b70_p2p_copy_probe
ONEAPI_DEVICE_SELECTOR=level_zero:* ZES_ENABLE_SYSMAN=1 ./b70_p2p_copy_probe
Output:
--- TEST 1: L0 direct copy dev0 -> dev1 ---
zeCommandListAppendMemoryCopy -> 0x70000003 (FAILED)
--- TEST 2: host-staged copy dev0 -> host -> dev1 ---
checksum: PASS
RESULT: l0_p2p=BROKEN(ze-error) host_staged=WORKS
Negative controls (what is NOT the cause)
- Host platform swap (different CPU/chipset, separate root ports, Gen4 x8/x8, separate IOMMU groups) → identical failure. PCIe bandwidth / root-port
layout / IOMMU grouping ruled out. Reproduces across two kernels. pcie_acs_overrideremoved control boot → still0x70000003, kernel names the P2PDMA refusal (above). Override ruled out. Neither GPU exposes a
p2pdmasysfs node even with ACS off.
Related (not duplicates — different HW or failure mode)
- intel/compute-runtime [GSD-12641] USM device allocation fails with UR_RESULT_ERROR_OUT_OF_DEVICE_MEMORY when Level Zero context spans 2 discrete Arc A770 GPUs (regression from 25.40 to 26.09) #916 — same
OUT_OF_DEVICE_MEMORYclass on a 2-GPU Level-Zero context, but A770 (DG2) andurUSMDeviceAlloc, not a peer copy. - intel/compute-runtime [GSD-12696] [Xe2/BMG-G31] multi-rank MPI + Level Zero: resource_info.cpp:15 abort after upgrading CR 26.05 → 26.14 (Ubuntu 26.04, xe driver) #922 — same dual-B70 (BMG-G31) multi-GPU Level-Zero frontier, but a multi-process
zeInitabort, not a single-process peer
copy. - vllm-project/vllm #41663 — the downstream TP=2 symptom on the same hardware.
Ask
- Is BMG + a consumer Intel host bridge eligible for
pci_p2pdma_whitelist(does the silicon route P2P through the root complex), or is
cross-root-port P2P genuinely unsupported here? Whitelist it, or document it? - Either way, can compute-runtime report the capability (a
zeDeviceCanAccessPeer-style query) so frameworks choose host-staging deliberately
instead of probing-by-failure? - Is
0x70000003(OUT_OF_DEVICE_MEMORY) the intended code for "peer access refused by the kernel P2PDMA layer", or does it mask the more specific
reason the kernel already names?
Happy to run additional diagnostics or driver builds — I have the two B70 set up and can iterate.
Additional Notes
No response
Activity
[-][BMG / Arc Pro B70] Is cross-root-port P2P expected? L0 dev-to-dev copy returns 0x70000003; kernel refuses at P2PDMA (host bridge not whitelisted) — and can the capability be made queryable?[/-][+][GSD-12874] [BMG / Arc Pro B70] Is cross-root-port P2P expected? L0 dev-to-dev copy returns 0x70000003; kernel refuses at P2PDMA (host bridge not whitelisted) — and can the capability be made queryable?[/+]mjsabby commentedon Jun 19, 2026
Do we have an answer from intel on this?
TSUMUGI-XE commentedon Jun 19, 2026
Follow-up — and to @mjsabby's question above: no technical reply from Intel on my side yet (it's tracked internally as
[GSD-12874]), but I have a concrete new result that I think answers my own Ask #1, so I'll add it here.Short version: I forced the consumer host bridge into
pci_p2pdma_whitelistto see if it's eligible. The kernel then allows the peer copy, but the root complex silently corrupts it — the command returns SUCCESS and the data is wrong.Setup. Same 2× Arc Pro B70 (BMG), on separate CPU root ports, consumer Rocket Lake-S host bridge
8086:4c43. I built a kernel with that host bridge added topci_p2pdma_whitelist[]and booted withintel_iommu=off(headless, GPUs otherwise idle).Result.
cannot be used for peer-to-peer DMA ... do not share an upstream bridge or whitelisted host bridgerefusal is gone, and a direct dev0→dev1zeCommandListAppendMemoryCopyreturnsZE_RESULT_SUCCESS(0x0) instead of0x70000003.Interpretation. Forcing the whitelist makes the kernel allow the mapping, but the RKL-S root complex doesn't correctly route the cross-root-port peer write. The L0/
xeside does issue the copy (no45/39, no capability rejection) — the failure is purely in the peer data path through the root complex. This is consistent withpci_p2pdma_whitelistdeliberately excluding consumer client host bridges.For contrast (not measured by me, just to scope it): Intel's own Platform Evaluation Guide for Multi-ARC notes no expected P2P restriction on AMD hosts, and there are community reports of multi-Arc P2P working on AMD EPYC. So this looks specific to consumer Intel single-IIO host bridges, not Arc P2P in general — Xeon/validated platforms are a separate case I'm not commenting on.
Two practical questions:
ZE_RESULT_SUCCESS+ corrupt data rather than a clean refusal, can the stack detect/refuse this case (a queryable peer-access capability, or a hard error) so a user can't unknowingly run a workload on a path that returns wrong results? Silent corruption is worse than the originalOUT_OF_DEVICE_MEMORY.I can share the one-line whitelist patch, the ~80-line probe, and the sanitized boot log if any of that is useful.
(English isn't my first language, so I drafted this with AI help — but every result above was measured by me on the hardware described.)
mjsabby commentedon Jun 19, 2026
Thanks @TSUMUGI-XE. I commented on your repo asking a similar question. Is it worth checking out consumer AMD platform and seeing if it works.
You also specifically call out cross root. Have you tried in a single root and it DOES work or also doesn't work? In other words is there any kernel driver work needed here? I will also clone this repo and ask Claude.
I think it will be tough for Intel to say or reverse course on the fact they themselves probably didn't allow list their client skus for p2p dma.
That said it is concerning that your est did not pass when you have server class skus.
TSUMUGI-XE commentedon Jun 19, 2026
Thanks @mjsabby — good questions.
Single-root: haven't been able to test it yet, and that's the key gap. Both B70s sit on separate CPU root ports, so I can't make them single-root without changing topology. Linux
pci_p2pdmaalready allows P2P unconditionally when two devices share an upstream PCIe switch — no driver work needed there. The refusal is specific to cross-root-port (no shared switch, host bridge not whitelisted). So I'd expect single-root / behind-a-switch to work at the kernel layer; the open question is whether the data then routes. I'm getting a PEX88096 switch board to put both cards under one upstream bridge and will postze_peernumbers."Kernel driver work needed?" For the shared-switch case, no — the path is already there. For cross-root-port on this consumer host, forcing it (whitelisting the host bridge) produces silent corruption, so the kernel's refusal is correct — it's not a missing driver, it's the root complex not routing peer TLPs.
On the SKU point: the GPUs aren't the problem — the B70s issue the copy and host-staged works. The failing piece is the consumer host's root complex (Rocket Lake-S,
8086:4c43), which isn't inpci_p2pdma_whitelist. On a whitelisted/server host (the list has Skylake-E / Xeon Scalable) or behind a switch, the same GPUs should route P2P.AMD: agreed, worth checking — I'm on an Intel host so can't test it myself, but Intel's own Multi-ARC guide notes no expected P2P restriction on AMD hosts, and there are community reports of multi-Arc P2P on EPYC. So this looks like a consumer-Intel-root-complex thing, not an Arc-wide limit.
Version caveat (in fairness): my data is from kernel 7.0.0-22 — I stay on 7.0 because 7.1 regresses my Wi-Fi — and I haven't tested released 7.1 or the latest UMF myself. I know it's been reported that the dual-B70 TP=2 path needs Linux ≥7.1; worth noting the one clean working setup I've seen is a Threadripper HEDT with multiple root complexes (TP=2 works across different RCs there, but still crashes on the same RC). The 7.1
xechangelog shows no P2P/multi-GPU changes, so I don't expect a kernel bump alone to flip the cross-root-port verdict on a consumer single-root-complex host — but I'll confirm that with the switch rather than assume it.Separately — I have a host-staged TP=2 workaround (routes the all-reduce through CPU/gloo so it never touches GPU P2P; works regardless of root-complex topology) plus the
ze_peer/checksum +pci_p2pdmadistance/whitelist probe scripts I used to characterize all this. Perf is modest but real: on a dense 27B it ran ~1.2x single-stream vs a single GPU and scaled with batch; on an MoE it was capacity-only (single-stream was actually slower than one card). Happy to share both if useful — heads up, I'm traveling for a bit, so I'll post the cleaned-up patch + scripts once I'm back.Will report back once the switch is in.
mjsabby commentedon Jun 19, 2026
Yes, your repo was very useful to me for investigations, so please continue. The goal is to not through CPU/gloo for me, but yes your experiment proves it is somewhat usable, although I suspect Intel probably wants to get it fixed.
BTW, you don't need EPYC right? All zen chipsets are allow listed.
mjsabby commentedon Jun 19, 2026
Another quesiton to ponder, if Windows supports it, given Intel controls the drivers there more strongly than having to wait for upstream Kernel.
TSUMUGI-XE commentedon Jun 19, 2026
Quick follow-up on the AMD question — I went and checked the actual Linux
pci_p2pdmawhitelist (drivers/pci/p2pdma.c), and it's asymmetric in a way that's directly relevant:if (cpu_vendor == AMD && family >= 0x17)⇒ any Zen-or-newer host is trusted for p2pdma, no per-device list.8086:4c43) aren't in it, so the distance check returns -1 and P2P is refused up front.So on a Zen+ box your two B70s should clear the kernel gate that mine fails — and the kernel only blanket-trusts AMD because the fabric actually routes peer TLPs (vs. my consumer Intel RC, which even when forced open returns the buffer as all-1s = a clean Unsupported-Request refusal, not a transfer). That makes consumer AMD a genuinely worthwhile test. If you try it, I'd love to know whether
p2p_accessflips to 1 and a real cross-card copy verifies (not just returns success).I'm taking the other branch: a PEX (PLX) switch on my Intel box to put both cards under one shared upstream bridge and bypass the root complex entirely — but it's ~2-3 weeks out. So let's split it: you on AMD, me on the switch, and compare notes. Whichever lands first tells us a lot.
mjsabby commentedon Jun 19, 2026
@TSUMUGI-XE yes I'm going to try with AMD host, your repo's code should be a quick minimal repro to test right?
TSUMUGI-XE commentedon Jun 19, 2026
Quick answers:
Repro: yes — it's a single ~85-line SYCL/Level-Zero file (
repro/b70_p2p_copy_probe.cpp), buildicpx -fsycl -O2 ... -lze_loader, runs in seconds. On AMD the key check is that the copy verifies (contents match), not just that it returns success — on my consumer Intel box the call "succeeds" but the destination comes back all-1s (a PCIe Unsupported-Request, i.e. the RC didn't actually move the bytes). So the thing to watch: doeszeDeviceCanAccessPeer/p2p_accessreport 1, and does the copied buffer match the source?Windows: I don't think it escapes the wall on consumer Intel. The all-1s is a hardware Unsupported-Request from the root complex — that's below the OS, so a Windows driver (even with Intel's tighter control there) hits the same silicon that won't route peer TLPs across root ports. Where the RC does route (AMD Zen / Xeon / behind a PCIe switch), Windows P2P should be fine — but Linux works there too. So Windows is a driver-exposure question, not a fix for the consumer-Intel-RC routing limit.
Looking forward to your AMD numbers — that's the cleanest test of the whole thing.
mjsabby commentedon Jun 28, 2026
AM5 works for P2P, I guess the best setup for developers not looking to fork $$$ on NVIDIA is use AMD CPUs and Intel GPUs. What a world!
TSUMUGI-XE commentedon Jun 28, 2026
Does putting both B70 under a single PCIe switch route cross-card P2P on a consumer Intel host?
Following up. On my consumer Intel host (Rocket Lake-S,
8086:4c43) cross-card B70 P2P doesn't work: the kernel refuses it (0x70000003/pci_p2pdma_distance= −1 — consumer Intel RCs aren't in the whitelist), and forcing the whitelist open returnsZE_RESULT_SUCCESSbut corrupt data. The same two cards verify clean cross-card P2P on an AMD host (third-party, checksum PASS both directions) — so this is the consumer Intel root complex, not the cards.My plan is a PCIe switch to put both cards under one shared upstream bridge, so peer TLPs route within the switch and never reach the root complex (per the kernel p2pdma docs: two devices under a common switch → distance 4 →
PCI_P2PDMA_MAP_BUS_ADDR).Question: is that the expected-to-work topology for B70↔B70 P2P on a consumer Intel host — and is there any ACS caveat I should clear on the switch (e.g. a downstream-port ACS redirect forcing traffic back through the host bridge)? A confirmation would de-risk it before I test the hardware — and once both cards sit under the switch, will
zeDeviceCanAccessPeerreport capable and the copy route correctly? A one-line note of the supported topology in the docs would also save the next person.TSUMUGI-XE commentedon Jul 6, 2026
Switch result: B70↔B70 P2P verifies clean on a consumer Intel host — but only with ACS redirect off on the switch
Following up on my own switch question with hardware in hand. I re-homed both B70 under a single Broadcom/LSI PEX880xx (Gen4) switch — both cards on downstream ports of one shared upstream bridge — on the same consumer Intel host (Rocket Lake-S,
8086:4c43). Upstream to the root complex is Gen4 x16.lspci -tv(both cards under one upstream01:00.0):The switch topology alone was not sufficient — there's a second gate: ACS redirect on the switch's own downstream ports. Both states, same cards, same box:
ACS redirect enabled (the switch's power-on default):
0xFFFFFFFF— the exact same silent corruption as the bare consumer RC. The switch hasReqRedir+/CmpltRedir+, so peer TLPs are redirected up to the root complex and corrupted there.ACS redirect cleared on the switch downstream ports (
setpci ECAP_ACSControl = 0):0xABover 256 MiB,268435456 / 268435456bytes correct).dmesgclean — no0x70000003, no engine reset.ze_peer, both cards under the switch, 256 MB):i.e. ~28.5 GB/s per direction, and ~56 GB/s aggregate with both directions running at once (Gen4 x16 upstream) — the same order as the AM5 third-party numbers earlier in the thread (~28.6 GB/s/dir there on Gen5 x8), and ~9× the host-staged path this box falls back to when P2P is refused. The bidirectional figure is the one that matters for TP=2 all-reduce.
To be clear, that's the switch's physical ACS, not the kernel
pcie_acs_override=knob (which only changes IOMMU grouping, not how the switch routes TLPs). So the working recipe on a consumer Intel host is: both cards under a common PCIe switch and ACS redirect disabled on the switch downstream ports. With either one missing you getZE_RESULT_SUCCESS+ silently corrupt data.This matches the AMD third-party datapoint from earlier in the thread (clean on Zen, corrupt on bare consumer Intel): the consumer RC is what mangles peer writes, and keeping the TLPs switch-local (ACS-off) sidesteps it entirely.
Follow-through: a real TP=2 all-reduce workload runs cleanly on this path. With the switch-local ACS-off peer path confirmed clean, I ran an actual tensor-parallel workload over it — vLLM serving a 27B model with
tensor-parallel-size=2across the two B70 (XCCL / oneCCL peer all-reduce). It initializes in ~90 s, serves, and a concurrency sweep completes 100% with clean output and no hang or engine reset. With CUDA-graph decode it sustains real serving throughput — up to ~919 tok/s aggregate at N=64, roughly 2.5× the same model on a single card:So beyond the raw copy benchmark, the clean switch-local peer path carries a real collective serving workload correctly — which is exactly why the routing question matters: with the corruption present (ACS-on), that same collective would be silently operating on mangled peer data.
(Orthogonal aside, in case it saves someone time: getting the collective to start on this many-interface host also required pinning oneCCL's OFI/TCP bootstrap to loopback —
FI_TCP_IFACE=lo— a oneCCL/libfabric interface-selection quirk unrelated to the peer-TLP routing question here.)Which brings the original ask back, sharper: at every stage before the fix,
zeCommandListAppendMemoryCopyreturnedZE_RESULT_SUCCESSon data that was silently wrong — nothing in the API distinguished "peer copy landed" from "peer copy got redirected and corrupted." A queryable capability — does this device→device path actually route peer TLPs on this platform/topology? — would let a runtime (or oneCCL) refuse or fall back instead of returning success on garbage. Is that detectable today from L0/the driver, or is silent-corruption-on-unsupported-topology the current expected behavior?(Happy to share the repro + both checksum logs if useful.)
ola2308 commentedon Aug 12, 2026
@TSUMUGI-XE thank you for the detailed investigation, and apologies for the delayed response.
Your results indicate that P2P works when both B70 GPUs share a PCIe switch and ACS redirect remains disabled. However, with ACS redirect enabled, pci_p2pdma_distance() should reject the path through the non-whitelisted host bridge.
Could you run p2p-distance with ACS redirect enabled on the external switch and share the full output? Please also confirm that the kernel used for this test did not contain the host-bridge whitelist modification or another whitelist bypass.
I agree that ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORY is misleading when peer access cannot be established. We are investigating this error path so that an unsupported P2P configuration can be reported more accurately.
TSUMUGI-XE commentedon Aug 13, 2026
@ola2308 Thanks — I ran
p2p-distancein both ACS states on the same box. Your expectation isexactly what it reports, and the result does not depend on any whitelist modification.
Both B70 sit under one PLX switch, so their common upstream bridge is the switch (
0000:0a:00.0),not the root complex.
ACS redirect ENABLED (
ECAP_ACS+0x6.w=000con every ReqRedir-capable bridge):ACS redirect DISABLED (switch downstream ports cleared — the configuration my
ze_peerPASS wasmeasured in):
So
pci_p2pdma_distance()behaves as you described: with redirect on, the path falls back to thehost bridge,
8086:4c43is not whitelisted, and the verdict isNOT_SUPPORTED. P2P is only possiblebecause the traffic stays switch-local.
On the whitelist question:
p2p-distanceimplements the stockpci_p2pdma_whitelist[](no8086:4c43entry) and printshost bridge 0000:00:00.0 (8086:4c43): NOT whitelistedin theredirect-on run — so the
BUS_ADDRverdict above comes from the shared switch bridge, not from awhitelist entry. In the interest of full disclosure: I do have a local kernel build that adds
8086:4c43to that table, from an earlier experiment testing whether the consumer RC could routepeer TLPs at all (it could not — the data came back silently corrupted, which is what led me to the
switch). It is irrelevant to the switch-local path, but I'm happy to re-run both measurements on a
stock distro kernel if you want that removed as a variable.
Thanks for looking at the
ZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORYpath — a distinct "peer access notsupported in this topology" error would have saved a lot of guessing here.
Full
p2p-distancerun (ACS on → measure → restore → verify)thellstrom-os commentedon Aug 18, 2026
@TSUMUGI-XE What we need to verify is that we don't get the silent corruption with a stock kernel and the local switch in ACS redirect mode. Instead p2pdma should be refused. The theory is that you see the silent corruption in the ACS redirect enabled case because even though you have a local switch, ACS redirects traffic through the host bridge which doesn't support it. So essentially you see the same error as without the switch.
TSUMUGI-XE commentedon Aug 18, 2026
@thellstrom-os Done — I re-ran it on a stock kernel, and your expectation holds.
Booted stock
7.0.0-22-generic(no whitelist patch), re-enabled ACS redirect at runtime withsetpcion the switch bridges, and ran both states on that one boot:pci_p2pdma_distancePCI_P2PDMA_MAP_NOT_SUPPORTEDPCI_P2PDMA_MAP_BUS_ADDR, distance 8zeDeviceCanAccessPeerZE_RESULT_ERROR_OUT_OF_DEVICE_MEMORYNo silent corruption in the redirect case — it is a clean refusal, which matches your reading that
ACS sends the traffic back through the host bridge.
Two notes on my earlier posts, since both could mislead:
pcie_acs_override=is on my cmdline but is ano-op on this build; a systemd unit clears the redirect with
setpciafter boot. So the redirectis active during early boot, which is where the
xerefusal message comes from.NOT_SUPPORTEDverdict I posted on Aug 13 came fromp2p-distance, which models the stockpci_p2pdma_whitelist[]in userspace — not from my patched kernel's own decision.That answers both halves of my original question: the refusal is expected, and
zeDeviceCanAccessPeerdoes report it. Happy for this to be closed.ola2308 commentedon Aug 20, 2026
@TSUMUGI-XE thank you for the thorough testing and for confirming the behavior on stock kernel. We're glad that the silent corruption isn't present there and that fallback is working correctly as expected.
Since your original question have been answered and you're good with the current state, I'll go ahead and close this issue. Please feel free to reach out or open a new issue if you have any further questions or run into any other problem in future.