Skip to content

feat(rocr/libhsakmt/dxg): map DXG system memory across GPUs - #13442

Merged
renjithravindrankannath merged 7 commits into
developfrom
users/fcui/wsl_mgpu
Nov 1, 2026
Merged

renjithravindrankannath merged 7 commits into
developfrom
users/fcui/wsl_mgpu

Conversation

@fcui-amd

Copy link
Copy Markdown
Contributor

Motivation

Map DXG system memory onto multiple GPUs. hsaKmtMemoryVaMap and hsaKmtMemoryVaUnmap submit the page-table update to the device selected by NodeId and wait only on that device's paging fence.

Only system memory can be mapped across GPUs. Peer VRAM mapping is not supported. GpuMemory::MapFromPeerDev returns UnSupported for local VRAM. Mapping local VRAM onto another GPU fails and is rolled back. Single-GPU behavior is unchanged.

Technical Details

  • Every enumerated GPU is recorded in the default node list. Callers that still need one GPU use the first entry.
  • System memory, user pointers, and dmabuf imports can be mapped onto the requested GPU nodes. A peer view uses the owner's VA and does not free that reservation.
  • hsaKmtMemoryVaMap and hsaKmtMemoryVaUnmap update the page tables on the target device.

Issue Tracking

Test Plan

import torch
torch.cuda.set_device(0)
x = torch.randn(5000, 5000, device='cuda:0')
y = torch.matmul(x, x)

torch.cuda.set_device(1)
x = torch.randn(100, 100, device='cuda:1')

Test Result

test pass. no segfault or hang

Submission Checklist

@therock-pr-bot

therock-pr-bot Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

❌ PR Check — Action Required

Check Status Details
📝 PR Description ❌ Fail Error: PR description must reference a JIRA ID, ISSUE ID, or a GitHub closing keyword.
Expected: include a JIRA ID / ISSUE ID line (separator : or -, or omitted; value may be a JIRA key, a number with/without #, or a link), OR a closing keyword + issue reference. Accepted examples:
• JIRA ID : TESTAUTO-6039
• JIRA ID - #330
• JIRA ID #330
• JIRA ID (on separate line)
ROCM-25757
• ISSUE ID : TESTUTO-3334
• ISSUE ID #3334
• ISSUE ID - TESTAUTO-3433
• ISSUE ID (on separate line)
AIRUNTIME-2352
• ISSUE ID : https://github.com/<org_name>/<repo_name>/issues/1234
• Closes #10
• Fixes octo-org/octo-repo#100
• Resolves: #123
• #123
• https://github.com/<org_name>/<repo_name>/issues/123
Current: no valid JIRA/ISSUE/closing-keyword reference found
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ⚠️ Warning Error: Source/code files changed without an accompanying unit test.
Expected: add at least one test file named like test_<name>.py / test_<name>.cpp (or <name>_test.*).
Current: code file(s) changed: projects/rocr-runtime/libhsakmt/include/impl/wddm/gpu_memory.h, projects/rocr-runtime/libhsakmt/src/dxg/libdrm.cpp, projects/rocr-runtime/libhsakmt/src/dxg/librocdxg.h, projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp, projects/rocr-runtime/libhsakmt/src/dxg/openclose.cpp (+2 more); no test file found
🔎 pre-commit ⏳ Pending ⏳ Still running…
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —

⚠️ 1 policy check(s) failed. Please address the issues above before this PR can be Reviewed.

🚫 Please fix the failed policies

  • ❌ PR Description

The Not ready to Review label was added to this PR. Once all policies pass, the label is removed automatically.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

🙋 Wish to Override Policy?

@therock-pr-bot

Copy link
Copy Markdown

🚫 Please fix the failed policies before requesting reviews.

The following policy checks failed:

  • ❌ PR Description

The Not ready to Review label has been added to this PR.
Once all policies pass, the label will be removed automatically.

Map system and user memory onto another adapter at the owner's VA. The peer view does not free that reservation.
Record each GPU's HSA node when the topology snapshot is taken. Callers that still need a single device use the first entry.
When the caller does not name a node, map system memory, user pointers, and dmabuf imports onto every default GPU at the owner's VA.
Submit the page-table update to the device named by NodeId, and use that device's peer view when it does not own the allocation.
@fcui-amd
fcui-amd force-pushed the users/fcui/wsl_mgpu branch from 982e7fd to fdd7881 Compare October 20, 2026 06:50
@fcui-amd

Copy link
Copy Markdown
Contributor Author

ping?

@peymanr

peymanr commented Oct 31, 2026

Copy link
Copy Markdown

PR Pundit review

(Testing PR Pundit Code Review agent. Not all comments may be relevant. This is a one-time automated pass: please address what is useful, no need to request a re-review. A quick reply per point (useful or not, and why) helps us calibrate the agent. Reach out to @peymanr for suggestions and feedback.)

# Finding Where
1 🔴 Multi-GPU DXG: mapping VRAM/dma-buf to default nodes fails, this PR fans default_node to all GPUs plus VRAM-unsupported peer views projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp:921
2 🟠 VaUnmap leaves stale peer view in peers_: this PR does not clear peer bookkeeping on unmap, causing silent no-op remap and double-unmap projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp:1320-1345
3 🟠 Cross-GPU peer map plus paging-fence wait runs under process-wide allocation_map_lock_, serializing other threads' memory calls projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp:636-654,704,720,965
4 🟠 DropPeer evicts and deletes peer while its unmap may still be pending: this PR omits the paging-fence wait VaUnmap performs projects/rocr-runtime/libhsakmt/src/dxg/wddm/gpu_memory.cpp:904-919
5 🟠 Imported system-memory peer view may not be backed by the resource's pages: this PR copies owner desc_ wholesale in MapFromPeer projects/rocr-runtime/libhsakmt/src/dxg/wddm/gpu_memory.cpp:926-956

1. 🔴 Blocking: Multi-GPU DXG: mapping VRAM/dma-buf to default nodes fails, this PR fans default_node to all GPUs plus VRAM-unsupported peer views

projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp:921

On multi-GPU DXG/WSL hosts, default_node now holds every adapter and all map/register paths (including the GTT/Local reuse branch and no-node imports) run map_to_nodes, but MapFromPeer returns UnSupported for non-kSystem/kUserMemory. So ViewOn yields nullptr on every non-owner node and mapping any VRAM allocation (scratch, AllowAccess, no-agent VRAM IPC attach) returns HSAKMT_STATUS_ERROR/OUT_OF_RESOURCES/HSA_STATUS_ERROR_INVALID_ARGUMENT where it succeeded before. A kSystem import with no VA also gets peers built from a desc with gpu_addr==0/no cpu_addr. Restrict peer creation to system/user memory and keep single-node behaviour when no VA is requested.

How it is reached: Multi-GPU DXG (Windows) system: hipDeviceEnablePeerAccess / hsa_amd_agents_allow_access on a VRAM pointer -> MemoryRegion::AllowAccess (local region, whitelist = owner + peer GPUs) -> KfdDriver::MakeMemoryResident -> hsaKmtMapMemoryToGPUNodes -> FindAllocation hits a non-userptr kLocal allocation -> map_to_nodes (memory.cpp:921) -> GpuMemory::ViewOn(peer) -> MapFromPeer returns UnSupported -> HSAKMT_STATUS_ERROR. Likewise GpuAgent scratch/reserved MakeMemoryResident -> hsaKmtMapMemoryToGPU with default_node = all GPUs -> same branch on kLocal scratch memory.

Who is affected: Every user of ROCm on a Windows/DXG host with two or more GPUs: peer access to VRAM, scratch reservation and default-node VRAM dma-buf registration now fail where the base succeeded.

Checked against the code by an independent pass.

2. 🟠 Should fix: VaUnmap leaves stale peer view in peers_: this PR does not clear peer bookkeeping on unmap, causing silent no-op remap and double-unmap

projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp:1320-1345

hsaKmtMemoryVaUnmap unmaps and evicts a peer view but leaves it in peers_ with its old GpuAddress (only hsakmt_debug_sysmem clears it). A later hsaKmtMemoryVaMap for that node then hits the 'bo is mapped already' early return without remapping, and DropPeer at free time issues a redundant unmap and may log 'peer unmap failed'. The base already behaves the same way for the owner object. A peer only exists at the owner's own VA, which is freed after ReleasePeers, so the second unmap cannot tear down a reused live mapping. Dropping the peer, or resetting its address, on unmap would be cleaner.

Who is affected: Callers that VaMap a kSystem/kUserMemory handle on a non-owner GPU at the owner's VA, then VaUnmap and VaMap it again on that GPU, without hsakmt_debug_sysmem.

Checked against the code by an independent pass.

3. 🟠 Should fix: Cross-GPU peer map plus paging-fence wait runs under process-wide allocation_map_lock_, serializing other threads' memory calls

projects/rocr-runtime/libhsakmt/src/dxg/memory.cpp:636-654,704,720,965

Every map_to_nodes/ViewOn call on an allocation already in allocation_map_ runs under allocation_map_lock_: lines 921 and 934 are inside the lock taken at 891, and 704 is inside the lock taken at 694. hsaKmtMemoryVaMap, hsaKmtMemoryVaUnmap, free and unmap also take it. The calls at 720 and 965 act on a GpuMemory that is not yet in allocation_map_, so no other thread can reach its peers_, and the race/corruption claim does not hold. What remains: creating a peer view (CreatePhysicalMemory, MapGpuVirtualAddress, MakeResident, paging-fence wait) runs under the process-wide allocation_map_lock_. This serializes other threads' alloc/map/free calls during multi-GPU maps.

Who is affected: Multi-GPU WSL/DXG processes that map system or user memory to several GPUs while other threads allocate, map or free memory.

Checked against the code by an independent pass.

4. 🟠 Should fix: DropPeer evicts and deletes peer while its unmap may still be pending: this PR omits the paging-fence wait VaUnmap performs

projects/rocr-runtime/libhsakmt/src/dxg/wddm/gpu_memory.cpp:904-919

DropPeer unmaps and Evicts the peer view and then deletes it without peer->GetDevice()->WaitOnPagingFenceFromCpu(), unlike hsaKmtMemoryVaUnmap, which waits on the fence before Evict. Adding the wait between the unmap and the Evict would match the existing pattern. No concrete failure is shown: a later map on that adapter goes to the same paging queue as the queued unmap. DropPeer is public and deletes any pointer it is given, but every current caller passes only peer views (map_to_nodes rollback, ReleasePeers).

Who is affected: Windows/WSL DXG users whose system or userptr memory is mapped to more than one GPU, when peer views are torn down.

Checked against the code by an independent pass.

5. 🟠 Should fix: Imported system-memory peer view may not be backed by the resource's pages: this PR copies owner desc_ wholesale in MapFromPeer

projects/rocr-runtime/libhsakmt/src/dxg/wddm/gpu_memory.cpp:926-956

MapFromPeer copies the owner's whole desc_ into the peer view. For a handle import that resolves to kSystem, desc_.cpu_addr is the reserved VA set by ReserveGpuVirtualAddress, so the peer's CreatePhysicalMemory wraps that range, not the imported resource's pages. is_locked is copied but LockSystemMemory is never called on the peer. If is_shared is set (imported shared handles, or hsakmt_debug_sysmem with NoAddress/Contiguous), the peer also creates an NT-shared resource carrying the owner's adapter_luid. Sanitize the copied flags, and refuse or handle imported kSystem owners.

Who is affected: Multi-GPU WSL systems that map imported kSystem graphics handles, or pinned/debug-sysmem allocations, to more than one GPU.

Checked against the code by an independent pass.

Shared from a pilot of PR Pundit, an agentic code reviewer that builds and tests the change and reads the project's past reviews. Reviewed at a64b8181; findings checked and posted by @peymanr; points already raised on this PR are left out.

@systems-assistant

Copy link
Copy Markdown
Contributor

TheRock Submodule Bump Activity

Newest first

erman-gurses pushed a commit that referenced this pull request Nov 4, 2026
## Motivation

Follow-up to #13442. After `default_node` started listing every GPU,
some multi-GPU
DXG paths that used to succeed now fail, and peer views have a few
teardown and
bookkeeping problems. Single-GPU behavior is unchanged.

## Technical Details

- `map_to_nodes` creates peer views only for system memory and user
pointers.
  VRAM  and imported resources stay mapped on the GPU that owns them, as
  before #13442.
- `hsaKmtMemoryVaUnmap` drops a peer view when the whole range is
unmapped. A
later `hsaKmtMemoryVaMap` builds a new view instead of returning early
with
"bo is mapped already", and freeing the memory no longer unmaps it
twice.
- `DropPeer` waits on the peer device's paging fence between the unmap
and
  `Evict()`, the same order `hsaKmtMemoryVaUnmap` uses.
- `MapFromPeer` refuses imported resources, whose pages are not behind
the
owner's `cpu_addr`. It also clears `is_shared` and `is_locked` on the
peer: the
owner keeps the shared handle and the Lock2 pin, and the peer only wraps
the
  same pages.

## Issue Tracking

<!-- Include a GitHub Issue and/or JIRA ID. Do not post any full JIRA
links here. -->
<!-- GitHub issue: #1234 -->
<!-- JIRA ID: EXAMPLECOMPONENT-1234 -->

## Test Plan

<!-- Explain any relevant testing done to verify this PR. -->

## Test Result

<!-- Briefly summarize test outcomes. -->

## Submission Checklist

- [ ] Look over the contributing guidelines at
https://github.com/ROCm/rocm-systems/blob/develop/CONTRIBUTING.md.

---------

Signed-off-by: Flora Cui <flora.cui@amd.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants