Repository navigation
drm/asahi: kill faulted G17 contexts before freeing their render work - #505
Merged
DjDeveloperr merged 1 commit intoOct 27, 2026
Merged
Conversation
When a compute command fails, the firmware holds the commands its context published after it and never runs them; only a kill of the context returns them. The context's render work behind the same failure behaves differently. It runs about a second later, consumes its ring entries and returns its free-list blocks, but the firmware never stores its completion stamp and leaves the job linked in the context's work list until it processes a kill. None of that work could retire, so a closed context whose compute failed kept its pools, its render pairs, its memory and its scheduler teardown until the GPU firmware stopped. Let the kill proceed for such a context. A quarantined render pair whose commands all ran (both pass timestamps written, rings consumed, free list balanced) counts as idle for the kill and is held for it, and the kill no longer waits for render pools that are only released after it. A faulted compute queue with no commands behind the failure is a kill candidate as well. This applies only once the owner has closed; a live owner's quarantined pair stays quarantined. Free only after the firmware acknowledges the kill. The pair's unstamped work is retired once the context's scheduler page reports the kill, and its graph memory stays allocated and out of the reuse cache until then. The detach path enforces the same rule: a context whose work list still links a node and has no acknowledged kill never has its graphs freed or recycled. The firmware leaves a killed context's last job linked even after the kill, so the context's 32 KiB work object is kept out of reuse until the firmware stops; debugfs counts these as firmware_linked_objects and a warning is printed past 256. Reuse a killed context's render pairs last. The kill hold on them ends when the context's scheduler release is consumed. A new owner then takes a clean released pair first, then an unowned slot while the physical queue table still has room for both of its identities, and only then the killed pair released longest ago. Siblings and spares never take killed pairs, and a sibling no longer claims an unowned slot once the table is full. Admission is told whether the table has room whenever the runtime changes it. Publish a failed context's kill only while no other context has a compute command in flight, or once the GPU has retired nothing for the liveness interval. When GPU memory comes within an eighth of the device limit, at most one waiting kill per 250 ms skips that wait. debugfs reports the waits and the pressure kills, and a wait over 5 s is warned about once per kill. The order matters. Earlier versions retired and freed the pair first and killed afterwards, and the firmware then used memory that had been handed out again: GPU recoveries 4-7 ms after the kill and occasional firmware crashes when the kill followed within milliseconds, and a storm of compute watchdog timeouts when it followed minutes later. A run that handed killed pairs to new owners first stranded the teardowns of 24 contexts, and a render stall behind a failed context took the desktop down. Reusing them last without regard to the queue table ran out of physical queues after about 50 kills. A kill published beside other clients' running compute made the firmware recover the GPU twice in ten minutes of a five-lane stress load, which the wait avoids. Waiting without a bound let the dead clients' memory reach the device limit under sustained compute, and gnome-shell then crashed on ENOMEM, which the memory escape avoids. On Neo, ten minutes of a five-lane stress load (render, render2, long ALU, churn and global-priority CTS beside two ray query geometry-shader stress loops) had 52 faults and 15 kills (2 under memory pressure), all acknowledged and their pairs retired. There were 0 GPU recoveries and nothing was left pending afterwards, against 12 teardowns left pending without this change. Ten minutes of two ray query stress loops beside heavy geometry had 51 faults and 51 kills (3 under memory pressure), with 0 recoveries, the physical queue table full at 127 of 128 with no refusals, and 4 render-only teardowns left pending. In both, GPU memory peaked at 4.3 GiB of the 5.0 GiB device limit, no allocation was refused and gnome-shell stayed up. Known limits: - A killed context's 32 KiB work object stays allocated until the GPU firmware restarts. - While other clients keep compute running, a faulted client's memory stays held until its kill publishes: at the next moment without foreign compute, or through the memory escape near the device limit. - A closed context whose compute side is clean but whose render pair holds executed, unstamped work is not killed, since no compute queue is left to carry the kill. A kill that doesn't need a compute exit candidate would close this. - A kill that is safe beside running compute would remove the wait. It is not understood yet. An asahi_g17_teardown tracepoint records fault, unstamped retire, kill, acknowledgement, detach and slot-owner events.
DjDeveloperr
changed the base branch from
fix/g17-quarantine-leak-20261026
to
integration/aurora
October 27, 2026 05:48
RyanTheTide
pushed a commit
that referenced
this pull request
Oct 27, 2026
…t record When a compute command fails on the GPU the firmware writes a zero-duration completion record for it, which quarantines its queue, and reports the failure with a firmware event of type 7 carrying the queue and the stamp the command would have stored. It never stores that stamp. The driver ignored the event, so the quarantine waited for a retirement witness that could not arrive, and the queue kept its pool, its traversal scratch and its share of the lower GPU VA window for as long as the module stayed loaded. Under ray query stress that fails this way every few seconds (for example ray_query.stress.geometry_shader beside other clients) the retained queues filled the lower window within minutes. Every later allocation there failed: publications returned EBUSY, and gnome-shell crashed on ENOMEM creating a 16K texture after about 7 minutes. Decode the event as ComputeFault and accept its stamp as the faulted command's completion stamp for the quarantine's retirement witness, while it is newer than the stored one. The witness still requires the queue's ring to be fully consumed, so commands published after the failed one keep waiting for their own stamp. Port prerequisite for #505. Keep type 7 unknown on 25G83: the public retirement witness is qualified on 26A428 only, and a matching tag and aligned integers cannot establish T6040 DMA visibility. KUnit checks that identical words cannot become a T6040 release witness. Link: 9a76756 Assisted-by: LLM Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
RyanTheTide
added a commit
that referenced
this pull request
Oct 27, 2026
Pin the public #496/#504/#505 ports and distinguish shared retention fixes from firmware-qualified fault witnesses and descriptor changes. Document raw observability and the first-fault/idle-floor evidence needed before claiming achieved-clock, CTS or remaining performance parity. Assisted-by: LLM Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
RyanTheTide
added a commit
that referenced
this pull request
Oct 27, 2026
…rfaces Keep host resource/ownership tests compiling the production methods after #496/#505. Include the actual fault-stamp comparison, model the new queue field, and supply inert tracing/capacity notifications. The owner-ordering fixture models no executed unstamped pair; that predicate and its lifetime are covered in kernel KUnit. Preserve each original negative control. Assisted-by: LLM Signed-off-by: Ryan Murray <ryan@aurorasilicon.org>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #496 (stacked on it). This handles the case #496 left open: a closed context whose compute command failed, with render work queued behind the failure.
What the firmware does
When a compute command fails, the firmware holds the commands its context published after it and never runs them. A kill of the context is the only thing that returns them. The context's render work behind the same failure behaves differently:
Before this change none of that work could retire. The context, its pools, its render pairs and its memory stayed allocated until the GPU firmware restarted, which on Neo means until reboot. On #496 a ten-minute fault storm left 12 such teardowns pending.
What this change does
The kill can proceed. For a closed context whose compute failed:
Memory is freed only after the firmware acknowledges the kill. The unstamped render work of such a pair is retired only once the context's scheduler page reports the kill. Until then its graph memory stays allocated and out of the reuse cache. This is enforced on the retire path itself, and again at the point where a detached context's graphs are handed to reclaim: a context whose work list still links a node, with no acknowledged kill, never has its graphs freed or recycled.
The context's work storage is retained. The firmware leaves a killed context's last job linked in its work list even after the kill. The 32 KiB work object is therefore kept mapped and out of reuse until the firmware stops, instead of being handed to the next context. Kept objects are counted in debugfs (
firmware_linked_objects), and a warning is printed past 256.Killed pairs are reused last. A killed context's render slots are released once its scheduler release is consumed, instead of at the next firmware stop. A new owner then takes, in this order:
Siblings and spares never take killed pairs, and a sibling doesn't claim an unowned slot once the table is full.
Closed owners only. The executed-witness retirement applies only to pairs whose owner has closed. A live owner's quarantined pair stays quarantined as before.
The kill waits for a moment without other clients' compute, within a memory bound.
failed_kills waiting= longest_wait_ms=). A wait over 5 s is warned about once per kill.Why the order matters
Earlier versions retired and freed the pair's memory first and published the kill a few milliseconds later, or minutes later with a gate. Both made the firmware use memory that had already been handed out again:
Killing first and freeing after the acknowledgement removes both. Each of the other rules is there because a simpler version failed on the same harnesses:
Measured effect
Neo (A18 Pro), Mesa agx3, final version of this change.
Five-lane stress, 10 minutes (render, render2, long ALU, churn and global-priority CTS, plus two loops of
dEQP-VK.ray_query.stress.geometry_shader.{triangles,aabbs}):Pairs harness, 10 minutes (two ray query stress loops beside heavy geometry):
Without the memory-pressure exception, this harness ended in a desktop death from ENOMEM while kills waited.
Known limits
Testing
build.cpu.level_geometries.aabbs_1_1048576_1,build.cpuht_max.level_geometries.triangles_1_1048576_1).asahi_g17_teardowntracepoint (static key) records fault, retire, kill (marking memory-pressure kills), acknowledgement, detach and slot-owner events for diagnosing teardown on hardware. It is only called on teardown paths.