diff --git a/src/config/types.rs b/src/config/types.rs index 6446e3e..297b65b 100644 --- a/src/config/types.rs +++ b/src/config/types.rs @@ -124,6 +124,7 @@ pub struct ProfileConfigToml { pub min_freq_mhz: Option, pub max_freq_mhz: Option, pub platform_profile: Option, + pub turbo_auto_settings: Option, #[serde(skip_serializing_if = "Option::is_none")] pub battery_charge_thresholds: Option, } @@ -151,6 +152,7 @@ impl Default for ProfileConfigToml { min_freq_mhz: None, max_freq_mhz: None, platform_profile: None, + turbo_auto_settings: None, battery_charge_thresholds: None, } } @@ -208,7 +210,9 @@ impl From for ProfileConfig { min_freq_mhz: toml_config.min_freq_mhz, max_freq_mhz: toml_config.max_freq_mhz, platform_profile: toml_config.platform_profile, - turbo_auto_settings: Some(TurboAutoSettings::default()), + turbo_auto_settings: toml_config + .turbo_auto_settings + .or_else(|| Some(TurboAutoSettings::default())), battery_charge_thresholds: toml_config.battery_charge_thresholds, } } diff --git a/src/cpu.rs b/src/cpu.rs index eeb4dfa..cbd37f8 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,6 +1,7 @@ use crate::core::{GovernorOverrideMode, TurboSetting}; use crate::util::error::ControlError; use core::str; +use log::debug; use std::{fs, io, path::Path, string::ToString}; pub type Result = std::result::Result; @@ -216,12 +217,19 @@ pub fn set_turbo(setting: TurboSetting) -> Result<()> { let value_pstate = match setting { TurboSetting::Always => "0", // no_turbo = 0 means turbo is enabled TurboSetting::Never => "1", // no_turbo = 1 means turbo is disabled - TurboSetting::Auto => return Err(ControlError::InvalidValueError("Turbo Auto cannot be directly set via intel_pstate/no_turbo or cpufreq/boost. System default.".to_string())), + // Auto mode is handled at the engine level, not directly at the sysfs level + TurboSetting::Auto => { + debug!("Turbo Auto mode is managed by engine logic based on system conditions"); + return Ok(()); + } }; let value_boost = match setting { TurboSetting::Always => "1", // boost = 1 means turbo is enabled TurboSetting::Never => "0", // boost = 0 means turbo is disabled - TurboSetting::Auto => return Err(ControlError::InvalidValueError("Turbo Auto cannot be directly set via intel_pstate/no_turbo or cpufreq/boost. System default.".to_string())), + TurboSetting::Auto => { + debug!("Turbo Auto mode is managed by engine logic based on system conditions"); + return Ok(()); + } }; // AMD specific paths diff --git a/src/engine.rs b/src/engine.rs index 791fa5a..4eb3e6b 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,6 +4,7 @@ use crate::core::{OperationalMode, SystemReport, TurboSetting}; use crate::cpu::{self}; use crate::util::error::{ControlError, EngineError}; use log::{debug, info, warn}; +use std::sync::atomic::{AtomicBool, Ordering}; /// Try applying a CPU feature and handle common error cases. Centralizes the where we /// previously did: @@ -172,6 +173,10 @@ pub fn determine_and_apply_settings( Ok(()) } +// Keep track of current auto turbo state for hysteresis using thread-safe atomics +static PREVIOUS_TURBO_STATE: AtomicBool = AtomicBool::new(false); +static TURBO_STATE_INITIALIZED: AtomicBool = AtomicBool::new(false); + fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<(), EngineError> { // Get the auto turbo settings from the config, or use defaults let turbo_settings = config.turbo_auto_settings.clone().unwrap_or_default(); @@ -204,10 +209,18 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<() } }; - // Decision logic for enabling/disabling turbo - let enable_turbo = match (cpu_temp, avg_cpu_usage) { + // Get previous state safely using atomic operations + let has_previous_state = TURBO_STATE_INITIALIZED.load(Ordering::Relaxed); + let previous_turbo_enabled = if has_previous_state { + Some(PREVIOUS_TURBO_STATE.load(Ordering::Relaxed)) + } else { + None + }; + + // Decision logic for enabling/disabling turbo with hysteresis + let enable_turbo = match (cpu_temp, avg_cpu_usage, previous_turbo_enabled) { // If temperature is too high, disable turbo regardless of load - (Some(temp), _) if temp >= turbo_settings.temp_threshold_high => { + (Some(temp), _, _) if temp >= turbo_settings.temp_threshold_high => { info!( "Auto Turbo: Disabled due to high temperature ({:.1}°C >= {:.1}°C)", temp, turbo_settings.temp_threshold_high @@ -215,7 +228,7 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<() false } // If load is high enough, enable turbo (unless temp already caused it to disable) - (_, Some(usage)) if usage >= turbo_settings.load_threshold_high => { + (_, Some(usage), _) if usage >= turbo_settings.load_threshold_high => { info!( "Auto Turbo: Enabled due to high CPU load ({:.1}% >= {:.1}%)", usage, turbo_settings.load_threshold_high @@ -223,21 +236,36 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<() true } // If load is low, disable turbo - (_, Some(usage)) if usage <= turbo_settings.load_threshold_low => { + (_, Some(usage), _) if usage <= turbo_settings.load_threshold_low => { info!( "Auto Turbo: Disabled due to low CPU load ({:.1}% <= {:.1}%)", usage, turbo_settings.load_threshold_low ); false } - // In intermediate load scenarios or if we can't determine, leave turbo in current state - // For now, we'll disable it as a safe default + // In intermediate load range, maintain previous state (hysteresis) + (_, Some(usage), Some(prev_state)) + if usage > turbo_settings.load_threshold_low + && usage < turbo_settings.load_threshold_high => + { + info!( + "Auto Turbo: Maintaining previous state ({}) due to intermediate load ({:.1}%)", + if prev_state { "enabled" } else { "disabled" }, + usage + ); + prev_state + } + // In indeterminate states or unknown previous state, default to disabled _ => { info!("Auto Turbo: Disabled (default for indeterminate state)"); false } }; + // Save the current state for next time using atomic operations + PREVIOUS_TURBO_STATE.store(enable_turbo, Ordering::Relaxed); + TURBO_STATE_INITIALIZED.store(true, Ordering::Relaxed); + // Apply the turbo setting let turbo_setting = if enable_turbo { TurboSetting::Always