From 5de99fc6f682f64fb9e22617063bb38158c455b1 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Oct 2026 21:51:41 -0400 Subject: [PATCH 1/2] drm/asahi: keep withholding G17 compute doorbells while a stamp is awaited A queue whose retirement stops at a command with a completion record but no covering stamp yet sets retire_pending, which counted as leaving ordinary retirement: the next publication rang its doorbell at once, and a withheld doorbell was released whatever its predecessors. That happens after most completions. Each doorbell makes the firmware read the work ring, and a hop between queues whose producer ends 30-80 us after one is slowed by 12-24 us. The withheld doorbell already counts only predecessors without a completion record, which covers a front waiting for its stamp. Count retire_pending as settling only when the front has no record, the case of a command the firmware finished without one. In a two-queue semaphore ping-pong (gpubench sync.signal, 0.6 s traced), doorbells rung at publication fall from 2006 to 96 and releases forced by settling from 1280 to 23; all doorbells fall from 8035 to 6707. --- drivers/gpu/drm/asahi/g17/queue/compute.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/asahi/g17/queue/compute.rs b/drivers/gpu/drm/asahi/g17/queue/compute.rs index 9c4c5c92d049d..9ad6f0507898d 100644 --- a/drivers/gpu/drm/asahi/g17/queue/compute.rs +++ b/drivers/gpu/drm/asahi/g17/queue/compute.rs @@ -628,7 +628,10 @@ impl Queue { } /// The queue has left ordinary retirement: a timeout, kill, quarantine, replay, recovery - /// witness or exit is in progress. + /// witness or exit is in progress, or retirement stopped at a command the firmware + /// finished without a completion record. A front whose record has arrived and that only + /// waits for its stamp is ordinary retirement: [`Self::due_doorbell`] counts it as + /// finished, and ringing at once there rang most withheld doorbells early. fn settling(&self) -> bool { self.quarantined || self.released @@ -636,7 +639,8 @@ impl Queue { || self.replays.len != 0 || self.replay_retirement || self.awaiting_witness - || self.retire_pending + || (self.retire_pending + && self.active.front().is_none_or(|active| active.observed.is_none())) || self.retired_by_teardown || self.spared_quarantine || self.failure_status_pending From 83ff3a9040e6c06ade92f690b39419d783289128 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Sun, 25 Oct 2026 21:51:58 -0400 Subject: [PATCH 2/2] drm/asahi: restart a withheld G17 compute doorbell another doorbell covered A doorbell announces every work slot written on its ring, so a withheld doorbell of one queue is often covered by a doorbell another queue rings. Publications of the first queue after that kept the withheld doorbell's first publication, whose predecessors had long retired: the doorbell was due at once and rang for one or two new slots. In a two-queue semaphore ping-pong almost half of all releases announced a single slot. When a publication finds that another doorbell announced every slot the withheld one covered, it starts the withheld doorbell over from itself, so the doorbell waits for this command's own predecessors. Measured on Neo with this and the previous change against the base in the same boot, alternating, three rounds (us, medians): sync.signal ping-pong doorbells 7409 -> 4625 hops slowed by more than 7 us 19.6% -> 13.9% sync.signal timeline, per hop 23.40 -> 22.83 sync.signal binary, per hop 23.35 -> 22.67 compute.light queue d64 13.89 -> 13.11 The median hop is unchanged; compute.light queue d1/d2/d3/d4, queues d1/d2/d4 and render.light d1/d32 stay within 1%. --- drivers/gpu/drm/asahi/g17/queue/compute.rs | 16 ++++++++++++++-- drivers/gpu/drm/asahi/g17/runtime.rs | 6 ++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/asahi/g17/queue/compute.rs b/drivers/gpu/drm/asahi/g17/queue/compute.rs index 9ad6f0507898d..b4edec4a7b6c0 100644 --- a/drivers/gpu/drm/asahi/g17/queue/compute.rs +++ b/drivers/gpu/drm/asahi/g17/queue/compute.rs @@ -594,8 +594,9 @@ pub(crate) struct Queue { source_readers: Arc, source_hint: crate::g17::sync_input::ReaderHint, /// The doorbell withheld from publications that had [`WITHHOLD_DEPTH`] - 1 or more - /// unretired commands of this queue ahead of them, the publication of the first, and the - /// compute work slots written at its priority once its slot was. + /// unretired commands of this queue ahead of them, the publication of the first of them + /// that no doorbell has announced, and the compute work slots written at its priority once + /// the last one's slot was. withheld_doorbell: Option<(u64, u64, u64)>, } @@ -1956,6 +1957,8 @@ pub(crate) trait Host { -> Result; fn notify_compute(&mut self, priority: u8, message: u64) -> Result; fn compute_written(&self, priority: u8) -> u64; + /// Compute work slots at `priority` that a doorbell has announced. + fn compute_announced(&self, priority: u8) -> u64; fn note_submission(&mut self) -> Result; } @@ -2307,6 +2310,15 @@ impl Queue { && self.withheld_doorbell.is_none_or(|(withheld, _, _)| withheld == doorbell) { let written = host.compute_written(policy.priority()); + // Once another doorbell announced every slot the withheld one covered, it + // starts over from this publication: it then waits for this command's own + // predecessors rather than for those of slots already announced. + if self + .withheld_doorbell + .is_some_and(|(_, _, last)| host.compute_announced(policy.priority()) >= last) + { + self.withheld_doorbell = None; + } let entry = self.withheld_doorbell.get_or_insert((doorbell, published, written)); entry.2 = written; } else { diff --git a/drivers/gpu/drm/asahi/g17/runtime.rs b/drivers/gpu/drm/asahi/g17/runtime.rs index e04295d576606..bf9399cd87e73 100644 --- a/drivers/gpu/drm/asahi/g17/runtime.rs +++ b/drivers/gpu/drm/asahi/g17/runtime.rs @@ -1396,6 +1396,9 @@ impl compute::Host for ComputeHost<'_> { fn compute_written(&self, priority: u8) -> u64 { self.port.compute_written(priority) } + fn compute_announced(&self, priority: u8) -> u64 { + self.port.compute_announced(priority) + } fn note_submission(&mut self) -> Result { self.effort.submit(); super::Firmware::apply_shared_effort(self.init, self.effort, self.port, super::power::FULL_EFFORT) @@ -1443,6 +1446,9 @@ impl compute::Host for DirectHost<'_> { fn compute_written(&self, priority: u8) -> u64 { self.port.compute_written(priority) } + fn compute_announced(&self, priority: u8) -> u64 { + self.port.compute_announced(priority) + } fn note_submission(&mut self) -> Result { Ok(()) }