Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions drivers/gpu/drm/asahi/g17/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub(super) trait Queues {
fn retain_render_completions(&mut self, masks: [u64; 2]);
fn complete_compute(&mut self, masks: [u64; 2]) -> Result;
fn context_killed(&mut self, cookie: u64);
/// The firmware failed a compute command (see [`Event::ComputeFault`]).
fn compute_fault(&mut self, qid: u8, stamp: u32);
}

/// Work requested after releasing the device mutex.
Expand Down Expand Up @@ -72,6 +74,11 @@ fn batch<H: Host>(host: &mut H, role: Role, masks: &mut [u64; 2]) -> Result {
}
Event::FreeListGrow(completion) => host.complete_freelist_grow(completion)?,
Event::ContextKilled(cookie) => host.context_killed(cookie),
Event::ComputeFault { qid, stamp } => {
host.compute_fault(qid, stamp);
// Its queue retires or settles its quarantine on this pass.
masks[usize::from(qid) / 64] |= 1 << (qid % 64);
}
Event::Unknown(_) => {}
}
Ok(true)
Expand Down
41 changes: 41 additions & 0 deletions drivers/gpu/drm/asahi/g17/fw/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ pub(crate) enum Event {
TvbGrow(TvbGrowRequest),
FreeListGrow(FreeListCompletion),
ContextKilled(u64),
/// The firmware failed a compute command of queue `qid` and stores no completion stamp for
/// it: `stamp` is the value that command would have stored.
ComputeFault { qid: u8, stamp: u32 },
Unknown(u32),
}

Expand All @@ -145,6 +148,8 @@ impl EventRecord {
const TVB_GROW: u32 = 6;
const FREE_LIST_GROW: u32 = 13;
const CONTEXT_KILLED: u32 = 12;
const COMPUTE_FAULT: u32 = 7;
const COMPUTE_QIDS: u32 = 128;
const DIAGNOSTIC_PAIRS_MAX: u16 = 5;
const TVB_SLOTS: u32 = 127;
const SUBPIPES: u32 = 64;
Expand Down Expand Up @@ -198,6 +203,19 @@ impl EventRecord {
halt_count: record.halt_count,
}))
}
Self::COMPUTE_FAULT => {
// SAFETY: Every constructor initializes all integer words.
let words = unsafe { self.words };
// Only compute faults are known to use this type: any other layout stays an
// unknown event, consumed and ignored as before.
if words[3] >= Self::COMPUTE_QIDS || words[4] & 0xff != 0 {
return Ok(Event::Unknown(Self::COMPUTE_FAULT));
}
Ok(Event::ComputeFault {
qid: words[3] as u8,
stamp: words[4],
})
}
Self::CONTEXT_KILLED => {
// SAFETY: Every constructor initializes all integer words.
let words = unsafe { self.words };
Expand Down Expand Up @@ -525,3 +543,26 @@ impl ContextKill {
}
}
}

#[cfg_attr(not(test), kernel::macros::kunit_tests(asahi_g17_event_records))]
mod tests {
use super::*;

fn record(words: &[u32]) -> EventRecord {
let mut all = [0u32; EVENT_WORDS];
all[..words.len()].copy_from_slice(words);
EventRecord::from_words(all)
}

#[test]
fn a_compute_fault_names_its_queue_and_stamp() {
let event = record(&[7, 1, 2, 0x39, 0x300, 0xffff_fc00, 1]).decode();
assert_eq!(event, Ok(Event::ComputeFault { qid: 0x39, stamp: 0x300 }));
}

#[test]
fn an_unrecognized_type_7_layout_stays_unknown() {
assert_eq!(record(&[7, 1, 2, 128, 0x300]).decode(), Ok(Event::Unknown(7)));
assert_eq!(record(&[7, 1, 2, 9, 0x301]).decode(), Ok(Event::Unknown(7)));
}
}
31 changes: 30 additions & 1 deletion drivers/gpu/drm/asahi/g17/queue/compute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,9 @@ pub(crate) struct Queue {
config_cache: Option<(QueueConfig, u64)>,
ordinal: u32,
submitted: u32,
/// The stamp of the newest command the firmware reported failed ([`Self::note_fault`]),
/// while it is newer than the stored stamp.
fault_stamp: Option<u32>,
/// Prepared packets of the bound owner about to publish (see [`Self::claim`]).
claims: u32,
last_published: u64,
Expand Down Expand Up @@ -694,6 +697,7 @@ impl Queue {
config_cache: None,
ordinal: 0,
submitted: 0,
fault_stamp: None,
claims: 0,
last_published: 0,
last_end: 0,
Expand All @@ -714,6 +718,29 @@ impl Queue {
withheld_doorbell: None,
})
}
/// The firmware failed the command whose completion stamp is `stamp`. It writes a
/// zero-duration completion record for it, which quarantines the queue, but stores no
/// stamp: without this the quarantine would wait for its retirement witness forever and
/// keep the queue's backing. Commands published after the failed one still need their own
/// stamp or record.
pub(crate) fn note_fault(&mut self, stamp: u32) {
if self
.fault_stamp
.is_none_or(|old| retirement::stamp_newer(stamp, old))
{
self.fault_stamp = Some(stamp);
}
}

/// The stamp that proves retirement: the stored one, or a newer reported fault's.
fn witness_stamp(&self) -> Result<u32> {
let stored = self.graph.stamp()?;
Ok(match self.fault_stamp {
Some(fault) if retirement::stamp_newer(fault, stored) => fault,
_ => stored,
})
}

pub(crate) fn qid(&self) -> u8 {
self.kick.id().qid()
}
Expand Down Expand Up @@ -1096,7 +1123,7 @@ impl Queue {
if !retirement::quarantined_retired(
&newest,
self.graph.cursors()?,
self.graph.stamp()?,
self.witness_stamp()?,
self.active.len as u32,
self.submitted,
)? {
Expand Down Expand Up @@ -1140,6 +1167,7 @@ impl Queue {
drop(self.active.pop());
}
self.awaiting_witness = false;
self.fault_stamp = None;
self.retirement_proved = false;
self.replay_retirement = false;
self.spared_deferred = false;
Expand Down Expand Up @@ -1584,6 +1612,7 @@ impl Queue {
self.spared_deferred = false;
self.retire_pending = false;
self.awaiting_witness = false;
self.fault_stamp = None;
self.retirement_proved = false;
self.replay_retirement = false;
self.replay_error = None;
Expand Down
15 changes: 15 additions & 0 deletions drivers/gpu/drm/asahi/g17/queue/compute/retirement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ impl Cursors {
}
}

/// Whether stamp `a` was stored after stamp `b` (both kick stamps, modulo wrap).
pub(crate) const fn stamp_newer(a: u32, b: u32) -> bool {
let delta = a.wrapping_sub(b);
delta != 0 && delta < 0x8000_0000
}

/// Outstanding kicks span fewer than half the 24-bit stamp sequence. Zero is a valid wrap.
pub(crate) const fn stamp_covers(word: u32, kick: u64) -> bool {
let delta = word.wrapping_sub((kick as u32).wrapping_shl(8));
Expand Down Expand Up @@ -447,6 +453,15 @@ mod tests {
assert!(stamp_covers(0, 0xff_ffff));
}

#[test]
fn a_reported_fault_stamp_is_newer_only_until_stored() {
assert!(stamp_newer(own(KICK), own(KICK - 1)));
assert!(!stamp_newer(own(KICK), own(KICK)));
assert!(!stamp_newer(own(KICK - 1), own(KICK)));
// Across the wrap of the 24-bit kick in the stamp word.
assert!(stamp_newer(own(0), own(0xff_ffff)));
}

#[test]
fn a_late_stamp_is_waited_for() {
// Under games on J700 the stamp lands up to about 4 ms after the record while the
Expand Down
9 changes: 9 additions & 0 deletions drivers/gpu/drm/asahi/g17/runtime/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ impl event::Queues for Service<'_> {
fn context_killed(&mut self, cookie: u64) {
self.firmware.queues.acknowledge_compute_exit(cookie);
}
fn compute_fault(&mut self, qid: u8, stamp: u32) {
for entry in self.firmware.queues.compute.iter_mut().flatten() {
if let Some(mut queue) = entry.queue.as_deref_mut() {
if queue.qid() == qid {
queue.note_fault(stamp);
}
}
}
}
fn flush_tvb_reply(&mut self) -> Result<bool> {
self.firmware.flush_tvb_reply()
}
Expand Down