mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-12 01:12:30 -04:00
Use hwmon to determine which sysfs path to use for gpu/power bits.
Instead of always using /sys/class/drm/card0 based paths, use /sys/class/hwmon looking for amdgpu name file to determine where to look. Fixes reading TDPLimit, GPUPerformanceLevel and ManualGPUClock in cases where sysfs uses card1 or other paths. NOTE: Using wrong cardX path is a bug and will be fixed, but this makes steamos-manager a bit more resilient to those bugs.
This commit is contained in:
parent
c82e8df52c
commit
f3e5c8b576
2 changed files with 43 additions and 22 deletions
|
@ -445,6 +445,7 @@ mod test {
|
||||||
|
|
||||||
let proxy = ManualGpuClockProxy::new(&test.connection).await.unwrap();
|
let proxy = ManualGpuClockProxy::new(&test.connection).await.unwrap();
|
||||||
|
|
||||||
|
power::test::setup().await;
|
||||||
assert!(proxy.manual_gpu_clock().await.is_err());
|
assert!(proxy.manual_gpu_clock().await.is_err());
|
||||||
|
|
||||||
power::test::write_clocks(1600).await;
|
power::test::write_clocks(1600).await;
|
||||||
|
|
64
src/power.rs
64
src/power.rs
|
@ -14,11 +14,11 @@ use tracing::error;
|
||||||
|
|
||||||
use crate::{path, write_synced};
|
use crate::{path, write_synced};
|
||||||
|
|
||||||
const GPU_HWMON_PREFIX: &str = "/sys/class/drm/card0/device/hwmon";
|
const GPU_HWMON_PREFIX: &str = "/sys/class/hwmon";
|
||||||
|
const GPU_HWMON_NAME: &str = "amdgpu";
|
||||||
|
|
||||||
const GPU_PERFORMANCE_LEVEL_PATH: &str =
|
const GPU_PERFORMANCE_LEVEL_SUFFIX: &str = "device/power_dpm_force_performance_level";
|
||||||
"/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
const GPU_CLOCKS_SUFFIX: &str = "device/pp_od_clk_voltage";
|
||||||
const GPU_CLOCKS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
|
||||||
|
|
||||||
const TDP_LIMIT1: &str = "power1_cap";
|
const TDP_LIMIT1: &str = "power1_cap";
|
||||||
const TDP_LIMIT2: &str = "power2_cap";
|
const TDP_LIMIT2: &str = "power2_cap";
|
||||||
|
@ -76,7 +76,8 @@ impl ToString for GPUPerformanceLevel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_gpu_performance_level() -> Result<GPUPerformanceLevel> {
|
pub async fn get_gpu_performance_level() -> Result<GPUPerformanceLevel> {
|
||||||
let level = fs::read_to_string(path(GPU_PERFORMANCE_LEVEL_PATH))
|
let base = find_hwmon().await?;
|
||||||
|
let level = fs::read_to_string(base.join(GPU_PERFORMANCE_LEVEL_SUFFIX))
|
||||||
.await
|
.await
|
||||||
.inspect_err(|message| error!("Error opening sysfs file for reading: {message}"))?;
|
.inspect_err(|message| error!("Error opening sysfs file for reading: {message}"))?;
|
||||||
|
|
||||||
|
@ -85,7 +86,8 @@ pub async fn get_gpu_performance_level() -> Result<GPUPerformanceLevel> {
|
||||||
|
|
||||||
pub async fn set_gpu_performance_level(level: GPUPerformanceLevel) -> Result<()> {
|
pub async fn set_gpu_performance_level(level: GPUPerformanceLevel) -> Result<()> {
|
||||||
let level: String = level.to_string();
|
let level: String = level.to_string();
|
||||||
write_synced(path(GPU_PERFORMANCE_LEVEL_PATH), level.as_bytes())
|
let base = find_hwmon().await?;
|
||||||
|
write_synced(base.join(GPU_PERFORMANCE_LEVEL_SUFFIX), level.as_bytes())
|
||||||
.await
|
.await
|
||||||
.inspect_err(|message| error!("Error writing to sysfs file: {message}"))
|
.inspect_err(|message| error!("Error writing to sysfs file: {message}"))
|
||||||
}
|
}
|
||||||
|
@ -95,7 +97,8 @@ pub async fn set_gpu_clocks(clocks: u32) -> Result<()> {
|
||||||
// Only used when GPU Performance Level is manual, but write whenever called.
|
// Only used when GPU Performance Level is manual, but write whenever called.
|
||||||
ensure!((200..=1600).contains(&clocks), "Invalid clocks");
|
ensure!((200..=1600).contains(&clocks), "Invalid clocks");
|
||||||
|
|
||||||
let mut myfile = File::create(path(GPU_CLOCKS_PATH))
|
let base = find_hwmon().await?;
|
||||||
|
let mut myfile = File::create(base.join(GPU_CLOCKS_SUFFIX))
|
||||||
.await
|
.await
|
||||||
.inspect_err(|message| error!("Error opening sysfs file for writing: {message}"))?;
|
.inspect_err(|message| error!("Error opening sysfs file for writing: {message}"))?;
|
||||||
|
|
||||||
|
@ -123,7 +126,8 @@ pub async fn set_gpu_clocks(clocks: u32) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_gpu_clocks() -> Result<u32> {
|
pub async fn get_gpu_clocks() -> Result<u32> {
|
||||||
let clocks_file = File::open(path(GPU_CLOCKS_PATH)).await?;
|
let base = find_hwmon().await?;
|
||||||
|
let clocks_file = File::open(base.join(GPU_CLOCKS_SUFFIX)).await?;
|
||||||
let mut reader = BufReader::new(clocks_file);
|
let mut reader = BufReader::new(clocks_file);
|
||||||
loop {
|
loop {
|
||||||
let mut line = String::new();
|
let mut line = String::new();
|
||||||
|
@ -155,7 +159,12 @@ async fn find_hwmon() -> Result<PathBuf> {
|
||||||
Some(entry) => entry.path(),
|
Some(entry) => entry.path(),
|
||||||
None => bail!("hwmon not found"),
|
None => bail!("hwmon not found"),
|
||||||
};
|
};
|
||||||
if fs::try_exists(base.join(TDP_LIMIT1)).await? {
|
let file_name = base.join("name");
|
||||||
|
let name = fs::read_to_string(file_name.as_path())
|
||||||
|
.await?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
if name == GPU_HWMON_NAME {
|
||||||
return Ok(base);
|
return Ok(base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,14 +208,23 @@ pub mod test {
|
||||||
use tokio::fs::{create_dir_all, read_to_string, remove_dir, write};
|
use tokio::fs::{create_dir_all, read_to_string, remove_dir, write};
|
||||||
|
|
||||||
pub async fn setup() {
|
pub async fn setup() {
|
||||||
let filename = path(GPU_PERFORMANCE_LEVEL_PATH);
|
// Use hwmon5 just as a test. We needed a subfolder of GPU_HWMON_PREFIX
|
||||||
|
// and this is as good as any.
|
||||||
|
let base = path(GPU_HWMON_PREFIX).join("hwmon5");
|
||||||
|
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||||
|
// Creates hwmon path, including device subpath
|
||||||
create_dir_all(filename.parent().unwrap())
|
create_dir_all(filename.parent().unwrap())
|
||||||
.await
|
.await
|
||||||
.expect("create_dir_all");
|
.expect("create_dir_all");
|
||||||
|
// Writes name file as addgpu so find_hwmon() will find it.
|
||||||
|
write_synced(base.join("name"), GPU_HWMON_NAME.as_bytes())
|
||||||
|
.await
|
||||||
|
.expect("write_synced");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_clocks(mhz: u32) {
|
pub async fn write_clocks(mhz: u32) {
|
||||||
let filename = path(GPU_CLOCKS_PATH);
|
let base = find_hwmon().await.unwrap();
|
||||||
|
let filename = base.join(GPU_CLOCKS_SUFFIX);
|
||||||
create_dir_all(filename.parent().unwrap())
|
create_dir_all(filename.parent().unwrap())
|
||||||
.await
|
.await
|
||||||
.expect("create_dir_all");
|
.expect("create_dir_all");
|
||||||
|
@ -227,7 +245,10 @@ CCLK_RANGE in Core0:
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn expect_clocks(mhz: u32) {
|
pub async fn expect_clocks(mhz: u32) {
|
||||||
let clocks = read_to_string(path(GPU_CLOCKS_PATH)).await.expect("read");
|
let base = find_hwmon().await.unwrap();
|
||||||
|
let clocks = read_to_string(base.join(GPU_CLOCKS_SUFFIX))
|
||||||
|
.await
|
||||||
|
.expect("read");
|
||||||
assert_eq!(clocks, format!("s 0 {mhz}\ns 1 {mhz}\nc\n"));
|
assert_eq!(clocks, format!("s 0 {mhz}\ns 1 {mhz}\nc\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,8 +256,9 @@ CCLK_RANGE in Core0:
|
||||||
async fn test_get_gpu_performance_level() {
|
async fn test_get_gpu_performance_level() {
|
||||||
let _h = testing::start();
|
let _h = testing::start();
|
||||||
|
|
||||||
let filename = path(GPU_PERFORMANCE_LEVEL_PATH);
|
|
||||||
setup().await;
|
setup().await;
|
||||||
|
let base = find_hwmon().await.unwrap();
|
||||||
|
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||||
assert!(get_gpu_performance_level().await.is_err());
|
assert!(get_gpu_performance_level().await.is_err());
|
||||||
|
|
||||||
write(filename.as_path(), "auto\n").await.expect("write");
|
write(filename.as_path(), "auto\n").await.expect("write");
|
||||||
|
@ -279,8 +301,9 @@ CCLK_RANGE in Core0:
|
||||||
async fn test_set_gpu_performance_level() {
|
async fn test_set_gpu_performance_level() {
|
||||||
let _h = testing::start();
|
let _h = testing::start();
|
||||||
|
|
||||||
let filename = path(GPU_PERFORMANCE_LEVEL_PATH);
|
|
||||||
setup().await;
|
setup().await;
|
||||||
|
let base = find_hwmon().await.unwrap();
|
||||||
|
let filename = base.join(GPU_PERFORMANCE_LEVEL_SUFFIX);
|
||||||
|
|
||||||
set_gpu_performance_level(GPUPerformanceLevel::Auto)
|
set_gpu_performance_level(GPUPerformanceLevel::Auto)
|
||||||
.await
|
.await
|
||||||
|
@ -323,10 +346,8 @@ CCLK_RANGE in Core0:
|
||||||
async fn test_get_tdp_limit() {
|
async fn test_get_tdp_limit() {
|
||||||
let _h = testing::start();
|
let _h = testing::start();
|
||||||
|
|
||||||
|
setup().await;
|
||||||
let hwmon = path(GPU_HWMON_PREFIX);
|
let hwmon = path(GPU_HWMON_PREFIX);
|
||||||
create_dir_all(hwmon.join("hwmon5").as_path())
|
|
||||||
.await
|
|
||||||
.expect("create_dir_all");
|
|
||||||
|
|
||||||
assert!(get_tdp_limit().await.is_err());
|
assert!(get_tdp_limit().await.is_err());
|
||||||
|
|
||||||
|
@ -351,14 +372,12 @@ CCLK_RANGE in Core0:
|
||||||
assert!(set_tdp_limit(10).await.is_err());
|
assert!(set_tdp_limit(10).await.is_err());
|
||||||
|
|
||||||
let hwmon = path(GPU_HWMON_PREFIX);
|
let hwmon = path(GPU_HWMON_PREFIX);
|
||||||
create_dir_all(hwmon.as_path())
|
|
||||||
.await
|
|
||||||
.expect("create_dir_all");
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
set_tdp_limit(10).await.unwrap_err().to_string(),
|
set_tdp_limit(10).await.unwrap_err().to_string(),
|
||||||
anyhow!("hwmon not found").to_string()
|
anyhow!("No such file or directory (os error 2)").to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setup().await;
|
||||||
let hwmon = hwmon.join("hwmon5");
|
let hwmon = hwmon.join("hwmon5");
|
||||||
create_dir_all(hwmon.join(TDP_LIMIT1))
|
create_dir_all(hwmon.join(TDP_LIMIT1))
|
||||||
.await
|
.await
|
||||||
|
@ -403,7 +422,8 @@ CCLK_RANGE in Core0:
|
||||||
assert!(get_gpu_clocks().await.is_err());
|
assert!(get_gpu_clocks().await.is_err());
|
||||||
setup().await;
|
setup().await;
|
||||||
|
|
||||||
let filename = path(GPU_CLOCKS_PATH);
|
let base = find_hwmon().await.unwrap();
|
||||||
|
let filename = base.join(GPU_CLOCKS_SUFFIX);
|
||||||
create_dir_all(filename.parent().unwrap())
|
create_dir_all(filename.parent().unwrap())
|
||||||
.await
|
.await
|
||||||
.expect("create_dir_all");
|
.expect("create_dir_all");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue