feat: basic gpu clock configuration

This commit is contained in:
Ilya Zlobintsev 2024-12-20 11:22:00 +02:00
parent 22bf7257f8
commit 29f04bd687
5 changed files with 146 additions and 35 deletions

View File

@ -1,10 +1,11 @@
use super::GpuController; use super::GpuController;
use crate::{config, server::vulkan::get_vulkan_info}; use crate::{config, server::vulkan::get_vulkan_info};
use amdgpu_sysfs::gpu_handle::power_profile_mode::PowerProfileModesTable; use amdgpu_sysfs::gpu_handle::power_profile_mode::PowerProfileModesTable;
use anyhow::anyhow; use anyhow::{anyhow, Context};
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use lact_schema::{ use lact_schema::{
ClocksInfo, ClockspeedStats, DeviceInfo, DeviceStats, GpuPciInfo, LinkInfo, PowerStates, ClocksInfo, ClocksTable, ClockspeedStats, DeviceInfo, DeviceStats, GpuPciInfo,
IntelClocksTable, LinkInfo, PowerStates,
}; };
use std::{ use std::{
fmt::Display, fmt::Display,
@ -14,15 +15,15 @@ use std::{
}; };
use tracing::{error, info, warn}; use tracing::{error, info, warn};
enum DriverType { // enum DriverType {
Xe, // Xe,
I915, // I915,
} // }
pub struct IntelGpuController { pub struct IntelGpuController {
sysfs_path: PathBuf, sysfs_path: PathBuf,
driver: String, driver: String,
driver_type: DriverType, // driver_type: DriverType,
pci_slot_id: Option<String>, pci_slot_id: Option<String>,
pci_info: GpuPciInfo, pci_info: GpuPciInfo,
tile_gts: Vec<PathBuf>, tile_gts: Vec<PathBuf>,
@ -35,15 +36,14 @@ impl IntelGpuController {
pci_slot_id: Option<String>, pci_slot_id: Option<String>,
pci_info: GpuPciInfo, pci_info: GpuPciInfo,
) -> Self { ) -> Self {
let driver_type = match driver.as_str() { // let driver_type = match driver.as_str() {
"xe" => DriverType::Xe, // "xe" => DriverType::Xe,
"i915" => DriverType::I915, // "i915" => DriverType::I915,
_ => unreachable!(), // _ => unreachable!(),
}; // };
let mut tile_gts = vec![]; let mut tile_gts = vec![];
if let DriverType::Xe = driver_type {
for entry in fs::read_dir(&sysfs_path).into_iter().flatten().flatten() { for entry in fs::read_dir(&sysfs_path).into_iter().flatten().flatten() {
if let Some(name) = entry.file_name().to_str() { if let Some(name) = entry.file_name().to_str() {
if name.starts_with("tile") { if name.starts_with("tile") {
@ -63,12 +63,10 @@ impl IntelGpuController {
tile_gts.len(), tile_gts.len(),
sysfs_path.display() sysfs_path.display()
); );
}
Self { Self {
sysfs_path, sysfs_path,
driver, driver,
driver_type,
pci_slot_id, pci_slot_id,
pci_info, pci_info,
tile_gts, tile_gts,
@ -133,9 +131,20 @@ impl GpuController for IntelGpuController {
fn apply_config<'a>( fn apply_config<'a>(
&'a self, &'a self,
_config: &'a config::Gpu, config: &'a config::Gpu,
) -> LocalBoxFuture<'a, anyhow::Result<()>> { ) -> LocalBoxFuture<'a, anyhow::Result<()>> {
Box::pin(async { Ok(()) }) Box::pin(async {
if let Some(max_clock) = config.clocks_configuration.max_core_clock {
self.write_gt_file("freq0/max_freq", &max_clock.to_string())
.context("Could not set max clock")?;
}
if let Some(min_clock) = config.clocks_configuration.min_core_clock {
self.write_gt_file("freq0/min_freq", &min_clock.to_string())
.context("Could not set min clock")?;
}
Ok(())
})
} }
fn get_stats(&self, _gpu_config: Option<&config::Gpu>) -> DeviceStats { fn get_stats(&self, _gpu_config: Option<&config::Gpu>) -> DeviceStats {
@ -157,7 +166,25 @@ impl GpuController for IntelGpuController {
} }
fn get_clocks_info(&self) -> anyhow::Result<ClocksInfo> { fn get_clocks_info(&self) -> anyhow::Result<ClocksInfo> {
Err(anyhow!("Not supported")) let clocks_table = IntelClocksTable {
gt_freq: self
.read_gt_file("freq0/min_freq")
.zip(self.read_gt_file("freq0/max_freq")),
rp0_freq: self.read_gt_file("freq0/rp0_freq"),
rpe_freq: self.read_gt_file("freq0/rpe_freq"),
rpn_freq: self.read_gt_file("freq0/rpn_freq"),
};
let table = if clocks_table == IntelClocksTable::default() {
None
} else {
Some(ClocksTable::Intel(clocks_table))
};
Ok(ClocksInfo {
table,
..Default::default()
})
} }
fn get_power_states(&self, _gpu_config: Option<&config::Gpu>) -> PowerStates { fn get_power_states(&self, _gpu_config: Option<&config::Gpu>) -> PowerStates {
@ -189,7 +216,9 @@ impl IntelGpuController {
T: FromStr, T: FromStr,
T::Err: Display, T::Err: Display,
{ {
if let Some(file_path) = self.first_tile_gt().map(|path| path.join(file_name)) { if let Some(gt_path) = self.first_tile_gt() {
let file_path = gt_path.join(file_name);
if file_path.exists() { if file_path.exists() {
match fs::read_to_string(&file_path) { match fs::read_to_string(&file_path) {
Ok(contents) => match contents.trim().parse() { Ok(contents) => match contents.trim().parse() {
@ -210,4 +239,20 @@ impl IntelGpuController {
None None
} }
fn write_gt_file(&self, file_name: &str, contents: &str) -> anyhow::Result<()> {
if let Some(gt_path) = self.first_tile_gt() {
let file_path = gt_path.join(file_name);
if file_path.exists() {
fs::write(&file_path, contents)
.with_context(|| format!("Could not write to '{}'", file_path.display()))?;
Ok(())
} else {
Err(anyhow!("File '{}' does not exist", file_path.display()))
}
} else {
Err(anyhow!("No GTs available"))
}
}
} }

View File

@ -3,7 +3,7 @@ source: lact-daemon/src/tests/mod.rs
expression: device_info expression: device_info
--- ---
{ {
"clocks_info": null, "clocks_info": {},
"info": { "info": {
"driver": "i915", "driver": "i915",
"link_info": {}, "link_info": {},

View File

@ -3,7 +3,20 @@ source: lact-daemon/src/tests/mod.rs
expression: device_info expression: device_info
--- ---
{ {
"clocks_info": null, "clocks_info": {
"table": {
"type": "intel",
"value": {
"gt_freq": [
1100,
1300
],
"rp0_freq": 1300,
"rpe_freq": 600,
"rpn_freq": 100
}
}
},
"info": { "info": {
"driver": "xe", "driver": "xe",
"link_info": {}, "link_info": {},

View File

@ -7,7 +7,9 @@ use amdgpu_sysfs::gpu_handle::overdrive::{ClocksTable as _, ClocksTableGen as Am
use glib::clone; use glib::clone;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::*; use gtk::*;
use lact_schema::{request::SetClocksCommand, ClocksTable, NvidiaClockInfo, NvidiaClocksTable}; use lact_schema::{
request::SetClocksCommand, ClocksTable, IntelClocksTable, NvidiaClockInfo, NvidiaClocksTable,
};
use subclass::prelude::ObjectSubclassIsExt; use subclass::prelude::ObjectSubclassIsExt;
use tracing::debug; use tracing::debug;
@ -165,6 +167,7 @@ impl ClocksFrame {
match table { match table {
ClocksTable::Amd(table) => self.set_amd_table(table), ClocksTable::Amd(table) => self.set_amd_table(table),
ClocksTable::Nvidia(table) => self.set_nvidia_table(table), ClocksTable::Nvidia(table) => self.set_nvidia_table(table),
ClocksTable::Intel(table) => self.set_intel_table(table),
} }
for adjustment in adjustments { for adjustment in adjustments {
@ -284,6 +287,42 @@ impl ClocksFrame {
} }
} }
fn set_intel_table(&self, table: IntelClocksTable) {
if let Some((current_gt_min, current_gt_max)) = table.gt_freq {
if let (Some(min_clock), Some(max_clock)) = (table.rpn_freq, table.rp0_freq) {
self.min_sclk_adjustment
.imp()
.adjustment
.set_lower(min_clock as f64);
self.min_sclk_adjustment
.imp()
.adjustment
.set_upper(max_clock as f64);
self.min_sclk_adjustment
.imp()
.adjustment
.set_value(current_gt_min as f64);
self.min_sclk_adjustment.set_visible(true);
self.max_sclk_adjustment
.imp()
.adjustment
.set_lower(min_clock as f64);
self.max_sclk_adjustment
.imp()
.adjustment
.set_upper(max_clock as f64);
self.max_sclk_adjustment
.imp()
.adjustment
.set_value(current_gt_max as f64);
self.max_sclk_adjustment.set_visible(true);
}
}
}
fn set_nvidia_table(&self, table: NvidiaClocksTable) { fn set_nvidia_table(&self, table: NvidiaClocksTable) {
if let Some(gpc_info) = &table.gpc { if let Some(gpc_info) = &table.gpc {
set_nvidia_clock_offset(gpc_info, &self.max_sclk_adjustment); set_nvidia_clock_offset(gpc_info, &self.max_sclk_adjustment);

View File

@ -146,6 +146,7 @@ pub struct ClocksInfo {
pub enum ClocksTable { pub enum ClocksTable {
Amd(AmdClocksTableGen), Amd(AmdClocksTableGen),
Nvidia(NvidiaClocksTable), Nvidia(NvidiaClocksTable),
Intel(IntelClocksTable),
} }
#[skip_serializing_none] #[skip_serializing_none]
@ -155,6 +156,19 @@ pub struct NvidiaClocksTable {
pub mem: Option<NvidiaClockInfo>, pub mem: Option<NvidiaClockInfo>,
} }
/// Doc from `xe_gt_freq.c`
#[skip_serializing_none]
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
pub struct IntelClocksTable {
pub gt_freq: Option<(u32, u32)>,
/// - rpn_freq: The Render Performance (RP) N level, which is the minimal one.
pub rpn_freq: Option<u32>,
/// - rpe_freq: The Render Performance (RP) E level, which is the efficient one.
pub rpe_freq: Option<u32>,
/// - rp0_freq: The Render Performance (RP) 0 level, which is the maximum one.
pub rp0_freq: Option<u32>,
}
#[derive(Serialize, Deserialize, Default, Debug, Clone)] #[derive(Serialize, Deserialize, Default, Debug, Clone)]
pub struct NvidiaClockInfo { pub struct NvidiaClockInfo {
pub max: i32, pub max: i32,