feat: use VRAM offset ratio on Ada, add config migration system to erase old

memory clock settings
This commit is contained in:
Ilya Zlobintsev
2025-01-05 11:59:26 +02:00
parent e93025164e
commit 740e38863b
8 changed files with 138 additions and 9 deletions

View File

@@ -1,4 +1,4 @@
use crate::server::gpu_controller::fan_control::FanCurve; use crate::server::gpu_controller::{fan_control::FanCurve, VENDOR_NVIDIA};
use amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind}; use amdgpu_sysfs::gpu_handle::{PerformanceLevel, PowerLevelKind};
use anyhow::Context; use anyhow::Context;
use indexmap::IndexMap; use indexmap::IndexMap;
@@ -18,7 +18,7 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use tokio::{sync::mpsc, time}; use tokio::{sync::mpsc, time};
use tracing::{debug, error}; use tracing::{debug, error, info};
const FILE_NAME: &str = "config.yaml"; const FILE_NAME: &str = "config.yaml";
const DEFAULT_ADMIN_GROUPS: [&str; 2] = ["wheel", "sudo"]; const DEFAULT_ADMIN_GROUPS: [&str; 2] = ["wheel", "sudo"];
@@ -29,6 +29,8 @@ const SELF_CONFIG_EDIT_PERIOD_MILLIS: u64 = 1000;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Config { pub struct Config {
#[serde(default)]
pub version: u64,
pub daemon: Daemon, pub daemon: Daemon,
#[serde(default = "default_apply_settings_timer")] #[serde(default = "default_apply_settings_timer")]
pub apply_settings_timer: u64, pub apply_settings_timer: u64,
@@ -51,6 +53,7 @@ impl Default for Config {
profiles: IndexMap::new(), profiles: IndexMap::new(),
current_profile: None, current_profile: None,
auto_switch_profiles: false, auto_switch_profiles: false,
version: 0,
} }
} }
} }
@@ -206,6 +209,36 @@ impl Config {
} }
} }
pub fn migrate_versions(&mut self) {
loop {
let next_version = self.version + 1;
match next_version {
0 => unreachable!(),
// Reset VRAM settings on Nvidia after new offset ratio logic
1 => {
for (id, gpu) in &mut self.gpus {
if id.starts_with(VENDOR_NVIDIA) {
gpu.clocks_configuration.max_memory_clock = None;
gpu.clocks_configuration.min_memory_clock = None;
}
}
for profile in &mut self.profiles.values_mut() {
for (id, gpu) in &mut profile.gpus {
if id.starts_with(VENDOR_NVIDIA) {
gpu.clocks_configuration.max_memory_clock = None;
gpu.clocks_configuration.min_memory_clock = None;
}
}
}
}
_ => break,
}
info!("migrated config version {} to {next_version}", self.version);
self.version = next_version;
}
}
/// Gets the GPU configs according to the current profile. Returns an error if the current profile could not be found. /// Gets the GPU configs according to the current profile. Returns an error if the current profile could not be found.
pub fn gpus(&self) -> anyhow::Result<&IndexMap<String, Gpu>> { pub fn gpus(&self) -> anyhow::Result<&IndexMap<String, Gpu>> {
match &self.current_profile { match &self.current_profile {
@@ -364,6 +397,7 @@ fn default_apply_settings_timer() -> u64 {
mod tests { mod tests {
use super::{ClocksConfiguration, Config, Daemon, FanControlSettings, Gpu}; use super::{ClocksConfiguration, Config, Daemon, FanControlSettings, Gpu};
use crate::server::gpu_controller::fan_control::FanCurve; use crate::server::gpu_controller::fan_control::FanCurve;
use indexmap::IndexMap;
use insta::assert_yaml_snapshot; use insta::assert_yaml_snapshot;
use lact_schema::{FanControlMode, PmfwOptions}; use lact_schema::{FanControlMode, PmfwOptions};
use std::collections::HashMap; use std::collections::HashMap;
@@ -431,4 +465,70 @@ mod tests {
gpu.clocks_configuration.voltage_offset = Some(10); gpu.clocks_configuration.voltage_offset = Some(10);
assert!(gpu.is_core_clocks_used()); assert!(gpu.is_core_clocks_used());
} }
#[test]
fn migrate_versions() {
let mut config = Config {
version: 0,
daemon: Daemon::default(),
apply_settings_timer: 5,
gpus: IndexMap::from([
(
"10DE:2704-1462:5110-0000:09:00.0".to_owned(),
Gpu {
clocks_configuration: ClocksConfiguration {
max_core_clock: Some(3000),
max_memory_clock: Some(10_000),
..Default::default()
},
..Default::default()
},
),
(
"1002:687F-1043:0555-0000:0b:00.0".to_owned(),
Gpu {
clocks_configuration: ClocksConfiguration {
max_core_clock: Some(1500),
max_memory_clock: Some(920),
..Default::default()
},
..Default::default()
},
),
]),
profiles: IndexMap::new(),
current_profile: None,
auto_switch_profiles: false,
};
config.migrate_versions();
assert_eq!(
config
.gpus
.get("10DE:2704-1462:5110-0000:09:00.0")
.unwrap()
.clocks_configuration
.max_core_clock,
Some(3000)
);
assert_eq!(
config
.gpus
.get("10DE:2704-1462:5110-0000:09:00.0")
.unwrap()
.clocks_configuration
.max_memory_clock,
None,
);
assert_eq!(
config
.gpus
.get("1002:687F-1043:0555-0000:0b:00.0")
.unwrap()
.clocks_configuration
.max_memory_clock,
Some(920),
);
}
} }

View File

@@ -12,6 +12,8 @@ use anyhow::Context;
use config::Config; use config::Config;
use futures::future::select_all; use futures::future::select_all;
use server::{handle_stream, handler::Handler, Server}; use server::{handle_stream, handler::Handler, Server};
use std::cell::Cell;
use std::time::Instant;
use std::{os::unix::net::UnixStream as StdUnixStream, time::Duration}; use std::{os::unix::net::UnixStream as StdUnixStream, time::Duration};
use tokio::net::UnixStream; use tokio::net::UnixStream;
use tokio::{ use tokio::{
@@ -46,7 +48,7 @@ pub fn run() -> anyhow::Result<()> {
.build() .build()
.expect("Could not initialize tokio runtime"); .expect("Could not initialize tokio runtime");
rt.block_on(async { rt.block_on(async {
let config = Config::load_or_create()?; let mut config = Config::load_or_create()?;
let env_filter = EnvFilter::builder() let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into()) .with_default_directive(LevelFilter::INFO.into())
@@ -54,6 +56,12 @@ pub fn run() -> anyhow::Result<()> {
.context("Invalid log level")?; .context("Invalid log level")?;
tracing_subscriber::fmt().with_env_filter(env_filter).init(); tracing_subscriber::fmt().with_env_filter(env_filter).init();
let original_version = config.version;
config.migrate_versions();
if config.version != original_version {
config.save(&Cell::new(Instant::now()))?;
}
ensure_sufficient_uptime().await; ensure_sufficient_uptime().await;
LocalSet::new() LocalSet::new()

View File

@@ -1,4 +1,4 @@
use super::{fan_control::FanCurve, FanControlHandle, GpuController}; use super::{fan_control::FanCurve, FanControlHandle, GpuController, VENDOR_AMD};
use crate::{ use crate::{
config::{self, ClocksConfiguration, FanControlSettings}, config::{self, ClocksConfiguration, FanControlSettings},
server::vulkan::get_vulkan_info, server::vulkan::get_vulkan_info,
@@ -47,7 +47,6 @@ use {
const GPU_CLOCKDOWN_TIMEOUT_SECS: u64 = 3; const GPU_CLOCKDOWN_TIMEOUT_SECS: u64 = 3;
const MAX_PSTATE_READ_ATTEMPTS: u32 = 5; const MAX_PSTATE_READ_ATTEMPTS: u32 = 5;
const VENDOR_AMD: &str = "1002";
const STEAM_DECK_IDS: [&str; 2] = ["163F", "1435"]; const STEAM_DECK_IDS: [&str; 2] = ["163F", "1435"];
pub struct AmdGpuController { pub struct AmdGpuController {

View File

@@ -3,6 +3,9 @@ mod amd;
pub mod fan_control; pub mod fan_control;
mod nvidia; mod nvidia;
pub const VENDOR_AMD: &str = "1002";
pub const VENDOR_NVIDIA: &str = "10DE";
pub use amd::AmdGpuController; pub use amd::AmdGpuController;
pub use nvidia::NvidiaGpuController; pub use nvidia::NvidiaGpuController;

View File

@@ -17,7 +17,8 @@ use lact_schema::{
}; };
use nvml_wrapper::{ use nvml_wrapper::{
bitmasks::device::ThrottleReasons, bitmasks::device::ThrottleReasons,
enum_wrappers::device::{Clock, TemperatureSensor, TemperatureThreshold}, enum_wrappers::device::{Brand, Clock, TemperatureSensor, TemperatureThreshold},
enums::device::DeviceArchitecture,
Device, Nvml, Device, Nvml,
}; };
use std::{ use std::{
@@ -248,6 +249,20 @@ impl NvidiaGpuController {
Ok(power_states) Ok(power_states)
} }
// See https://github.com/ilya-zlobintsev/LACT/issues/418
fn vram_offset_ratio(&self) -> i32 {
let device = self.device();
if let (Ok(brand), Ok(architecture)) = (device.brand(), device.architecture()) {
let ratio = match (brand, architecture) {
(Brand::GeForce, DeviceArchitecture::Ada) => 2,
// TODO: check others
_ => 1,
};
return ratio;
}
1
}
} }
impl GpuController for NvidiaGpuController { impl GpuController for NvidiaGpuController {
@@ -495,6 +510,7 @@ impl GpuController for NvidiaGpuController {
gpc = Some(NvidiaClockInfo { gpc = Some(NvidiaClockInfo {
max: max as i32, max: max as i32,
offset, offset,
offset_ratio: 1,
offset_range, offset_range,
}); });
} }
@@ -511,6 +527,7 @@ impl GpuController for NvidiaGpuController {
mem = Some(NvidiaClockInfo { mem = Some(NvidiaClockInfo {
max: max as i32, max: max as i32,
offset, offset,
offset_ratio: self.vram_offset_ratio(),
offset_range, offset_range,
}); });
} }
@@ -600,7 +617,7 @@ impl GpuController for NvidiaGpuController {
let default_max_clock = device let default_max_clock = device
.max_clock_info(Clock::Memory) .max_clock_info(Clock::Memory)
.context("Could not read max memory clock")?; .context("Could not read max memory clock")?;
let offset = max_mem_clock - default_max_clock as i32; let offset = (max_mem_clock - default_max_clock as i32) * self.vram_offset_ratio();
debug!("Using mem clock offset {offset} (default max clock: {default_max_clock})"); debug!("Using mem clock offset {offset} (default max clock: {default_max_clock})");
device device

View File

@@ -1,8 +1,8 @@
--- ---
source: lact-daemon/src/config.rs source: lact-daemon/src/config.rs
expression: deserialized_config expression: deserialized_config
snapshot_kind: text
--- ---
version: 0
daemon: daemon:
log_level: info log_level: info
admin_groups: admin_groups:

View File

@@ -412,7 +412,8 @@ fn set_nvidia_clock_offset(clock_info: &NvidiaClockInfo, adjustment_row: &Adjust
let oc_adjustment = &adjustment_row.imp().adjustment; let oc_adjustment = &adjustment_row.imp().adjustment;
oc_adjustment.set_lower((clock_info.max + clock_info.offset_range.0) as f64); oc_adjustment.set_lower((clock_info.max + clock_info.offset_range.0) as f64);
oc_adjustment.set_upper((clock_info.max + clock_info.offset_range.1) as f64); oc_adjustment.set_upper((clock_info.max + clock_info.offset_range.1) as f64);
oc_adjustment.set_value((clock_info.max + clock_info.offset) as f64); oc_adjustment
.set_value((clock_info.max + (clock_info.offset / clock_info.offset_ratio)) as f64);
adjustment_row.set_visible(true); adjustment_row.set_visible(true);
} }

View File

@@ -159,6 +159,7 @@ pub struct NvidiaClocksTable {
pub struct NvidiaClockInfo { pub struct NvidiaClockInfo {
pub max: i32, pub max: i32,
pub offset: i32, pub offset: i32,
pub offset_ratio: i32,
pub offset_range: (i32, i32), pub offset_range: (i32, i32),
} }