Skip to content

drm/asahi: g17: M5: keep the host's QoS table words and effort; one history pass per render pair - #1508

Merged
DjDeveloperr merged 4 commits into
integration/aurorafrom
djdev/g17-m5-qos-host-copy
Nov 11, 2026
Merged

DjDeveloperr merged 4 commits into
integration/aurorafrom
djdev/g17-m5-qos-host-copy

Conversation

@DjDeveloperr

Copy link
Copy Markdown
Contributor

Follows #1504. After it, an M5 render submission still held the device mutex for about 20 µs. Several of the uncached loads left in that time read words only the host writes. This series takes four of them out:

  • QoS counts. Each QoS publication read the queue, owner and data-master submission counts, and the busy-interval count when an engine went busy, back from the uncached table before storing them incremented. That is 7-8 loads per render pair and 3-4 per compute command. The accounting now keeps the values it last stored.
  • QoS rows. Each publication read the owners' rows, and each completion read the row's owner byte back to validate it: 2 + 2 loads per render pair. These checks now use the same copy. A cancellation still compares the table.
  • Submission history. The render doorbell counted the pair's two submissions with two passes over the firmware's submission history. Each pass loaded the generation and the slot byte and ended in a full fence. It now counts both in one pass, in the slot of that moment.
  • Effort word. Every submission re-applies full effort, and when the value was unchanged it read the effort word back from region C. On M5 the firmware maps region C read-only, so once the host has written the word, or read it back equal, the read-back is skipped. Other generations keep it.

Why the copies are safe

The firmware reads the QoS table and does not write it. This was checked on an M5 Pro (J714s, firmware 26A428) with two measurement-only builds, under render, compute, multi-queue and idle workloads:

  • Read-only map. The table was mapped read-only to the firmware: 3.4M publications, with no firmware fault or GPU recovery, and every test passed.
  • Read-back audit. With the table read-write, every publication and completion compared each word the old code read with the host's copy. Every 1024th publication, and the first one after an idle second, compared the whole table: 28.8M words compared, 0 differences.
  • Across a hang recovery. The audit repeated across a forced firmware hang recovery (a vertex phase past the firmware's ~13 ms limit) and still found 0 differences.

The stores to the table and their order are unchanged.

Measured

M5 Pro, gpubench pinned to a P core. Each change was switched at runtime and interleaved on one boot, 4-6 rounds, and every change was faster on both render rows. Microseconds per submit:

#1504 this series macOS 26A428 (same firmware) macOS 27.2
render.light d16 26.50 25.27 25.28 25.98
render.light d64 26.85 25.54 24.96 26.08
compute.light d16 13.62 12.52 14.46 15.15
compute.light d64 13.60 12.53 14.24 14.73

Per change, as the paired median change per round on render d16 / d64 and compute d16 / d64:

change render d16 / d64 compute d16 / d64
QoS counts -2.3% / -2.6% -3.2% / -2.6%
QoS rows -1.0% / -1.3% -3.6% / -4.1%
one history pass -0.9% / -0.7% +0.1% / +0.1% (noise)
effort word -0.7% / -0.3% -1.1% / -1.0%

On fresh boots, one pass each, render.light d16 reads 25.05 µs with these four on #1504's kernel and 25.66 µs on this branch's own kernel.

Tested

  • Kernels. The series was tested as a whole kernel built from this branch, and as aurora.ko on the M5's default kernel.
  • dEQP-VK. Each kernel was run with Mesa main + the M5 render/occlusion fixes and with the device's older Mesa, on the multi-queue lists, synchronization, synchronization2, s0, compute, glsl, draw, texture and occlusion query samples. The result was 0 Fail, with the same counts as before.
  • gpubench. core-a,feat 41/41.
  • Recoveries. None.
  • Build. No new warnings.

🤖 Generated with Claude Code

Each QoS publication read the queue, owner and data-master submission
counts back from the uncached table before storing them incremented,
and the busy-interval count when an engine went busy: seven or eight
loads per render pair, made under the device mutex. Only the host
writes these words; the firmware reads them. Keep the values last
stored in the accounting and count on from those. The rows are still
read, and the stores and their order are unchanged.

Signed-off-by: Diljit Singh <hey@djdev.me>
A QoS publication still read each owner's row from the uncached table,
for the record a cancellation compares and to refuse an owner change
while work is in flight, and every completion read the row's owner byte
back to validate it: two loads per render pair under the device mutex
and two in the event worker's. Only the host writes the rows. Keep them
with the counts and check against the copy; a cancellation still
compares the table.

Signed-off-by: Diljit Singh <hey@djdev.me>
The render doorbell counted the pair's tiling and fragment submissions
with two passes over the firmware's submission history, each loading
the history generation and the slot byte (uncached, under the device
mutex) and ending in a full fence. Count both in one pass, in the slot
of that moment, as two passes microseconds apart do.

Signed-off-by: Diljit Singh <hey@djdev.me>
Every submission re-applies full effort, and when the value is
unchanged it read the firmware's effort word back from region C to
confirm it: one uncached load per compute and render submission, under
the device mutex. On M5 region C is mapped read-only to the firmware,
so once the host has written the word, or read it back equal, it holds
the value applied. Skip the read-back then; other generations keep it.

Signed-off-by: Diljit Singh <hey@djdev.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant