power: Query GPU clock range from pp_dpm_sclk

This commit is contained in:
Vicki Pfau 2024-07-30 20:13:48 -07:00
parent 6867c4e870
commit cf9253a709
2 changed files with 64 additions and 7 deletions

View file

@ -22,7 +22,7 @@ use crate::error::{to_zbus_error, to_zbus_fdo_error, zbus_to_zbus_fdo};
use crate::hardware::check_support; use crate::hardware::check_support;
use crate::job::JobManagerCommand; use crate::job::JobManagerCommand;
use crate::power::{ use crate::power::{
get_available_cpu_scaling_governors, get_cpu_scaling_governor, get_gpu_clocks, get_available_cpu_scaling_governors, get_cpu_scaling_governor, get_gpu_clocks, get_gpu_clocks_range,
get_gpu_performance_level, get_gpu_power_profile, get_gpu_power_profiles, get_tdp_limit, get_gpu_performance_level, get_gpu_power_profile, get_gpu_power_profiles, get_tdp_limit,
}; };
use crate::wifi::{get_wifi_backend, get_wifi_power_management_state}; use crate::wifi::{get_wifi_backend, get_wifi_power_management_state};
@ -309,15 +309,13 @@ impl SteamOSManager {
} }
#[zbus(property(emits_changed_signal = "const"))] #[zbus(property(emits_changed_signal = "const"))]
async fn manual_gpu_clock_min(&self) -> u32 { async fn manual_gpu_clock_min(&self) -> fdo::Result<u32> {
// TODO: Can this be queried from somewhere? Ok(get_gpu_clocks_range().await.map_err(to_zbus_fdo_error)?.0)
200
} }
#[zbus(property(emits_changed_signal = "const"))] #[zbus(property(emits_changed_signal = "const"))]
async fn manual_gpu_clock_max(&self) -> u32 { async fn manual_gpu_clock_max(&self) -> fdo::Result<u32> {
// TODO: Can this be queried from somewhere? Ok(get_gpu_clocks_range().await.map_err(to_zbus_fdo_error)?.1)
1600
} }
#[zbus(property(emits_changed_signal = "false"))] #[zbus(property(emits_changed_signal = "false"))]

View file

@ -29,6 +29,7 @@ const CPU_POLICY_NAME: &str = "policy";
const GPU_POWER_PROFILE_SUFFIX: &str = "device/pp_power_profile_mode"; const GPU_POWER_PROFILE_SUFFIX: &str = "device/pp_power_profile_mode";
const GPU_PERFORMANCE_LEVEL_SUFFIX: &str = "device/power_dpm_force_performance_level"; const GPU_PERFORMANCE_LEVEL_SUFFIX: &str = "device/power_dpm_force_performance_level";
const GPU_CLOCKS_SUFFIX: &str = "device/pp_od_clk_voltage"; const GPU_CLOCKS_SUFFIX: &str = "device/pp_od_clk_voltage";
const GPU_CLOCK_LEVELS_SUFFIX: &str = "device/pp_dpm_sclk";
const CPU_SCALING_GOVERNOR_SUFFIX: &str = "scaling_governor"; const CPU_SCALING_GOVERNOR_SUFFIX: &str = "scaling_governor";
const CPU_SCALING_AVAILABLE_GOVERNORS_SUFFIX: &str = "scaling_available_governors"; const CPU_SCALING_AVAILABLE_GOVERNORS_SUFFIX: &str = "scaling_available_governors";
@ -38,6 +39,8 @@ const TDP_LIMIT2: &str = "power2_cap";
lazy_static! { lazy_static! {
static ref GPU_POWER_PROFILE_REGEX: Regex = static ref GPU_POWER_PROFILE_REGEX: Regex =
Regex::new(r"^\s*(?<value>[0-9]+)\s+(?<name>[0-9A-Za-z_]+)(?<active>\*)?").unwrap(); Regex::new(r"^\s*(?<value>[0-9]+)\s+(?<name>[0-9A-Za-z_]+)(?<active>\*)?").unwrap();
static ref GPU_CLOCK_LEVELS_REGEX: Regex =
Regex::new(r"^\s*(?<index>[0-9]+): (?<value>[0-9]+)Mhz").unwrap();
} }
#[derive(Display, EnumString, PartialEq, Debug, Copy, Clone)] #[derive(Display, EnumString, PartialEq, Debug, Copy, Clone)]
@ -274,6 +277,33 @@ pub(crate) async fn set_cpu_scaling_governor(governor: CPUScalingGovernor) -> Re
write_cpu_governor_sysfs_contents(name).await write_cpu_governor_sysfs_contents(name).await
} }
pub(crate) async fn get_gpu_clocks_range() -> Result<(u32, u32)> {
let contents = read_gpu_sysfs_contents(GPU_CLOCK_LEVELS_SUFFIX).await?;
let lines = contents.lines();
let mut min = 1000000;
let mut max = 0;
for line in lines {
let caps = GPU_CLOCK_LEVELS_REGEX.captures(line);
let caps = match caps {
Some(caps) => caps,
None => continue,
};
let value: u32 = caps["value"]
.parse()
.map_err(|message| anyhow!("Unable to parse value for GPU power profile: {message}"))?;
if value < min {
min = value;
}
if value > max {
max = value;
}
}
ensure!(min <= max, "Could not read any clocks");
Ok((min, max))
}
pub(crate) async fn set_gpu_clocks(clocks: u32) -> Result<()> { pub(crate) async fn set_gpu_clocks(clocks: u32) -> Result<()> {
// Set GPU clocks to given value valid // Set GPU clocks to given value valid
// Only used when GPU Performance Level is manual, but write whenever called. // Only used when GPU Performance Level is manual, but write whenever called.
@ -633,6 +663,35 @@ CCLK_RANGE in Core0:
assert_eq!(read_clocks().await.unwrap(), format_clocks(1600)); assert_eq!(read_clocks().await.unwrap(), format_clocks(1600));
} }
#[tokio::test]
async fn test_get_gpu_clocks_range() {
let _h = testing::start();
setup().await;
let base = find_hwmon().await.unwrap();
let filename = base.join(GPU_CLOCK_LEVELS_SUFFIX);
create_dir_all(filename.parent().unwrap())
.await
.expect("create_dir_all");
assert!(get_gpu_clocks_range().await.is_err());
write(filename.as_path(), &[] as &[u8; 0]).await.expect("write");
assert!(get_gpu_clocks_range().await.is_err());
let contents = "0: 200Mhz *
1: 1100Mhz
2: 1600Mhz";
write(filename.as_path(), contents).await.expect("write");
assert_eq!(get_gpu_clocks_range().await.unwrap(), (200, 1600));
let contents = "0: 1600Mhz *
1: 200Mhz
2: 1100Mhz";
write(filename.as_path(), contents).await.expect("write");
assert_eq!(get_gpu_clocks_range().await.unwrap(), (200, 1600));
}
#[test] #[test]
fn gpu_power_profile_roundtrip() { fn gpu_power_profile_roundtrip() {
enum_roundtrip!(GPUPowerProfile { enum_roundtrip!(GPUPowerProfile {