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
21 changes: 19 additions & 2 deletions drivers/gpu/drm/asahi/g17/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use core::ops::Range;
use core::sync::atomic::{
AtomicBool,
AtomicU32,
AtomicU64,
Ordering, //
};

Expand Down Expand Up @@ -369,7 +370,7 @@ impl rtkit::Operations for RoleOps {
if !is_events_message(ep, msg) {
return false;
}
data.shared.queue_events();
data.shared.queue_event_records();
true
}

Expand Down Expand Up @@ -857,6 +858,11 @@ struct Shared {
compute_demand: AtomicU32,
#[pin]
work: Work<Shared, EVENT_WORK>,
/// The next event pass runs every housekeeping step: set by every [`Self::queue_events`]
/// request, not by the firmware's event notification ([`Self::queue_event_records`]).
housekeeping: AtomicBool,
/// When an event pass last ran every housekeeping step, in ns.
housekeeping_ns: AtomicU64,
#[pin]
poll: DelayedWork<Shared, POLL_WORK>,
#[pin]
Expand Down Expand Up @@ -989,8 +995,17 @@ impl Shared {
self.port.store(core::ptr::null_mut(), Ordering::Release);
}

/// Queues the event worker. Callable from any context.
/// Queues the event worker for a pass that runs every housekeeping step. Callable from any
/// context.
fn queue_events(self: &Arc<Self>) {
self.housekeeping.store(true, Ordering::Release);
self.queue_event_records();
}

/// Queues the event worker for firmware records alone: completions, or records that arrived
/// during a pass. The pass skips the housekeeping that nothing needs (see
/// `runtime_event_turn`). Callable from any context.
fn queue_event_records(self: &Arc<Self>) {
// Already queued if this fails, which is just as good.
let _ = workqueue::system_highpri().enqueue::<Arc<Shared>, EVENT_WORK>(self.clone());
}
Expand Down Expand Up @@ -1170,6 +1185,8 @@ impl Gpu {
renders,
compute_demand: AtomicU32::new(0),
work <- new_work!("g17::Shared::work"),
housekeeping: AtomicBool::new(true),
housekeeping_ns: AtomicU64::new(0),
poll <- new_delayed_work!("g17::Shared::poll"),
idle <- new_delayed_work!("g17::Shared::idle"),
grow <- new_work!("g17::Shared::grow"),
Expand Down
61 changes: 43 additions & 18 deletions drivers/gpu/drm/asahi/g17/runtime/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use kernel::{prelude::*, sync::Arc};

/// Wake interval while a tiler heap may still shrink: the native check period.
const SHRINK_WAKE_NS: u64 = 1_000_000_000;
/// Longest interval between two event passes that run every housekeeping step while
/// completion records alone keep the worker busy.
const HOUSEKEEPING_NS: u64 = 1_000_000;

struct Service<'a> {
firmware: &'a mut Firmware,
Expand Down Expand Up @@ -378,6 +381,10 @@ impl crate::g17::Shared {
}

fn runtime_event_turn(self: &Arc<Self>, deferred: &mut DeferredBatch) -> Result {
// Taken before consuming records: a request made during this pass gets the next one.
let requested = self
.housekeeping
.swap(false, core::sync::atomic::Ordering::AcqRel);
let (recover, masks) = {
let mut state = self.state.lock();
let Some(firmware) = (*state).as_deref_mut() else {
Expand Down Expand Up @@ -438,24 +445,42 @@ impl crate::g17::Shared {
.any(|queue| queue.in_flight());
let in_flight = compute || firmware.queues.render_in_flight();
let idle = firmware.idle_check(now, in_flight)?;
let lingers = if firmware.recovery.pending() {
None
} else {
firmware.expire_render_lingers(now)?
};
firmware.reclaim_drained_renders(deferred)?;
firmware.service_compute_exit(deferred)?;
let pools = firmware.service_compute_pools(deferred, now)?;
// Expiries, drained pairs, exits, pool and heap trimming and teardown give back what
// retirement freed; a completion needs none of them to retire or to signal. While work
// is in flight and nothing waits for capacity, a pass for firmware records alone skips
// them. Any requested pass, recovery, teardown, a waiter, the last retirement and, at
// the latest, the first pass after HOUSEKEEPING_NS run them all.
let housekeeping = requested
|| recover
|| !in_flight
|| firmware.recovery.pending()
|| !firmware.queues.teardown.is_empty()
|| self.channels.has_waiters()
|| self.progress.has_waiters()
|| self.queues.has_waiters()
|| self.renders.has_waiters()
|| now.saturating_sub(self.housekeeping_ns.load(core::sync::atomic::Ordering::Relaxed))
>= HOUSEKEEPING_NS;
let mut lingers = None;
let mut pools = None;
let mut shrinking = false;
if !firmware.recovery.pending() {
firmware
.queues
.vacate_evicted_render_pools(&firmware.init, deferred)?;
shrinking = firmware
.queues
.shrink_render_heaps(&firmware.init, now, deferred);
}
{
if housekeeping {
self.housekeeping_ns
.store(now, core::sync::atomic::Ordering::Relaxed);
if !firmware.recovery.pending() {
lingers = firmware.expire_render_lingers(now)?;
}
firmware.reclaim_drained_renders(deferred)?;
firmware.service_compute_exit(deferred)?;
pools = firmware.service_compute_pools(deferred, now)?;
if !firmware.recovery.pending() {
firmware
.queues
.vacate_evicted_render_pools(&firmware.init, deferred)?;
shrinking = firmware
.queues
.shrink_render_heaps(&firmware.init, now, deferred);
}
let mut reclaim = self.reclaim.lock();
firmware.service_teardown(&mut reclaim)?;
if !reclaim.is_empty() {
Expand Down Expand Up @@ -507,7 +532,7 @@ impl crate::g17::Shared {
{
// A full bounded pass must not depend on a fresh notification for
// records that arrived while it was consuming the same ring.
self.queue_events();
self.queue_event_records();
}
let grow_ready = firmware.render_grow_ready();
drop(state);
Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/asahi/g17/runtime/render/direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ impl crate::g17::Firmware {
) -> Result {
self.queues.render.return_pair(slot, &mut loan.pair)?;
if self.queues.render.take_missed_event(slot) {
self.primary.state.shared.queue_events();
// The record is retired by the next pass's observation, like any completion.
self.primary.state.shared.queue_event_records();
}
let entry = self.queues.render.entry_mut(slot)?;
// A failure or timeout recorded against the lent pair is settled by the
Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/asahi/g17/runtime/render/observe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ impl crate::g17::Shared {
let firmware = (*state).as_deref_mut().ok_or(EIO)?;
self.note_changed();
if firmware.render_grow_pending() || firmware.queues.render.take_missed_event(slot as u8) {
self.queue_events();
// Observation and the growth check run on every pass.
self.queue_event_records();
}
if !epoch.is_current()
|| firmware.recovery.pending()
Expand Down