Repository navigation
feat(rocr): Restore SVM-API host unregister with debug skip flag - #11411
dayatsin-amd wants to merge 5 commits into
Conversation
✅ All Policy Checks Passed
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
cfreeamd
left a comment
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd)
Files that look correct:
- Interval arithmetic in
svm_api_range_put_locked: Verified. Scan boundbase - svm_api_max_extentis correct (any reachable range has key ≥ bound).o->startmonotonic non-decreasing in tree-key order so the left-to-rightcursweep is valid. Gap/tail emission and theend ≤ baseearly-out are correct. - Locking (commit 2): mutex held across tree-update + ioctl in both directions. Failed-grant path correctly gives the reference back and drops the revoke set under the same lock.
- Teardown (
hsakmt_fmm_destroy_process_apertures): takessvm_api_mutex, drains tree, frees nodes, resetssvm_api_max_extent. No ioctl on teardown. No thread-join under the lock. Safe. fmm_unregister_mem_svm_apierror path: ioctl failure correctly treated as non-fatal (VA may be already unmapped; kernel reclaims via MMU notifier).
[Important] Reland provenance not documented. The title says "Restore" — confirming this reinstates previously removed functionality. A re-land inherits the original revert's risk (commonly a perf regression in this codebase). The PR description is empty. Please add: (a) what was removed and why, (b) what this version does differently, and (c) benchmark results comparing with HSA_SKIP_SVM_HOST_UNREGISTER_DEBUG=1 as the skip-path baseline on a workload that exercises the hot path (hipMemcpy2DAsync on gfx950 xnack-off is a natural candidate given the commit message). The debug flag makes A/B measurement straightforward — please post the numbers.
kentrussell
left a comment
There was a problem hiding this comment.
Nothing glaring from me, but definitely not my area of expertise. Will defer to Chris and Philip, and can Approve if they are fine with it
cfreeamd
left a comment
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd) — follow-up after author updates
All prior [Question] and [Optional] findings from my 2026-09-10 review have been addressed:
- perf-residency-pinning [Question]: Feature is now opt-in off-by-default (
HSA_SVM_HOST_UNREGISTER_DEBUGmust be explicitly set). No per-operation overhead for existing workloads. Accepted. - Inward-rounding [Question]: Design intent confirmed — inward rounding preserves kernel mapping for pages shared with surviving neighbor registrations. The jemalloc VA-reuse scenario is safe. Accepted.
- Hot-path mutex [Question]: Per-extent exclusion deferred as too complex (would require "revoke in flight" placeholders). Acceptable given opt-in default limits exposure to explicit users. Accepted.
- Failed-grant rollback [Optional]: Fixed in
9722f5c3viasvm_api_range_unget_locked()— drops the node without the interval-subtraction sweep, so the rollback is now allocation-free. Addressed.
Recommend approve.
9722f5c to
c5eb743
Compare
…nership fixes Import the pinned ROCr/HSA registration and memory-region corrections with the relevant libhsakmt definitions. Preserve source identities and original component licensing for reproducible runtime builds. Provenance Upstream PRs and authors: - ROCm/rocm-systems#9394 — ashetaia-amd - ROCm/rocm-systems#11140 — andmar-amd - ROCm/rocm-systems#11411 — dayatsin-amd
…nership fixes Import the pinned ROCr/HSA registration and memory-region corrections with the relevant libhsakmt definitions. Preserve source identities and original component licensing for reproducible runtime builds. Provenance Upstream PRs and authors: - ROCm/rocm-systems#9394 — ashetaia-amd - ROCm/rocm-systems#11140 — andmar-amd - ROCm/rocm-systems#11411 — dayatsin-amd
c5eb743 to
a2377f5
Compare
Host ranges registered through the KFD SVM API have no vm_object, so deregister was a silent no-op and a later jemalloc reuse of the same VA could still see NO_ACCESS. Track those ranges, revoke GPU access on the last drop, re-grant ACCESS_IN_PLACE on re-register, and allow skipping that path with HSA_SKIP_SVM_HOST_UNREGISTER_DEBUG. Co-authored-by: Cursor <cursoragent@cursor.com>
The revoke path picked its ranges under svm_api_mutex but issued NO_ACCESS after dropping it, and register granted ACCESS_IN_PLACE before inserting into the tracking tree. Either gap lets a NO_ACCESS reach the kernel after a later grant for the same pages, so a live registration loses GPU access and the copy in flight on it faults with "Write access to a read-only page". hipMemcpy2DAsync pins the page-rounded-out host range, so two adjacent malloc'd buffers get pins that share a page and take this path constantly. Unit_hipMemset2DAsync_MultiThread aborted that way on gfx950 with xnack off. Track the range before the grant ioctl and hold svm_api_mutex across both the grant and the revoke so the tree and the kernel agree on one order. Give the reference back if the grant fails. Co-authored-by: Cursor <cursoragent@cursor.com>
HSA_SKIP_SVM_HOST_UNREGISTER_DEBUG becomes HSA_SVM_HOST_UNREGISTER_DEBUG with the opposite sense: the host unregister runs only when the variable is set to a non-0 value. Unset, which is now the default, deregistering an SVM-API host range stays the no-op it has always been and register only sets the coherency flags, so the restored teardown ships dormant and is opted into for testing rather than opted out of. Co-authored-by: Cursor <cursoragent@cursor.com>
When the SET_ATTR that grants access fails, the tracking reference taken just before it has to go back, but nothing reached the kernel so there is nothing to revoke. Routing that through svm_api_range_put_locked ran the interval-subtraction sweep and could allocate a revoke array only to free it again, on an error path that may itself be an out-of-memory one. Add svm_api_range_unget_locked(), which returns the reference and drops the node without computing a revoke set. Co-authored-by: Cursor <cursoragent@cursor.com>
…ct is released Paged host memory now allocated without a userptr BO registers itself through fmm_register_mem_svm_api(), which takes a reference on the tracking range this series added. That path releases the object instead of deregistering it, so the reference was never given back: the range outlived the VA, and a later registration reusing that VA inherited the stale entry and its refcount. Drop the reference in __fmm_release() for is_svm_paged objects, which also issues the matching revoke, so allocate/release is symmetric with register/deregister.
a2377f5 to
220d97d
Compare
|
Superseded by #12045. Same commits, rebased onto current develop (the Reopened as a plain PR against |
Summary
Restore teardown of host ranges registered through the KFD SVM API, behind a debug env var that is off by default.
Those registrations only set SVM attributes and do not create a
vm_object, sohsaKmtDeregisterMemorywas a silent no-op. After a revoke, a later register of a reused VA (typical of jemalloc) could still seeNO_ACCESSin the kernel. When enabled, this change:NO_ACCESSfor only the sub-ranges no surviving registration still covers, on last deregisterACCESS_IN_PLACEplus the coherency flags on re-registerEnabling it
HSA_SVM_HOST_UNREGISTER_DEBUGset to a non-0value turns the unregister path on.Unset - the default - deregistering an SVM-API host range stays the no-op it has always been, and register only sets the two coherency flags, exactly as before this PR. The restored teardown therefore ships dormant and is opted into for testing rather than opted out of. With it off,
svm_api_mutexis never taken and no range tracking happens.Register/revoke serialization
The first commit picked the revoke ranges under
svm_api_mutexbut issuedNO_ACCESSafter dropping it, and register grantedACCESS_IN_PLACEbefore inserting into the tracking tree. Either gap lets aNO_ACCESSreach the kernel after a later grant for the same pages, so a live registration loses GPU access and the copy in flight on it faults withWrite access to a read-only page.This was easy to hit because
hipMemcpy2DAsyncpins the page-rounded-out host range: two adjacentmalloc(65536)buffers get 17-page pins that share one page, and that page was revoked whenever either buffer's refcount reached zero. On 8x gfx950Unit_hipMemset2DAsync_MultiThreadaborted on every run.The fix tracks the range before the grant ioctl and holds
svm_api_mutexacross both the grant and the revoke, so the tree and the kernel agree on one order. The reference is given back if the grant fails.History
This re-lands #6699 (
ce90901121a), which was reverted in #9187 after #9202 reported ~20 math-lib jobs on gfx942/gfx950 returning wrong values. Relative to that original commit, this adds interval subtraction so page-sharing neighbors keep their access, keying by the user pointer instead of the page-aligned base, theACCESS_IN_PLACEre-grant on re-register, the serialization above, and the default-off gate.JIRA ID
JIRA ID : ROCM-30561
Test plan
Built
libhsa-runtime64against/opt/rocmand loaded it ahead of the installed copy. "off" is the default; "on" isHSA_SVM_HOST_UNREGISTER_DEBUG=1.Full suites on 8x gfx950 (amdgpu 7.1.9-2402117, ROCm 10.2.0), run in both modes:
rocrtst64: 108/108 passed off and on (Memory_Max_Memexcluded)rocsparse-test(*bsrmv*:*bsrilu0*:*gebsr2gebsr*): 7395/7395 off and onRace fix, same 8x gfx950 box, unregister on:
Unit_hipMemset2DAsync_MultiThread: 0-1 of 15 passed before the serialization fix, 20/20 afterhipMemset2DAsync+hipMemcpy2DAsyncreproducer: 0/10 passed before, 20/20 afterRace fix, 1x gfx950 in CPX (8 partitions), which exercises the same 8-way attribute arrays:
Unit_hipMemset2DAsync_MultiThread: 9/15 passed before the fix at default xnack and 2/15 withHSA_XNACK=0; 15/15 after the fix in bothMAPPING_ERRORand a retry-fault storm in dmesg; 20/20 clean after the fixSuites on 1x gfx950, both modes:
rocrtst64: 109/109 passed in bothHIP catch: same 2 failures in both, and both also fail with the stock
libhsa-runtime64.so.1(Unit_hipStreamAttachMemAsync_Negative,Unit_hipIpcMemAccess_Semaphores)rocprim / rocthrust / rocrand / rocsparse, 21 binaries incl. 7395
rocsparse-testcases: 0 failures in bothAMDKFD_IOC_SVMioctl sizes confirm the two modes differ: off sends the 2-attr register only, on also sends the per-GPUNO_ACCESSrevoke and the per-GPUACCESS_IN_PLACEre-grant