Public bug reported:

**Descriptive title** (kept as the heading of the description field)

  amdgpu/amdkfd SVM: hung `svm_range_restore_work` waits on a fence while 
holding a range lock,
  wedging kswapd and every allocating task; VRAM is never released and SIGKILL 
does not work
  (RX 7600 XT / gfx1102, kernel 7.0.0-38-generic)

**1. Summary**

On Ubuntu 26.04.1 with kernel `7.0.0-38-generic` (package `7.0.0-38.38`, 
upstream base `7.0.14`),
repeated attempts to load a 13 GiB GGUF model through a ROCm `llama-server` 
left the machine in an
unrecoverable state: the KFD SVM worker (`svm_range_restore_work`) got stuck in
`dma_fence_wait_timeout()` inside `svm_range_map_to_gpus()` while holding the 
per-range lock, and
the kernel itself then reports that `kswapd0`, the model server, GNOME Shell, a 
browser renderer, a
Python process and another thread are "blocked on a mutex likely owned by task 
kworker/1:2:…".
VRAM was never released, `SIGKILL` was ineffective, the graphical session froze 
and SSH dropped.
The only way out was the emergency SysRq reboot sequence, which left the 
session's journal unclean;
the ≈12.8 GiB held by the GPU was released only at that reboot. §2 is quoted 
from the kernel journal
of that boot; §3 separates what that evidence proves from what it does not.

**2. Confirmed evidence (verbatim from the kernel journal — full excerpt
attached)**

Host `<host>`; log times local (UTC-03). The complete, unedited call traces are 
in the attachment
`ANEXO-TRECHOS-KERNEL-HIGIENIZADOS.txt`; this is the condensed version.

```
19:44:02 <host> kernel: workqueue: kfd_process_wq_release [amdgpu] hogged CPU 
for >10000us 4 times, consider switching to WQ_UNBOUND
19:48:50 <host> kernel: workqueue: svm_range_restore_work [amdgpu] hogged CPU 
for >10000us 4 times, consider switching to WQ_UNBOUND
19:49:18 <host> kernel: [drm:drm_sched_entity_push_job [gpu_sched]] *ERROR* 
Trying to push to a killed entity
19:52:45 <host> kernel: INFO: task kswapd0:144 blocked for more than 122 
seconds.
19:52:45 <host> kernel: task:kswapd0  state:D stack:0  pid:144 tgid:144 ppid:2
19:52:45 <host> kernel:  mutex_lock+0x3b/0x50
19:52:45 <host> kernel:  svm_range_cpu_invalidate_pagetables+0x7c/0x150 [amdgpu]
19:52:45 <host> kernel:  __mmu_notifier_invalidate_range_start+0x149/0x1b0
19:52:45 <host> kernel:  try_to_unmap_one+0xf70/0x1260
19:52:45 <host> kernel:  shrink_folio_list+0x559/0x1080
19:52:45 <host> kernel:  evict_folios+0x3b6/0xbe0
19:52:45 <host> kernel:  balance_pgdat+0x2d3/0x940
19:52:45 <host> kernel:  kswapd+0xeb/0x1a0
19:52:45 <host> kernel: INFO: task kswapd0:144 is blocked on a mutex likely 
owned by task kworker/1:2:151726.

19:52:46 <host> kernel: INFO: task kworker/1:2:151726 blocked for more than 122 
seconds.
19:52:46 <host> kernel: task:kworker/1:2  state:D stack:0  pid:151726 
tgid:151726 ppid:2
19:52:46 <host> kernel: Workqueue: events_freezable svm_range_restore_work 
[amdgpu]
19:52:46 <host> kernel:  schedule_timeout+0xcf/0x110
19:52:46 <host> kernel:  dma_fence_default_wait+0x1a0/0x280
19:52:46 <host> kernel:  dma_fence_wait_timeout+0x13b/0x1c0
19:52:46 <host> kernel:  svm_range_map_to_gpus+0x186/0x380 [amdgpu]
19:52:46 <host> kernel:  svm_range_validate_and_map+0x8d3/0xaa0 [amdgpu]
19:52:46 <host> kernel:  svm_range_restore_work+0xe0/0x3b0 [amdgpu]
19:52:46 <host> kernel:  process_one_work+0x1ac/0x3d0
19:52:46 <host> kernel:  worker_thread+0x1b8/0x360
```

Same boot, other confirmed facts:

- **Seven tasks were reported blocked in that round, and six of them wait on 
the mutex owned by the
  seventh.** The kernel printed "blocked for more than 122 seconds" for seven 
distinct tasks and its
  lock-owner note — "is blocked on a mutex likely owned by task 
kworker/1:2:151726" — for **six** of
  them, because the owner itself never gets that note. The seventh is therefore 
`kworker/1:2:151726`,
  the SVM worker quoted above; the six waiters are `kswapd0:144`, the model 
server `ollama:2124`,
  `gnome-shell:47229`, a browser renderer `Isolated Web Co:49818`, 
`python3:154641` and
  `Thread-3 (loop):154642`. All six wait inside 
`svm_range_cpu_invalidate_pagetables`, i.e. in the
  reclaim path.
- After another 122 s (19:54:48) three of them are re-reported as blocked for 
more than 245 s, and
  the kernel then prints "Future hung task reports are suppressed".
- **Recovery and unclean shutdown.** The wedged machine could not be brought 
down normally: it was
  recovered with the emergency **SysRq** reboot sequence (operator's account — 
no SysRq trace
  survives in the preserved journal of that boot, which ends at 20:04:50 with 
the system already
  stalled). The termination was unclean, as the next boot records for both 
journal files:
  `20:12:03 systemd-journald: File …/system.journal corrupted or uncleanly shut 
down, renaming and
  replacing`, and the same for the user journal at 20:12:13 (path elided). The 
≈12.8 GiB of VRAM
  held during the incident was released only at that reboot: afterwards the 
card reports
  `mem_info_vram_used = 268611584` (≈0.25 GiB) against `mem_info_vram_total = 
17163091968`
  (sysfs, post-reboot).
- The client aborted the load five times in 26 minutes (19:23:45, 19:28:48, 
19:33:53, 19:39:07,
  19:44:19); each abort logged `client connection closed before llama-server 
finished loading,
  aborting load` and `Load failed … "timed out waiting for llama-server to 
start: context
  canceled"`, immediately followed at 19:49:18 by the killed-entity message. 
Every attempt had
  reached `load_tensors: ROCm0 model buffer size = 12807.21 MiB`.
- Hung-task timeout is 122 s, so the wedged wait began ≈19:50:43…19:50:46 — 
**after** the
  killed-entity message (19:49:18) and after the last abort.
- Host conditions when it wedged: 14.4 GiB RAM with ~205 MB free and ~13 GB in 
page cache; 16 GiB
  VRAM of which ≈12.8 GiB belonged to the model. An unrelated service was in a 
crash-restart loop
  (restart counter ≈1490) at the same time.
- **No lockdep report exists in this boot**: the deadlock was detected only by 
the hung-task
  mechanism, not as a lockdep cycle.
- The same model, context and binary had loaded in 8.6–71.5 s on the same 
machine under the
  previous kernel (noble HWE `7.0.0-38.38~24.04.4`, also 7.0.14-based).

**3. Interpretation, by strength of evidence**

*(a) Confirmed — by code inspection of the installed base, reproducible
with the checks in §11:*

1. **The contended lock is identifiable.** Both sides take the per-`svm_range` 
mutex via
   `svm_range_lock()`: the reclaim/notifier path inside 
`svm_range_cpu_invalidate_pagetables()`
   (kfd_svm.c:2669) and the worker path inside `svm_range_validate_and_map()` 
(lines 1774, 1812).
   The hung stack shows `mutex_lock` inlined at exactly that notifier call 
site, and the kernel's
   lock-owner annotation independently names the SVM worker as the likely owner.
2. **The wait is on the fence the mapping returned, and that fence is handed 
out only on success.**
   `svm_range_map_to_gpus()` (plural) calls `svm_range_map_to_gpu()` (singular) 
and then
   `dma_fence_wait(fence, false)` (line 1551). The singular sets `*fence = 
dma_fence_get(vm->last_update)`
   only *after* its error paths (`if (fence) *fence = 
dma_fence_get(vm->last_update);`, lines
   1503-1509 — reached only when `amdgpu_vm_update_range()` and 
`amdgpu_vm_update_pdes()` both
   returned 0). A failed mapping produces no fence and therefore no wait.
3. **"Killed entity" is the scheduler-entity stopped state.** 
`vm->immediate.stopped` /
   `vm->delayed.stopped`, checked by `amdgpu_vm_ready()`, are the `stopped` 
field of
   `struct drm_sched_entity`, documented in the *installed* header
   
(`/usr/src/linux-headers-7.0.0-38-generic/include/drm/gpu_scheduler.h:205-212`) 
as "Marks the
   entity as removed from rq and destined for termination. This is set by 
calling
   `drm_sched_entity_flush()` and by `drm_sched_fini()`".

*(b) Supported inference (consistent with everything above, but not
proven):*

The wedge is the SVM worker waiting forever on a fence while holding the range 
lock, which converts
a local GPU problem into a system-wide stall: reclaim cannot enter the 
notifier, so allocation
stalls, so the session and SSH die. A GPU scheduler entity belonging to the 
just-killed model
server was already dead ~90 s before the wait started.

*(c) Open — I could not determine this, and it is not presented as
fact:*

How a never-signalling fence reached the worker. Two mutually exclusive
candidates:

- **H1** — the VM was already not-ready (its entity stopped) when the worker 
called
  `svm_range_map_to_gpu()`; the push failed *inside* `amdgpu_vm_update_range()` 
(which receives
  `&vm->last_update` as its fence output), and a later call then returned a 
stale, never-submitted
  fence. Whether that error path can leave a dead fence in `vm->last_update` 
depends on
  `amdgpu_vm_sdma_commit()` / `amdgpu_vm_cpu_commit()`, which I could **not** 
inspect (only kernel
  headers are installed locally) — so this is unverified.
- **H2** — the fence was legitimately created by a *successful* push and never 
signalled because
  the queue/scheduler died afterwards. Under H2 the VM was ready at submission 
time and the guard
  discussed in §5 would **not** have prevented the wait.

Telling H1 from H2 needs runtime tracing or a reproduction with a candidate 
patch; neither was
possible here (read-only audit, no inference, no load tests — see §9).

**4. Status of the two commits (corrected in v2)**

`f101c13a8720c73e67f8f9d511fbbeda95bcedb1` — "drm/amdgpu: fix task hang from 
failed job submission
during process kill" (Liu01 Tong, 2025-08-11).

> **Integrally incorporated in the installed kernel — not merely functionally 
> covered by an
> equivalent change.** Two independent verifications:

1. **Ancestry.** The GitHub comparison API for `f101c13a…v7.0` returns `status: 
ahead`,
   `behind_by: 0` and `merge_base_commit: 
f101c13a8720c73e67f8f9d511fbbeda95bcedb1`. The commit is
   an ancestor of the `v7.0` release tag, hence part of every 7.0.x stable 
release, including the
   installed 7.0.14. (The raw API responses are kept in the reporter's private 
archive; the same
   call can be repeated by anyone — see §11(a), item 6.)
2. **Code.** Both hunks are present in the installed base: 
`amdgpu_vm.c:amdgpu_vm_ready()` contains
   `ret &= !vm->immediate.stopped;` and `ret &= !vm->delayed.stopped;` and is 
byte-identical to
   the 7.1.7 version (the `bool empty;` the patch removed is gone), and 
`amdgpu_cs.c` has
   `if (!amdgpu_vm_ready(vm)) return -EINVAL;` immediately before 
`amdgpu_vm_clear_freed()`
   (line ~1119).

The installed changelog lists no entry for it, which is consistent with it 
arriving with the 7.0
base rather than as a listed stable backport. **Therefore §8 does not request 
it.**

`d0f5711fa14a09c010537375cf34893cd33bc2ee` — "drm/amdkfd: check if vm ready in 
svm map and unmap to
gpu" (YuanShang Mao; Reviewed-by: Philip Yang; Signed-off-by: Alex Deucher).

> **Absent** from the installed kernel and from the entire
resolute/noble 7.0 line:

```
amdgpu_vm_ready() call sites in kfd_svm.c
  upstream v7.0.14 (installed base) ......... 0   MISSING
  upstream v6.18.43 ......................... 0
  upstream v7.1.3 / v7.1.6 / v7.1.7 ......... 2   PRESENT
  upstream v7.2.5 ........................... 2
  Ubuntu linux 7.0.0-38.38       (installed)  0
  Ubuntu linux-hwe-7.0 7.0.0-38.38~24.04.4    0
  Ubuntu linux 7.0.0-39.39       (-proposed)  0
```
Ancestry: `status: diverged`, `merge_base_commit: 
591cd656a1bf5ea94a222af5ef2ee76df029c1d2` —
**not** an ancestor of `v7.0`; present from v7.1.3 onward. The installed build 
declares "Upstream
stable to v6.18.41, v6.18.42, v6.18.43, v7.1.6, v7.1.7" in its changelog, i.e. 
it already reaches
7.1.6/7.1.7, which **do** contain the guard — so the omission looks like patch 
selection rather
than unavailability.

**5. What the missing change does at the call site involved here**

In 7.0.14, `svm_range_map_to_gpus()` (plural) loops over GPUs, calling 
`svm_range_map_to_gpu()`
(singular) for each; when that returns 0 it waits on the returned fence 
(`dma_fence_wait(fence,
false)`, line 1551) and then flushes the TLB. `d0f5711f` inserts at the top of 
the singular
`svm_range_map_to_gpu()` (and of `svm_range_unmap_from_gpu()`):

```c
        if (!amdgpu_vm_ready(vm)) {
                pr_debug("VM not ready, canceling map\n");
                return -EINVAL;
        }
```

With the guard, a not-ready VM never reaches `amdgpu_vm_update_range()`; the 
singular returns
`-EINVAL`, the plural's `if (r) break;` fires, and the `dma_fence_wait()` is 
not reached. The guard
therefore sits on the code path our worker occupied, and the author's stated 
rationale — "Don't map
or unmap svm range to gpu if vm is not ready for updates. Why: DRM entity may 
already be killed
when the svm worker try to update gpu vm." — describes the situation our log 
documents.

**6. Why the fix must not be presented as guaranteed**

1. **The guard fires only if `amdgpu_vm_ready(vm)` is false**, and which of its 
four conditions
   (`vm->evicting`, non-empty `vm->evicted`, `immediate.stopped`, 
`delayed.stopped`) held at that
   instant is unknown: nothing in the log pins the VM state at the moment of 
the wait. The
   killed-entity message proves *an* entity was dead, not that *this* VM's 
entity was.
2. **The ordering argues for caution.** The wait began ≈19:50:43, i.e. *after* 
the killed-entity
   message, and per §3(a)(2) a mapping whose push fails produces no fence and 
hence no wait. So
   this is not a plain "push failed for a stopped entity and we waited anyway"; 
the intervening
   state change is not in the log, and H2 of §3(c) remains possible.
3. **The commit is hardening for a class of failure, not a verified cure for 
this occurrence.**
   Nothing in it guarantees that a fence already handed out by a successful 
update will signal.
4. **It was not tested.** No reproduction with or without the patch was 
performed (read-only audit;
   loading models was out of bounds), so its effect here is unmeasured.

What the evidence *does* justify: this is the only upstream AMDGPU/KFD change 
identified that
addresses "the SVM worker updates the GPU VM of an already-dead scheduler 
entity", which is the
state our log documents. Treat a backport as **risk reduction for that class**, 
not as a proven fix
for this incident.

**7. Companion evidence: this is not Launchpad #2161667**

That report (package `linux-hwe-7.0`, status *New*) shows the same two 
`workqueue: … hogged CPU`
lines, but its reporter found it no longer reproducible on `7.0.0-34-generic` 
and attributed the fix
to `drm/amdgpu: drop retry loop in amdgpu_hmm_range_get_pages`, which the 
installed kernel already
contains (since `7.0.0-31.31`, per its changelog). Independent reports with our 
stack —
`peonist-ai/halogen-flash-server` #34 (2026-09-12, kernel `7.0.0-31-generic 
#31~24.04.1-Ubuntu`,
`SIGKILL` ineffective, container stuck until reboot) and `ROCm/ROCm` #6508 
(open; kernel 7.0 hangs
where 6.17 worked, same workqueue messages) — indicate a distinct, still-open 
defect. Keep the
tracker tasks separate so this report is not closed as a duplicate.

**8. Requested action**

1. Consider backporting `d0f5711fa14a09c010537375cf34893cd33bc2ee` to the 26.04 
kernel, and
   evaluating the same for the noble `linux-hwe-7.0` line. Its prerequisite 
`f101c13a8720c7` is
   **already present** in both (verified in §4), so no companion commit is 
required; the delta is
   the two `amdgpu_vm_ready()` call-site guards in `kfd_svm.c` (11 insertions, 
one file).
2. Treat the effect as **hardening of the SVM worker path**, not as a confirmed 
fix for the
   occurrence reported here, and consider whether the remaining case — a fence 
from a *successful*
   update that never signals after its queue dies — needs separate handling.
3. To test the hypothesis before committing to it: a temporary probe logging 
`amdgpu_vm_ready()`'s
   result at the top of `svm_range_map_to_gpu()` (or an ftrace probe on that 
function plus
   `drm_sched_entity_push_job`) would settle §3(c) on a re-run. That 
measurement is what this report
   is missing.

**9. Limitations of this investigation (declared)**

- Read-only audit: no packages installed, no kernel/firmware update, no service 
restarted, no model
  loaded, no load test, no changes to GRUB/systemd/ROCm/Ollama. Nothing in the 
evidence was produced
  by running the workload.
- The incident was not reproduced. Conclusions rest on the journal of the 
affected boot, the sources
  of the exact installed base (v7.0.14), the official Canonical source deltas, 
and the upstream tags
  listed above.
- `amdgpu_vm_sdma.c` / `amdgpu_vm_cpu.c` were not inspected (only kernel 
headers are installed
  locally), which is why §3(c) stays open.
- The client machine (`<client>`) was not reachable from here; its 300 s header 
timeout and its
  automatic retries are known from the server-side log only.
- Ancestry results come from the GitHub comparison API (metadata) corroborated 
by code inspection of
  published tags; the Ubuntu source package's git history was not inspected.
- The `drm_sched_entity_push_job` message does not name the entity it rejected, 
so the entity's
  identity is inferred, not read.
- The recovery method (SysRq) is the operator's account: no trace of it 
survives in the preserved
  journal, because the stalled system could not flush it.

**10. Attachment policy**

Attached, and only this: a hand-curated kernel-log excerpt of the affected boot 
covering 19:44–19:55
— the companion file `ANEXO-TRECHOS-KERNEL-HIGIENIZADOS.txt`. It contains the 
two `workqueue`
warnings, the killed-entity message, the **complete** call traces (from 
`<TASK>` to `</TASK>`) of
the worker holding the lock and of `kswapd0` and the model server, and the 
header/anchor lines of
the remaining waiting tasks. Removed: host name (→ `<host>`), all IP and MAC 
addresses, tailnet and
bridge names, disk UUIDs and serial numbers, machine-id, SSH key fingerprints, 
login e-mails, and
the names of private self-hosted services; register dumps were dropped (address 
values, no bearing
on the traces). The attachment states its own omissions, and the removals were 
verified
programmatically (zero matches for those patterns), so the excerpt can be 
compared against the
reporter's private archive.

**11. Reproducing the checks**

*(a) Checks you can run against your own kernel tree.* The paths below are 
relative to the root of a
Linux source tree of the corresponding version (upstream `v7.0.14` / `v7.1.6` / 
`v7.1.7`, or the
Ubuntu tree carrying the same base). Line numbers cited anywhere in this report 
refer to **upstream
v7.0.14**. Adjust the top-level directory as needed. Only the first and last 
commands touch the
installed system.

```bash
# base of the installed kernel, taken from the installed headers
sed -n '1,4p' /usr/src/linux-headers-7.0.0-38-generic/Makefile      # VERSION 7 
PATCHLEVEL 0 SUBLEVEL 14

# 1) d0f5711f is ABSENT: no amdgpu_vm_ready() call site in kfd_svm.c
grep -c amdgpu_vm_ready drivers/gpu/drm/amd/amdkfd/kfd_svm.c       # 0 in 
v7.0.14, 2 in v7.1.6 and v7.1.7

# 2) f101c13a is PRESENT: the two "stopped" checks inside amdgpu_vm_ready()
awk '/^bool amdgpu_vm_ready/,/^}/' drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 
grep stopped   # 2 checks
grep -n -B4 amdgpu_vm_clear_freed drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c        
          # guard at ~line 1119

# 3) the fence is handed out only on success (§3(a)(2))
sed -n '1495,1508p' drivers/gpu/drm/amd/amdkfd/kfd_svm.c
# 4) the lock both sides take (§3(a)(1))
grep -n 'svm_range_lock(prange)' drivers/gpu/drm/amd/amdkfd/kfd_svm.c     # 
1774, 1812, 2669

# 5) drm_sched_entity::stopped semantics — from the INSTALLED headers
sed -n '205,212p' 
/usr/src/linux-headers-7.0.0-38-generic/include/drm/gpu_scheduler.h

# 6) ancestry of the two commits (metadata API; expect ahead/behind_by:0, and 
diverged)
curl -s 
'https://api.github.com/repos/torvalds/linux/compare/f101c13a8720c73e67f8f9d511fbbeda95bcedb1...v7.0'
 | grep -E '"(status|behind_by)"'
curl -s 
'https://api.github.com/repos/torvalds/linux/compare/d0f5711fa14a09c010537375cf34893cd33bc2ee...v7.0'
 | grep -E '"(status|behind_by)"'
```

*(b) Where the copies quoted in this report come from.* This report also refers 
to files kept in the
reporter's private archive (for example `kernel/fontes-upstream/…`, 
`kernel/diffs-canonical/…` and
the `_7014`-suffixed source copies). **Those paths are not reachable by 
maintainers** and are
mentioned only for the reporter's own traceability; every quoted file has a 
public origin that
anyone can fetch:

- `kfd_svm.c`, `amdgpu_vm.c`, `amdgpu_vm.h`, `amdgpu_cs.c` per tag, from the 
upstream mirror:
  
`https://raw.githubusercontent.com/gregkh/linux/<tag>/drivers/gpu/drm/amd/amdkfd/kfd_svm.c`
  (and `.../drivers/gpu/drm/amd/amdgpu/{amdgpu_vm.c,amdgpu_vm.h,amdgpu_cs.c}`), 
with
  `<tag>` ∈ {v7.0.14, v6.18.43, v7.1.3, v7.1.6, v7.1.7, v7.2.5}.
- Ubuntu source deltas of the releases compared above — source package `linux` 
at `7.0.0-38.38`
  (installed) and `7.0.0-39.39` (proposed), and `linux-hwe-7.0` at 
`7.0.0-38.38~24.04.4`: the
  per-version pages under 
`https://launchpad.net/ubuntu/+source/<package>/<version>` link to the
  `.diff.gz` and `.dsc`; `apt-get source linux=7.0.0-38.38` (with `deb-src` 
enabled) yields the same
  files.
- Commit patches: `https://github.com/torvalds/linux/commit/<sha>.patch`.
- The kernel headers quoted in §3(a)(3) and §11(a)(5) are part of the installed
  `linux-headers-7.0.0-38-generic`.

** Affects: linux (Ubuntu)
     Importance: Undecided
         Status: New

** Attachment added: "Sanitized kernel log excerpt — AMDGPU/KFD SVM deadlock, 
2026-10-15"
   
https://bugs.launchpad.net/bugs/2171116/+attachment/6009850/+files/ANEXO-TRECHOS-KERNEL-HIGIENIZADOS.txt

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2171116

Title:
  [7.0.0-38] amdgpu/KFD SVM worker hangs on GPU fence, blocking kswapd
  and memory allocation

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/2171116/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to