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
2 changes: 2 additions & 0 deletions drivers/gpu/drm/asahi/g16/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1358,5 +1358,7 @@ impl Firmware {
s.starved_ns += s.pub_ns.wrapping_sub(s.starve_from_ns);
}
s.starve_from_ns = 0;
let now = s.pub_ns;
self.power.utilization.busy(now);
}
}
21 changes: 18 additions & 3 deletions drivers/gpu/drm/asahi/g16/poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ impl Shared {
let evented = self.evented.swap(false, Ordering::AcqRel);
match firmware.poll(evented, &mut done) {
Ok(true) => self.rearm(firmware.engine.chan_wait),
// Idle: one more pass after a window gives the state back. A failed
// engine's poll no longer services power, so it has nothing to give back.
Ok(false)
if firmware.recovery.failed().is_none()
&& firmware.power.utilization.decaying() =>
{
self.mod_poll(kernel::time::msecs_to_jiffies(
(super::power::policy::WINDOW_NS / 1_000_000) as u32,
))
}
Ok(false) => {}
Err(error) => {
firmware.fail_stop(error, &mut done);
Expand Down Expand Up @@ -535,6 +545,7 @@ impl Firmware {
if self.engine.flights.is_empty() {
// Drained: a gap until the next publication.
self.engine.stats.idle_from_ns = now_ns();
self.power.utilization.idle(self.engine.stats.idle_from_ns);
}
// The next prefix starts executing about now.
self.renew_deadlines();
Expand Down Expand Up @@ -861,9 +872,13 @@ impl Firmware {
super::recovery::logic::FIRMWARE_STAMP_PERIOD, self.engine.ordinal, self.engine.draw)?;
writeln!(
f,
"power: governor {} budget {} effective S{} temperature_mc {} updates {} sensor_errors {}",
self.power.governor, self.power.budget, self.power.effective,
self.power.temperature_mc, self.power.updates, self.power.sensor_errors
"power: governor {} budget {} demand S{} utilization {} effective S{} temperature_mc {} updates {} sensor_errors {}",
self.power.governor,
self.power.budget,
self.power.utilization.demand,
self.power.utilization.average,
self.power.effective, self.power.temperature_mc, self.power.updates,
self.power.sensor_errors
)?;
writeln!(
f,
Expand Down
18 changes: 14 additions & 4 deletions drivers/gpu/drm/asahi/g16/power.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-only OR MIT

//! PMP-owned cooling requests and retained-session performance ceiling.
//! PMP-owned cooling requests, retained-session performance ceiling and the
//! utilization governor.
//!
//! The firmware owns voltage and clock transitions through PMP. The host
//! consumes PMP-owner telemetry and publishes only three Globals selectors,
//! in hundredths of a performance state. Firmware power tables and policy
//! words from other GPU generations are never modified here.
//!
//! The firmware runs at the published state, busy or not, so the host requests
//! one from the GPU's busy share of each window, bounded by the ceiling.

pub(crate) mod policy;

Expand Down Expand Up @@ -113,6 +117,7 @@ pub(crate) struct Power {
pub(crate) sensor_errors: u64,
pub(crate) decisions: u64,
last_decision: Option<(u32, u32, u32, u32, Option<Error>)>,
pub(crate) utilization: policy::Governor,
}

impl Power {
Expand All @@ -139,6 +144,7 @@ impl Power {
sensor_errors: 0,
decisions: 0,
last_decision: None,
utilization: policy::Governor::new(),
})
}

Expand Down Expand Up @@ -184,6 +190,10 @@ impl Firmware {
}
let now = now_ns();
let (limit, decision) = self.power.ceiling(now);
let target = self
.power
.utilization
.govern(now, limit, !self.engine.flights.is_empty());
if decision {
dev_info!(
self.dev(),
Expand All @@ -197,15 +207,15 @@ impl Firmware {
limit
);
}
if limit == self.power.effective {
if target == self.power.effective {
return Ok(());
}
let globals = self.init.globals.as_ref().ok_or(ENODEV)?;
for offset in globals::PSTATE_SELECTORS {
globals.w32(offset, limit * 100)?;
globals.w32(offset, target * 100)?;
}
wmb();
self.power.effective = limit;
self.power.effective = target;
self.power.updates += 1;
Ok(())
}
Expand Down
Loading