Skip to content

Revert "fix(rocr/thunk): Revoke SVM-API GPU access attributes on deregister" - #9187

Merged
amd-chiranjeevi merged 1 commit into
developfrom
revert-6699-users/saleelk/FmmUnreg
Jul 24, 2026
Merged

amd-chiranjeevi merged 1 commit into
developfrom
revert-6699-users/saleelk/FmmUnreg

Conversation

@TriveniTadapaneni

@TriveniTadapaneni TriveniTadapaneni commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

@therock-pr-bot

therock-pr-bot Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

❌ PR Check — Action Required

Check Status Details
🌿 Branch Name ✅ Pass —
📝 PR Title/Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ❌ Fail 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/src/fmm.c, projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDSVMRangeTest.cpp, projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDSVMRangeTest.hpp; 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

  • ❌ Unit Test

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.

@therock-pr-bot

therock-pr-bot Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

🚫 Please fix the failed policies before requesting reviews.

The following policy checks failed:

  • ❌ Unit Test

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

@amd-chiranjeevi
amd-chiranjeevi marked this pull request as ready for review July 24, 2026 13:53
@amd-chiranjeevi
amd-chiranjeevi merged commit 043d0b3 into develop Jul 24, 2026
40 of 55 checks passed
@amd-chiranjeevi
amd-chiranjeevi deleted the revert-6699-users/saleelk/FmmUnreg branch July 24, 2026 14:04
ammallya pushed a commit that referenced this pull request Sep 24, 2026
)

## 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`, so `hsaKmtDeregisterMemory` was a silent no-op. After a
revoke, a later register of a reused VA (typical of jemalloc) could
still see `NO_ACCESS` in the kernel. When enabled, this change:

- tracks SVM-API host ranges (keyed by the user pointer, refcounted)
- issues `NO_ACCESS` for only the sub-ranges no surviving registration
still covers, on last deregister
- re-grants `ACCESS_IN_PLACE` plus the coherency flags on re-register
- serializes the grant against the revoke (see below)

### Enabling it

`HSA_SVM_HOST_UNREGISTER_DEBUG` set to a non-`0` value 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_mutex` is never taken and no range tracking happens.

### Register/revoke serialization

The first commit picked the revoke 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`.

This was easy to hit because `hipMemcpy2DAsync` pins the
page-rounded-out host range: two adjacent `malloc(65536)` buffers get
17-page pins that share one page, and that page was revoked whenever
either buffer's refcount reached zero. On 8x gfx950
`Unit_hipMemset2DAsync_MultiThread` aborted on every run.

The fix tracks the range before the grant ioctl and holds
`svm_api_mutex` across 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: what the first attempt broke, and what is different here

This re-lands #6699 (`ce90901121a`), reverted in #9187 after #9202
reported ~20 math-lib jobs on gfx942/gfx950 **returning wrong values** -
not crashing. The revoke was too broad: it keyed the tracking by the
page-aligned base and revoked the whole page-rounded extent, so
deregistering one buffer dropped GPU access to pages a *neighbouring*
live registration still owned. A kernel then read from a range the GPU
could no longer translate.

Four things address that here:

- keyed by the exact user pointer, so two sub-page buffers sharing a
page-aligned base are separate registrations rather than one merged
extent
- interval subtraction at revoke time, so only the sub-ranges no
surviving registration still covers are revoked
- `ACCESS_IN_PLACE` is re-granted on re-register, so a VA an allocator
hands back out does not stay revoked
- the grant and the revoke are serialized under `svm_api_mutex`, so a
`NO_ACCESS` cannot land after a later grant for the same pages

**With the flag unset - the default - none of that machinery runs at
all.** `svm_api_mutex` is never taken, no range is tracked, no
allocation happens on the register path, and deregister returns without
issuing any ioctl, exactly as before this PR. The only way to reach the
new code is to opt in.

## JIRA ID

JIRA ID : ROCM-30561

### Rebased onto current develop

Re-landed on develop after the `pagedSvm` / SVM-paged-host-memory work
went in.
The textual conflict was one declaration line. The functional overlap
needed a
fifth commit: `fmm_allocate_host_gpu()` is now a second caller of
`fmm_register_mem_svm_api()`, and that path releases the object instead
of
deregistering it, so the tracking reference was taken and never given
back.
`__fmm_release()` now drops it for `is_svm_paged` objects.

Revalidated on 8x gfx950 (amdgpu 7.1.9-2402117 plus the XCC-parallel TLB
flush
patch, ROCm 10.2.0), rebuilt from this branch:

- [x] `rocrtst64`: 112/112 passed
- [x] ioctl attribute counts unchanged: register 10 attrs, map 1, revoke
8
- [x] reproducer: 1916 copies/s with the path on, 28457 with it off, no
faults

## Test plan

Built `libhsa-runtime64` against `/opt/rocm` and loaded it ahead of the
installed copy. "off" is the default; "on" is
`HSA_SVM_HOST_UNREGISTER_DEBUG=1`.

Full suites on **8x gfx950** (amdgpu 7.1.9-2402117, ROCm 10.2.0), run in
both modes:

- [x] `rocrtst64`: 108/108 passed off and on (`Memory_Max_Mem` excluded)
- [x] HIP catch (gfx950 ignore list): **4941/4941 passed off and on**
- [x] rocprim / rocthrust / rocrand, 20 binaries: 0 failures off and on
- [x] `rocsparse-test` (`*bsrmv*:*bsrilu0*:*gebsr2gebsr*`): 7395/7395
off and on

Race fix, same 8x gfx950 box, unregister on:

- [x] `Unit_hipMemset2DAsync_MultiThread`: 0-1 of 15 passed before the
serialization fix, 20/20 after
- [x] Threaded `hipMemset2DAsync` + `hipMemcpy2DAsync` reproducer: 0/10
passed before, 20/20 after
- [x] Both 12/12 again on the final tree, off and on

Race fix, 1x gfx950 in CPX (8 partitions), which exercises the same
8-way attribute arrays:

- [x] `Unit_hipMemset2DAsync_MultiThread`: 9/15 passed before the fix at
default xnack and 2/15 with `HSA_XNACK=0`; 15/15 after the fix in both
- [x] The reproducer hangs rather than aborts there, with
`MAPPING_ERROR` and a retry-fault storm in dmesg; 20/20 clean after the
fix
- [x] Single-threaded and page-aligned-buffer variants pass either way,
confirming the failure was the register/revoke ordering rather than the
revoke extent

Suites on 1x gfx950, both modes:

- [x] `rocrtst64`: 109/109 passed in both
- [x] HIP catch: same 2 failures in both, and both also fail with the
stock `libhsa-runtime64.so.1` (`Unit_hipStreamAttachMemAsync_Negative`,
`Unit_hipIpcMemAccess_Semaphores`)
- [x] rocprim / rocthrust / rocrand / rocsparse, 21 binaries incl. 7395
`rocsparse-test` cases: 0 failures in both

- [x] `AMDKFD_IOC_SVM` ioctl sizes confirm the two modes differ: off
sends the 2-attr register only, on also sends the per-GPU `NO_ACCESS`
revoke and the per-GPU `ACCESS_IN_PLACE` re-grant

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
anujshuk-amd pushed a commit that referenced this pull request Sep 25, 2026
)

## 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`, so `hsaKmtDeregisterMemory` was a silent no-op. After a
revoke, a later register of a reused VA (typical of jemalloc) could
still see `NO_ACCESS` in the kernel. When enabled, this change:

- tracks SVM-API host ranges (keyed by the user pointer, refcounted)
- issues `NO_ACCESS` for only the sub-ranges no surviving registration
still covers, on last deregister
- re-grants `ACCESS_IN_PLACE` plus the coherency flags on re-register
- serializes the grant against the revoke (see below)

### Enabling it

`HSA_SVM_HOST_UNREGISTER_DEBUG` set to a non-`0` value 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_mutex` is never taken and no range tracking happens.

### Register/revoke serialization

The first commit picked the revoke 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`.

This was easy to hit because `hipMemcpy2DAsync` pins the
page-rounded-out host range: two adjacent `malloc(65536)` buffers get
17-page pins that share one page, and that page was revoked whenever
either buffer's refcount reached zero. On 8x gfx950
`Unit_hipMemset2DAsync_MultiThread` aborted on every run.

The fix tracks the range before the grant ioctl and holds
`svm_api_mutex` across 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: what the first attempt broke, and what is different here

This re-lands #6699 (`ce90901121a`), reverted in #9187 after #9202
reported ~20 math-lib jobs on gfx942/gfx950 **returning wrong values** -
not crashing. The revoke was too broad: it keyed the tracking by the
page-aligned base and revoked the whole page-rounded extent, so
deregistering one buffer dropped GPU access to pages a *neighbouring*
live registration still owned. A kernel then read from a range the GPU
could no longer translate.

Four things address that here:

- keyed by the exact user pointer, so two sub-page buffers sharing a
page-aligned base are separate registrations rather than one merged
extent
- interval subtraction at revoke time, so only the sub-ranges no
surviving registration still covers are revoked
- `ACCESS_IN_PLACE` is re-granted on re-register, so a VA an allocator
hands back out does not stay revoked
- the grant and the revoke are serialized under `svm_api_mutex`, so a
`NO_ACCESS` cannot land after a later grant for the same pages

**With the flag unset - the default - none of that machinery runs at
all.** `svm_api_mutex` is never taken, no range is tracked, no
allocation happens on the register path, and deregister returns without
issuing any ioctl, exactly as before this PR. The only way to reach the
new code is to opt in.

## JIRA ID

JIRA ID : ROCM-30561

### Rebased onto current develop

Re-landed on develop after the `pagedSvm` / SVM-paged-host-memory work
went in.
The textual conflict was one declaration line. The functional overlap
needed a
fifth commit: `fmm_allocate_host_gpu()` is now a second caller of
`fmm_register_mem_svm_api()`, and that path releases the object instead
of
deregistering it, so the tracking reference was taken and never given
back.
`__fmm_release()` now drops it for `is_svm_paged` objects.

Revalidated on 8x gfx950 (amdgpu 7.1.9-2402117 plus the XCC-parallel TLB
flush
patch, ROCm 10.2.0), rebuilt from this branch:

- [x] `rocrtst64`: 112/112 passed
- [x] ioctl attribute counts unchanged: register 10 attrs, map 1, revoke
8
- [x] reproducer: 1916 copies/s with the path on, 28457 with it off, no
faults

## Test plan

Built `libhsa-runtime64` against `/opt/rocm` and loaded it ahead of the
installed copy. "off" is the default; "on" is
`HSA_SVM_HOST_UNREGISTER_DEBUG=1`.

Full suites on **8x gfx950** (amdgpu 7.1.9-2402117, ROCm 10.2.0), run in
both modes:

- [x] `rocrtst64`: 108/108 passed off and on (`Memory_Max_Mem` excluded)
- [x] HIP catch (gfx950 ignore list): **4941/4941 passed off and on**
- [x] rocprim / rocthrust / rocrand, 20 binaries: 0 failures off and on
- [x] `rocsparse-test` (`*bsrmv*:*bsrilu0*:*gebsr2gebsr*`): 7395/7395
off and on

Race fix, same 8x gfx950 box, unregister on:

- [x] `Unit_hipMemset2DAsync_MultiThread`: 0-1 of 15 passed before the
serialization fix, 20/20 after
- [x] Threaded `hipMemset2DAsync` + `hipMemcpy2DAsync` reproducer: 0/10
passed before, 20/20 after
- [x] Both 12/12 again on the final tree, off and on

Race fix, 1x gfx950 in CPX (8 partitions), which exercises the same
8-way attribute arrays:

- [x] `Unit_hipMemset2DAsync_MultiThread`: 9/15 passed before the fix at
default xnack and 2/15 with `HSA_XNACK=0`; 15/15 after the fix in both
- [x] The reproducer hangs rather than aborts there, with
`MAPPING_ERROR` and a retry-fault storm in dmesg; 20/20 clean after the
fix
- [x] Single-threaded and page-aligned-buffer variants pass either way,
confirming the failure was the register/revoke ordering rather than the
revoke extent

Suites on 1x gfx950, both modes:

- [x] `rocrtst64`: 109/109 passed in both
- [x] HIP catch: same 2 failures in both, and both also fail with the
stock `libhsa-runtime64.so.1` (`Unit_hipStreamAttachMemAsync_Negative`,
`Unit_hipIpcMemAccess_Semaphores`)
- [x] rocprim / rocthrust / rocrand / rocsparse, 21 binaries incl. 7395
`rocsparse-test` cases: 0 failures in both

- [x] `AMDKFD_IOC_SVM` ioctl sizes confirm the two modes differ: off
sends the 2-attr register only, on also sends the per-GPU `NO_ACCESS`
revoke and the per-GPU `ACCESS_IN_PLACE` re-grant

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
dgaliffiAMD pushed a commit that referenced this pull request Sep 25, 2026
)

## 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`, so `hsaKmtDeregisterMemory` was a silent no-op. After a
revoke, a later register of a reused VA (typical of jemalloc) could
still see `NO_ACCESS` in the kernel. When enabled, this change:

- tracks SVM-API host ranges (keyed by the user pointer, refcounted)
- issues `NO_ACCESS` for only the sub-ranges no surviving registration
still covers, on last deregister
- re-grants `ACCESS_IN_PLACE` plus the coherency flags on re-register
- serializes the grant against the revoke (see below)

### Enabling it

`HSA_SVM_HOST_UNREGISTER_DEBUG` set to a non-`0` value 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_mutex` is never taken and no range tracking happens.

### Register/revoke serialization

The first commit picked the revoke 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`.

This was easy to hit because `hipMemcpy2DAsync` pins the
page-rounded-out host range: two adjacent `malloc(65536)` buffers get
17-page pins that share one page, and that page was revoked whenever
either buffer's refcount reached zero. On 8x gfx950
`Unit_hipMemset2DAsync_MultiThread` aborted on every run.

The fix tracks the range before the grant ioctl and holds
`svm_api_mutex` across 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: what the first attempt broke, and what is different here

This re-lands #6699 (`ce90901121a`), reverted in #9187 after #9202
reported ~20 math-lib jobs on gfx942/gfx950 **returning wrong values** -
not crashing. The revoke was too broad: it keyed the tracking by the
page-aligned base and revoked the whole page-rounded extent, so
deregistering one buffer dropped GPU access to pages a *neighbouring*
live registration still owned. A kernel then read from a range the GPU
could no longer translate.

Four things address that here:

- keyed by the exact user pointer, so two sub-page buffers sharing a
page-aligned base are separate registrations rather than one merged
extent
- interval subtraction at revoke time, so only the sub-ranges no
surviving registration still covers are revoked
- `ACCESS_IN_PLACE` is re-granted on re-register, so a VA an allocator
hands back out does not stay revoked
- the grant and the revoke are serialized under `svm_api_mutex`, so a
`NO_ACCESS` cannot land after a later grant for the same pages

**With the flag unset - the default - none of that machinery runs at
all.** `svm_api_mutex` is never taken, no range is tracked, no
allocation happens on the register path, and deregister returns without
issuing any ioctl, exactly as before this PR. The only way to reach the
new code is to opt in.

## JIRA ID

JIRA ID : ROCM-30561

### Rebased onto current develop

Re-landed on develop after the `pagedSvm` / SVM-paged-host-memory work
went in.
The textual conflict was one declaration line. The functional overlap
needed a
fifth commit: `fmm_allocate_host_gpu()` is now a second caller of
`fmm_register_mem_svm_api()`, and that path releases the object instead
of
deregistering it, so the tracking reference was taken and never given
back.
`__fmm_release()` now drops it for `is_svm_paged` objects.

Revalidated on 8x gfx950 (amdgpu 7.1.9-2402117 plus the XCC-parallel TLB
flush
patch, ROCm 10.2.0), rebuilt from this branch:

- [x] `rocrtst64`: 112/112 passed
- [x] ioctl attribute counts unchanged: register 10 attrs, map 1, revoke
8
- [x] reproducer: 1916 copies/s with the path on, 28457 with it off, no
faults

## Test plan

Built `libhsa-runtime64` against `/opt/rocm` and loaded it ahead of the
installed copy. "off" is the default; "on" is
`HSA_SVM_HOST_UNREGISTER_DEBUG=1`.

Full suites on **8x gfx950** (amdgpu 7.1.9-2402117, ROCm 10.2.0), run in
both modes:

- [x] `rocrtst64`: 108/108 passed off and on (`Memory_Max_Mem` excluded)
- [x] HIP catch (gfx950 ignore list): **4941/4941 passed off and on**
- [x] rocprim / rocthrust / rocrand, 20 binaries: 0 failures off and on
- [x] `rocsparse-test` (`*bsrmv*:*bsrilu0*:*gebsr2gebsr*`): 7395/7395
off and on

Race fix, same 8x gfx950 box, unregister on:

- [x] `Unit_hipMemset2DAsync_MultiThread`: 0-1 of 15 passed before the
serialization fix, 20/20 after
- [x] Threaded `hipMemset2DAsync` + `hipMemcpy2DAsync` reproducer: 0/10
passed before, 20/20 after
- [x] Both 12/12 again on the final tree, off and on

Race fix, 1x gfx950 in CPX (8 partitions), which exercises the same
8-way attribute arrays:

- [x] `Unit_hipMemset2DAsync_MultiThread`: 9/15 passed before the fix at
default xnack and 2/15 with `HSA_XNACK=0`; 15/15 after the fix in both
- [x] The reproducer hangs rather than aborts there, with
`MAPPING_ERROR` and a retry-fault storm in dmesg; 20/20 clean after the
fix
- [x] Single-threaded and page-aligned-buffer variants pass either way,
confirming the failure was the register/revoke ordering rather than the
revoke extent

Suites on 1x gfx950, both modes:

- [x] `rocrtst64`: 109/109 passed in both
- [x] HIP catch: same 2 failures in both, and both also fail with the
stock `libhsa-runtime64.so.1` (`Unit_hipStreamAttachMemAsync_Negative`,
`Unit_hipIpcMemAccess_Semaphores`)
- [x] rocprim / rocthrust / rocrand / rocsparse, 21 binaries incl. 7395
`rocsparse-test` cases: 0 failures in both

- [x] `AMDKFD_IOC_SVM` ioctl sizes confirm the two modes differ: off
sends the 2-attr register only, on also sends the per-GPU `NO_ACCESS`
revoke and the per-GPU `ACCESS_IN_PLACE` re-grant

---------

Co-authored-by: Cursor <cursoragent@cursor.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.

2 participants