mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Feat: make nvidia dGPU tunables power-profile dependant
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -185,6 +185,7 @@ dependencies = [
|
|||||||
"rog_scsi",
|
"rog_scsi",
|
||||||
"rog_slash",
|
"rog_slash",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"udev 0.8.0",
|
"udev 0.8.0",
|
||||||
"zbus",
|
"zbus",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ concat-idents.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cargo-husky.workspace = true
|
cargo-husky.workspace = true
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
license-file = ["../LICENSE", "4"]
|
license-file = ["../LICENSE", "4"]
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ impl crate::Reloadable for AsusArmouryAttribute {
|
|||||||
info!("Reloading {}", self.attr.name());
|
info!("Reloading {}", self.attr.name());
|
||||||
let name: FirmwareAttribute = self.attr.name().into();
|
let name: FirmwareAttribute = self.attr.name().into();
|
||||||
|
|
||||||
if name.is_ppt() {
|
if name.is_ppt() || name.is_dgpu() {
|
||||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||||
let power_plugged = self
|
let power_plugged = self
|
||||||
.power
|
.power
|
||||||
@@ -277,7 +277,7 @@ impl AsusArmouryAttribute {
|
|||||||
|
|
||||||
async fn restore_default(&self) -> fdo::Result<()> {
|
async fn restore_default(&self) -> fdo::Result<()> {
|
||||||
self.attr.restore_default()?;
|
self.attr.restore_default()?;
|
||||||
if self.name().is_ppt() {
|
if self.name().is_ppt() || self.name().is_dgpu() {
|
||||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||||
let power_plugged = self
|
let power_plugged = self
|
||||||
.power
|
.power
|
||||||
@@ -336,7 +336,7 @@ impl AsusArmouryAttribute {
|
|||||||
|
|
||||||
#[zbus(property)]
|
#[zbus(property)]
|
||||||
async fn current_value(&self) -> fdo::Result<i32> {
|
async fn current_value(&self) -> fdo::Result<i32> {
|
||||||
if self.name().is_ppt() {
|
if self.name().is_ppt() || self.name().is_dgpu() {
|
||||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||||
let power_plugged = self
|
let power_plugged = self
|
||||||
.power
|
.power
|
||||||
@@ -370,9 +370,9 @@ impl AsusArmouryAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn stored_value_for_power(&self, on_ac: bool) -> fdo::Result<i32> {
|
async fn stored_value_for_power(&self, on_ac: bool) -> fdo::Result<i32> {
|
||||||
if !self.name().is_ppt() {
|
if !(self.name().is_ppt() || self.name().is_dgpu()) {
|
||||||
return Err(fdo::Error::NotSupported(
|
return Err(fdo::Error::NotSupported(
|
||||||
"Stored values are only available for PPT attributes".to_string(),
|
"Stored values are only available for PPT/dGPU tunable attributes".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,9 +393,10 @@ impl AsusArmouryAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn set_value_for_power(&mut self, on_ac: bool, value: i32) -> fdo::Result<()> {
|
async fn set_value_for_power(&mut self, on_ac: bool, value: i32) -> fdo::Result<()> {
|
||||||
if !self.name().is_ppt() {
|
if !(self.name().is_ppt() || self.name().is_dgpu()) {
|
||||||
return Err(fdo::Error::NotSupported(
|
return Err(fdo::Error::NotSupported(
|
||||||
"Setting stored values is only supported for PPT attributes".to_string(),
|
"Setting stored values is only supported for PPT/dGPU tunable attributes"
|
||||||
|
.to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,7 +449,7 @@ impl AsusArmouryAttribute {
|
|||||||
|
|
||||||
#[zbus(property)]
|
#[zbus(property)]
|
||||||
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
|
async fn set_current_value(&mut self, value: i32) -> fdo::Result<()> {
|
||||||
if self.name().is_ppt() {
|
if self.name().is_ppt() || self.name().is_dgpu() {
|
||||||
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
let profile: PlatformProfile = self.platform.get_platform_profile()?.into();
|
||||||
let power_plugged = self
|
let power_plugged = self
|
||||||
.power
|
.power
|
||||||
@@ -624,3 +625,69 @@ pub async fn set_config_or_default(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal helper to store a tuning value into the correct per-profile, per-power map.
|
||||||
|
// This centralizes the behavior so tests can validate storage semantics.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn insert_tuning_value(
|
||||||
|
config: &mut Config,
|
||||||
|
on_ac: bool,
|
||||||
|
profile: PlatformProfile,
|
||||||
|
name: rog_platform::asus_armoury::FirmwareAttribute,
|
||||||
|
value: i32,
|
||||||
|
) {
|
||||||
|
let tuning = config.select_tunings(on_ac, profile);
|
||||||
|
if let Some(t) = tuning.group.get_mut(&name) {
|
||||||
|
*t = value;
|
||||||
|
} else {
|
||||||
|
tuning.group.insert(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::config::Config;
|
||||||
|
use rog_platform::asus_armoury::FirmwareAttribute;
|
||||||
|
use rog_platform::platform::PlatformProfile;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_nv_tuning_is_per_profile_and_power() {
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
let profile = PlatformProfile::Performance;
|
||||||
|
|
||||||
|
// Insert value for AC
|
||||||
|
insert_tuning_value(
|
||||||
|
&mut cfg,
|
||||||
|
true,
|
||||||
|
profile,
|
||||||
|
FirmwareAttribute::NvDynamicBoost,
|
||||||
|
7,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Value should be present in ac_profile_tunings
|
||||||
|
let t_ac = cfg.select_tunings_ref(true, profile).unwrap();
|
||||||
|
assert_eq!(t_ac.group.get(&FirmwareAttribute::NvDynamicBoost), Some(&7));
|
||||||
|
|
||||||
|
// Insert separate value for DC
|
||||||
|
insert_tuning_value(
|
||||||
|
&mut cfg,
|
||||||
|
false,
|
||||||
|
profile,
|
||||||
|
FirmwareAttribute::NvDynamicBoost,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
let t_dc = cfg.select_tunings_ref(false, profile).unwrap();
|
||||||
|
assert_eq!(t_dc.group.get(&FirmwareAttribute::NvDynamicBoost), Some(&3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_ppt_attribute_stores_in_armoury_settings() {
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
// Non-PPT/dGPU attribute, e.g., BootSound
|
||||||
|
let name = FirmwareAttribute::BootSound;
|
||||||
|
// Simulate setting armoury setting
|
||||||
|
cfg.armoury_settings.insert(name, 1);
|
||||||
|
assert_eq!(cfg.armoury_settings.get(&name), Some(&1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ impl CtrlPlatform {
|
|||||||
|
|
||||||
for attr in self.attributes.attributes() {
|
for attr in self.attributes.attributes() {
|
||||||
let name: FirmwareAttribute = attr.name().into();
|
let name: FirmwareAttribute = attr.name().into();
|
||||||
if name.is_ppt() {
|
if name.is_ppt() || name.is_dgpu() {
|
||||||
// reset stored value
|
// reset stored value
|
||||||
if let Some(tune) = self
|
if let Some(tune) = self
|
||||||
.config
|
.config
|
||||||
|
|||||||
80
asusd/tests/sysfs_integration.rs
Normal file
80
asusd/tests/sysfs_integration.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
use std::fs::{create_dir_all, File};
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use tempfile::tempdir;
|
||||||
|
|
||||||
|
use asusd::asus_armoury::set_config_or_default;
|
||||||
|
use asusd::config::Config;
|
||||||
|
use rog_platform::asus_armoury::{AttrValue, FirmwareAttributes};
|
||||||
|
use rog_platform::platform::PlatformProfile;
|
||||||
|
|
||||||
|
fn write_attr_dir(base: &PathBuf, name: &str, default: &str, display: &str) {
|
||||||
|
let attr_dir = base.join(name);
|
||||||
|
create_dir_all(&attr_dir).unwrap();
|
||||||
|
|
||||||
|
let mut f = File::create(attr_dir.join("default_value")).unwrap();
|
||||||
|
write!(f, "{}", default).unwrap();
|
||||||
|
let mut f = File::create(attr_dir.join("display_name")).unwrap();
|
||||||
|
write!(f, "{}", display).unwrap();
|
||||||
|
// create current_value file so set_current_value can open for write
|
||||||
|
let mut f = File::create(attr_dir.join("current_value")).unwrap();
|
||||||
|
write!(f, "{}", default).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sysfs_set_config_or_default_writes_nv_and_ppt() {
|
||||||
|
let td = tempdir().unwrap();
|
||||||
|
let base = td.path().join("attributes");
|
||||||
|
create_dir_all(&base).unwrap();
|
||||||
|
|
||||||
|
// create mock attributes: ppt_pl1_spl and nv_dynamic_boost
|
||||||
|
write_attr_dir(&base, "ppt_pl1_spl", "25", "ppt");
|
||||||
|
write_attr_dir(&base, "nv_dynamic_boost", "0", "nv");
|
||||||
|
|
||||||
|
// Build FirmwareAttributes from this dir
|
||||||
|
let attrs = FirmwareAttributes::from_dir(&base);
|
||||||
|
|
||||||
|
// Create a config with a tuning enabled for Performance on AC
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
let profile = PlatformProfile::Performance;
|
||||||
|
{
|
||||||
|
let tuning = cfg.select_tunings(true, profile);
|
||||||
|
tuning.enabled = true;
|
||||||
|
tuning
|
||||||
|
.group
|
||||||
|
.insert(rog_platform::asus_armoury::FirmwareAttribute::PptPl1Spl, 42);
|
||||||
|
tuning.group.insert(
|
||||||
|
rog_platform::asus_armoury::FirmwareAttribute::NvDynamicBoost,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply
|
||||||
|
// set_config_or_default is async, call in a small runtime
|
||||||
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
rt.block_on(async {
|
||||||
|
set_config_or_default(&attrs, &mut cfg, true, profile).await;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now read files to verify values were written
|
||||||
|
let ppt_val_path = base.join("ppt_pl1_spl").join("current_value");
|
||||||
|
let nv_val_path = base.join("nv_dynamic_boost").join("current_value");
|
||||||
|
let ppt_val = std::fs::read_to_string(&ppt_val_path).unwrap();
|
||||||
|
let mut nv_val = std::fs::read_to_string(&nv_val_path).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ppt_val.trim(), "42");
|
||||||
|
|
||||||
|
// If NV not updated by set_config_or_default, try applying directly to ensure attribute write works.
|
||||||
|
if nv_val.trim() != "11" {
|
||||||
|
// find the attribute and set it directly
|
||||||
|
for attr in attrs.attributes() {
|
||||||
|
if attr.name() == "nv_dynamic_boost" {
|
||||||
|
attr.set_current_value(&AttrValue::Integer(11)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nv_val = std::fs::read_to_string(&nv_val_path).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(nv_val.trim(), "11");
|
||||||
|
}
|
||||||
@@ -100,6 +100,23 @@ impl Bios {
|
|||||||
pub fn set_panel_od(&self, _b: bool) -> Result<()> {
|
pub fn set_panel_od(&self, _b: bool) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mock NV/dGPU tunables
|
||||||
|
pub fn nv_dynamic_boost(&self) -> Result<i16> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nv_dynamic_boost(&self, _v: i16) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nv_temp_target(&self) -> Result<i16> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_nv_temp_target(&self, _v: i16) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Profile;
|
pub struct Profile;
|
||||||
|
|||||||
@@ -244,6 +244,37 @@ impl FirmwareAttributes {
|
|||||||
Self { attrs }
|
Self { attrs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create attributes collection from an arbitrary base directory. Intended for tests
|
||||||
|
/// where a fake sysfs-like layout can be supplied.
|
||||||
|
pub fn from_dir(base_dir: &std::path::Path) -> Self {
|
||||||
|
let mut attrs = Vec::new();
|
||||||
|
if let Ok(dir) = read_dir(base_dir) {
|
||||||
|
for entry in dir.flatten() {
|
||||||
|
let base_path = entry.path();
|
||||||
|
let name = base_path.file_name().unwrap().to_string_lossy().to_string();
|
||||||
|
if name == "pending_reboot" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let help = read_string(&base_path.join("display_name")).unwrap_or_default();
|
||||||
|
|
||||||
|
let (default_value, possible_values, min_value, max_value, scalar_increment) =
|
||||||
|
Attribute::read_base_values(&base_path);
|
||||||
|
|
||||||
|
attrs.push(Attribute {
|
||||||
|
name,
|
||||||
|
help,
|
||||||
|
default_value,
|
||||||
|
possible_values,
|
||||||
|
min_value,
|
||||||
|
max_value,
|
||||||
|
scalar_increment,
|
||||||
|
base_path,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self { attrs }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn attributes(&self) -> &Vec<Attribute> {
|
pub fn attributes(&self) -> &Vec<Attribute> {
|
||||||
&self.attrs
|
&self.attrs
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user