mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-07 15:10:29 -04:00
power: Replace LenovoWmiTdpLimiter with FirmwareAttributeTdpLimiter
It seems several devices use a firmware-attribute interface to do TDP limiting. This turns LenovoWmiTdpLimiter into a generic interface that can be configured to use an arbitrary firmware-attribute name and check for an arbitrary power profile.
This commit is contained in:
parent
a67e911aa7
commit
4eeffda8ef
4 changed files with 223 additions and 24 deletions
|
@ -3,4 +3,8 @@ platform_profile_name = "lenovo-wmi-gamezone"
|
||||||
suggested_default = "custom"
|
suggested_default = "custom"
|
||||||
|
|
||||||
[tdp_limit]
|
[tdp_limit]
|
||||||
method = "lenovo_wmi"
|
method = "firmware_attribute"
|
||||||
|
|
||||||
|
[tdp_limit.firmware_attribute]
|
||||||
|
attribute = "lenovo-wmi-other-0"
|
||||||
|
performance_profile = "custom"
|
||||||
|
|
|
@ -1026,6 +1026,7 @@ mod test {
|
||||||
method: TdpLimitingMethod::GpuHwmon,
|
method: TdpLimitingMethod::GpuHwmon,
|
||||||
range: Some(RangeConfig::new(3, 15)),
|
range: Some(RangeConfig::new(3, 15)),
|
||||||
download_mode_limit: NonZeroU32::new(6),
|
download_mode_limit: NonZeroU32::new(6),
|
||||||
|
firmware_attribute: None,
|
||||||
}),
|
}),
|
||||||
gpu_clocks: Some(RangeConfig::new(200, 1600)),
|
gpu_clocks: Some(RangeConfig::new(200, 1600)),
|
||||||
battery_charge_limit: Some(BatteryChargeLimitConfig {
|
battery_charge_limit: Some(BatteryChargeLimitConfig {
|
||||||
|
|
|
@ -118,6 +118,12 @@ pub(crate) struct BatteryChargeLimitConfig {
|
||||||
pub attribute: String,
|
pub attribute: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, Debug)]
|
||||||
|
pub(crate) struct FirmwareAttributeConfig {
|
||||||
|
pub attribute: String,
|
||||||
|
pub performance_profile: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Debug)]
|
#[derive(Clone, Deserialize, Debug)]
|
||||||
pub(crate) struct PerformanceProfileConfig {
|
pub(crate) struct PerformanceProfileConfig {
|
||||||
pub suggested_default: String,
|
pub suggested_default: String,
|
||||||
|
@ -130,6 +136,7 @@ pub(crate) struct TdpLimitConfig {
|
||||||
pub method: TdpLimitingMethod,
|
pub method: TdpLimitingMethod,
|
||||||
pub range: Option<RangeConfig<u32>>,
|
pub range: Option<RangeConfig<u32>>,
|
||||||
pub download_mode_limit: Option<NonZeroU32>,
|
pub download_mode_limit: Option<NonZeroU32>,
|
||||||
|
pub firmware_attribute: Option<FirmwareAttributeConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, Deserialize, Debug)]
|
#[derive(Clone, Default, Deserialize, Debug)]
|
||||||
|
|
233
src/power.rs
233
src/power.rs
|
@ -101,18 +101,21 @@ pub enum CPUScalingGovernor {
|
||||||
SchedUtil,
|
SchedUtil,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Display, EnumString, VariantNames, PartialEq, Debug, Copy, Clone)]
|
#[derive(Display, EnumString, VariantNames, PartialEq, Debug, Clone)]
|
||||||
#[strum(serialize_all = "snake_case")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
pub enum TdpLimitingMethod {
|
pub enum TdpLimitingMethod {
|
||||||
GpuHwmon,
|
GpuHwmon,
|
||||||
LenovoWmi,
|
FirmwareAttribute,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct GpuHwmonTdpLimitManager {}
|
pub(crate) struct GpuHwmonTdpLimitManager {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct LenovoWmiTdpLimitManager {}
|
pub(crate) struct FirmwareAttributeLimitManager {
|
||||||
|
attribute: String,
|
||||||
|
performance_profile: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub(crate) trait TdpLimitManager: Send + Sync {
|
pub(crate) trait TdpLimitManager: Send + Sync {
|
||||||
|
@ -131,8 +134,16 @@ pub(crate) async fn tdp_limit_manager() -> Result<Box<dyn TdpLimitManager>> {
|
||||||
.and_then(|config| config.tdp_limit.as_ref())
|
.and_then(|config| config.tdp_limit.as_ref())
|
||||||
.ok_or(anyhow!("No TDP limit configured"))?;
|
.ok_or(anyhow!("No TDP limit configured"))?;
|
||||||
|
|
||||||
Ok(match config.method {
|
Ok(match &config.method {
|
||||||
TdpLimitingMethod::LenovoWmi => Box::new(LenovoWmiTdpLimitManager {}),
|
TdpLimitingMethod::FirmwareAttribute => {
|
||||||
|
let Some(ref firmware_attribute) = config.firmware_attribute else {
|
||||||
|
bail!("Firmware attribute TDP limiting method not configured");
|
||||||
|
};
|
||||||
|
Box::new(FirmwareAttributeLimitManager {
|
||||||
|
attribute: firmware_attribute.attribute.clone(),
|
||||||
|
performance_profile: firmware_attribute.performance_profile.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
TdpLimitingMethod::GpuHwmon => Box::new(GpuHwmonTdpLimitManager {}),
|
TdpLimitingMethod::GpuHwmon => Box::new(GpuHwmonTdpLimitManager {}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -492,27 +503,18 @@ impl TdpLimitManager for GpuHwmonTdpLimitManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LenovoWmiTdpLimitManager {
|
impl FirmwareAttributeLimitManager {
|
||||||
const PREFIX: &str = "/sys/class/firmware-attributes/lenovo-wmi-other-0/attributes";
|
const PREFIX: &str = "/sys/class/firmware-attributes";
|
||||||
const SPL_SUFFIX: &str = "ppt_pl1_spl";
|
const SPL_SUFFIX: &str = "ppt_pl1_spl";
|
||||||
const SPPT_SUFFIX: &str = "ppt_pl2_sppt";
|
const SPPT_SUFFIX: &str = "ppt_pl2_sppt";
|
||||||
const FPPT_SUFFIX: &str = "ppt_pl3_fppt";
|
const FPPT_SUFFIX: &str = "ppt_pl3_fppt";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl TdpLimitManager for LenovoWmiTdpLimitManager {
|
impl TdpLimitManager for FirmwareAttributeLimitManager {
|
||||||
async fn get_tdp_limit(&self) -> Result<u32> {
|
async fn get_tdp_limit(&self) -> Result<u32> {
|
||||||
let config = platform_config().await?;
|
ensure!(self.is_active().await?, "TDP limiting not active");
|
||||||
if let Some(config) = config
|
let base = path(Self::PREFIX).join(&self.attribute).join("attributes");
|
||||||
.as_ref()
|
|
||||||
.and_then(|config| config.performance_profile.as_ref())
|
|
||||||
{
|
|
||||||
ensure!(
|
|
||||||
get_platform_profile(&config.platform_profile_name).await? == "custom",
|
|
||||||
"TDP limiting not active"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let base = path(Self::PREFIX);
|
|
||||||
|
|
||||||
fs::read_to_string(base.join(Self::SPL_SUFFIX).join("current_value"))
|
fs::read_to_string(base.join(Self::SPL_SUFFIX).join("current_value"))
|
||||||
.await
|
.await
|
||||||
|
@ -523,13 +525,14 @@ impl TdpLimitManager for LenovoWmiTdpLimitManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_tdp_limit(&self, limit: u32) -> Result<()> {
|
async fn set_tdp_limit(&self, limit: u32) -> Result<()> {
|
||||||
|
ensure!(self.is_active().await?, "TDP limiting not active");
|
||||||
ensure!(
|
ensure!(
|
||||||
self.get_tdp_limit_range().await?.contains(&limit),
|
self.get_tdp_limit_range().await?.contains(&limit),
|
||||||
"Invalid limit"
|
"Invalid limit"
|
||||||
);
|
);
|
||||||
|
|
||||||
let limit = limit.to_string();
|
let limit = limit.to_string();
|
||||||
let base = path(Self::PREFIX);
|
let base = path(Self::PREFIX).join(&self.attribute).join("attributes");
|
||||||
write_synced(
|
write_synced(
|
||||||
base.join(Self::SPL_SUFFIX).join("current_value"),
|
base.join(Self::SPL_SUFFIX).join("current_value"),
|
||||||
limit.as_bytes(),
|
limit.as_bytes(),
|
||||||
|
@ -551,7 +554,10 @@ impl TdpLimitManager for LenovoWmiTdpLimitManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_tdp_limit_range(&self) -> Result<RangeInclusive<u32>> {
|
async fn get_tdp_limit_range(&self) -> Result<RangeInclusive<u32>> {
|
||||||
let base = path(Self::PREFIX).join(Self::SPL_SUFFIX);
|
let base = path(Self::PREFIX)
|
||||||
|
.join(&self.attribute)
|
||||||
|
.join("attributes")
|
||||||
|
.join(Self::SPL_SUFFIX);
|
||||||
|
|
||||||
let min: u32 = fs::read_to_string(base.join("min_value"))
|
let min: u32 = fs::read_to_string(base.join("min_value"))
|
||||||
.await
|
.await
|
||||||
|
@ -569,12 +575,15 @@ impl TdpLimitManager for LenovoWmiTdpLimitManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_active(&self) -> Result<bool> {
|
async fn is_active(&self) -> Result<bool> {
|
||||||
|
let Some(ref performance_profile) = self.performance_profile else {
|
||||||
|
return Ok(true);
|
||||||
|
};
|
||||||
let config = platform_config().await?;
|
let config = platform_config().await?;
|
||||||
if let Some(config) = config
|
if let Some(config) = config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|config| config.performance_profile.as_ref())
|
.and_then(|config| config.performance_profile.as_ref())
|
||||||
{
|
{
|
||||||
Ok(get_platform_profile(&config.platform_profile_name).await? == "custom")
|
Ok(get_platform_profile(&config.platform_profile_name).await? == *performance_profile)
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
@ -848,7 +857,10 @@ pub(crate) mod test {
|
||||||
use crate::error::to_zbus_fdo_error;
|
use crate::error::to_zbus_fdo_error;
|
||||||
use crate::hardware::test::fake_model;
|
use crate::hardware::test::fake_model;
|
||||||
use crate::hardware::SteamDeckVariant;
|
use crate::hardware::SteamDeckVariant;
|
||||||
use crate::platform::{BatteryChargeLimitConfig, PlatformConfig, RangeConfig, TdpLimitConfig};
|
use crate::platform::{
|
||||||
|
BatteryChargeLimitConfig, FirmwareAttributeConfig, PerformanceProfileConfig,
|
||||||
|
PlatformConfig, RangeConfig, TdpLimitConfig,
|
||||||
|
};
|
||||||
use crate::{enum_roundtrip, testing};
|
use crate::{enum_roundtrip, testing};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
@ -1034,6 +1046,7 @@ CCLK_RANGE in Core0:
|
||||||
method: TdpLimitingMethod::GpuHwmon,
|
method: TdpLimitingMethod::GpuHwmon,
|
||||||
range: Some(RangeConfig { min: 3, max: 15 }),
|
range: Some(RangeConfig { min: 3, max: 15 }),
|
||||||
download_mode_limit: None,
|
download_mode_limit: None,
|
||||||
|
firmware_attribute: None,
|
||||||
});
|
});
|
||||||
handle.test.platform_config.replace(Some(platform_config));
|
handle.test.platform_config.replace(Some(platform_config));
|
||||||
let manager = tdp_limit_manager().await.unwrap();
|
let manager = tdp_limit_manager().await.unwrap();
|
||||||
|
@ -1058,6 +1071,7 @@ CCLK_RANGE in Core0:
|
||||||
method: TdpLimitingMethod::GpuHwmon,
|
method: TdpLimitingMethod::GpuHwmon,
|
||||||
range: Some(RangeConfig { min: 3, max: 15 }),
|
range: Some(RangeConfig { min: 3, max: 15 }),
|
||||||
download_mode_limit: None,
|
download_mode_limit: None,
|
||||||
|
firmware_attribute: None,
|
||||||
});
|
});
|
||||||
handle.test.platform_config.replace(Some(platform_config));
|
handle.test.platform_config.replace(Some(platform_config));
|
||||||
let manager = tdp_limit_manager().await.unwrap();
|
let manager = tdp_limit_manager().await.unwrap();
|
||||||
|
@ -1641,6 +1655,7 @@ CCLK_RANGE in Core0:
|
||||||
method: TdpLimitingMethod::GpuHwmon,
|
method: TdpLimitingMethod::GpuHwmon,
|
||||||
range: Some(RangeConfig { min: 3, max: 15 }),
|
range: Some(RangeConfig { min: 3, max: 15 }),
|
||||||
download_mode_limit: NonZeroU32::new(6),
|
download_mode_limit: NonZeroU32::new(6),
|
||||||
|
firmware_attribute: None,
|
||||||
});
|
});
|
||||||
h.test.platform_config.replace(Some(platform_config));
|
h.test.platform_config.replace(Some(platform_config));
|
||||||
let manager = tdp_limit_manager().await.unwrap();
|
let manager = tdp_limit_manager().await.unwrap();
|
||||||
|
@ -1736,6 +1751,7 @@ CCLK_RANGE in Core0:
|
||||||
method: TdpLimitingMethod::GpuHwmon,
|
method: TdpLimitingMethod::GpuHwmon,
|
||||||
range: Some(RangeConfig { min: 3, max: 15 }),
|
range: Some(RangeConfig { min: 3, max: 15 }),
|
||||||
download_mode_limit: None,
|
download_mode_limit: None,
|
||||||
|
firmware_attribute: None,
|
||||||
});
|
});
|
||||||
h.test.platform_config.replace(Some(platform_config));
|
h.test.platform_config.replace(Some(platform_config));
|
||||||
let manager = tdp_limit_manager().await.unwrap();
|
let manager = tdp_limit_manager().await.unwrap();
|
||||||
|
@ -1786,4 +1802,175 @@ CCLK_RANGE in Core0:
|
||||||
fin_tx.send(()).expect("fin");
|
fin_tx.send(()).expect("fin");
|
||||||
task.await.expect("exit").expect("exit2");
|
task.await.expect("exit").expect("exit2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_firmware_attribute_tdp_limiter() {
|
||||||
|
let h = testing::start();
|
||||||
|
setup().await.expect("setup");
|
||||||
|
|
||||||
|
let mut platform_config = PlatformConfig::default();
|
||||||
|
platform_config.performance_profile = Some(PerformanceProfileConfig {
|
||||||
|
platform_profile_name: String::from("platform-profile0"),
|
||||||
|
suggested_default: String::from("custom"),
|
||||||
|
});
|
||||||
|
platform_config.tdp_limit = Some(TdpLimitConfig {
|
||||||
|
method: TdpLimitingMethod::FirmwareAttribute,
|
||||||
|
range: Some(RangeConfig { min: 3, max: 15 }),
|
||||||
|
download_mode_limit: None,
|
||||||
|
firmware_attribute: Some(FirmwareAttributeConfig {
|
||||||
|
attribute: String::from("tdp0"),
|
||||||
|
performance_profile: Some(String::from("custom")),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
h.test.platform_config.replace(Some(platform_config));
|
||||||
|
|
||||||
|
let attributes_base = path(FirmwareAttributeLimitManager::PREFIX)
|
||||||
|
.join("tdp0")
|
||||||
|
.join("attributes");
|
||||||
|
let spl_base = attributes_base.join(FirmwareAttributeLimitManager::SPL_SUFFIX);
|
||||||
|
let sppt_base = attributes_base.join(FirmwareAttributeLimitManager::SPPT_SUFFIX);
|
||||||
|
let fppt_base = attributes_base.join(FirmwareAttributeLimitManager::FPPT_SUFFIX);
|
||||||
|
create_dir_all(&spl_base).await.unwrap();
|
||||||
|
write_synced(spl_base.join("current_value"), b"10\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
create_dir_all(&sppt_base).await.unwrap();
|
||||||
|
write_synced(sppt_base.join("current_value"), b"10\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
create_dir_all(&fppt_base).await.unwrap();
|
||||||
|
write_synced(fppt_base.join("current_value"), b"10\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
write_synced(spl_base.join("min_value"), b"6\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
write_synced(spl_base.join("max_value"), b"20\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let platform_profile_base = path(PLATFORM_PROFILE_PREFIX).join("platform-profile0");
|
||||||
|
create_dir_all(&platform_profile_base).await.unwrap();
|
||||||
|
write_synced(platform_profile_base.join("name"), b"platform-profile0\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
write_synced(platform_profile_base.join("profile"), b"custom\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let manager = tdp_limit_manager().await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manager.is_active().await.unwrap(), true);
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 10);
|
||||||
|
|
||||||
|
manager.set_tdp_limit(15).await.unwrap();
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 15);
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(spl_base.join("current_value"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"15"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sppt_base.join("current_value"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"15"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(fppt_base.join("current_value"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"15"
|
||||||
|
);
|
||||||
|
|
||||||
|
manager.set_tdp_limit(25).await.unwrap_err();
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 15);
|
||||||
|
|
||||||
|
manager.set_tdp_limit(2).await.unwrap_err();
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 15);
|
||||||
|
|
||||||
|
write_synced(platform_profile_base.join("profile"), b"balanced\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager.set_tdp_limit(10).await.unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_firmware_attribute_tdp_limiter_no_profile() {
|
||||||
|
let h = testing::start();
|
||||||
|
setup().await.expect("setup");
|
||||||
|
|
||||||
|
let mut platform_config = PlatformConfig::default();
|
||||||
|
platform_config.tdp_limit = Some(TdpLimitConfig {
|
||||||
|
method: TdpLimitingMethod::FirmwareAttribute,
|
||||||
|
range: Some(RangeConfig { min: 3, max: 15 }),
|
||||||
|
download_mode_limit: None,
|
||||||
|
firmware_attribute: Some(FirmwareAttributeConfig {
|
||||||
|
attribute: String::from("tdp0"),
|
||||||
|
performance_profile: None,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
h.test.platform_config.replace(Some(platform_config));
|
||||||
|
|
||||||
|
let attributes_base = path(FirmwareAttributeLimitManager::PREFIX)
|
||||||
|
.join("tdp0")
|
||||||
|
.join("attributes");
|
||||||
|
let spl_base = attributes_base.join(FirmwareAttributeLimitManager::SPL_SUFFIX);
|
||||||
|
let sppt_base = attributes_base.join(FirmwareAttributeLimitManager::SPPT_SUFFIX);
|
||||||
|
let fppt_base = attributes_base.join(FirmwareAttributeLimitManager::FPPT_SUFFIX);
|
||||||
|
create_dir_all(&spl_base).await.unwrap();
|
||||||
|
write_synced(spl_base.join("current_value"), b"10\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
create_dir_all(&sppt_base).await.unwrap();
|
||||||
|
write_synced(sppt_base.join("current_value"), b"10\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
create_dir_all(&fppt_base).await.unwrap();
|
||||||
|
write_synced(fppt_base.join("current_value"), b"10\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
write_synced(spl_base.join("min_value"), b"6\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
write_synced(spl_base.join("max_value"), b"20\n")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let manager = tdp_limit_manager().await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manager.is_active().await.unwrap(), true);
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 10);
|
||||||
|
|
||||||
|
manager.set_tdp_limit(15).await.unwrap();
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 15);
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(spl_base.join("current_value"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"15"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sppt_base.join("current_value"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"15"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(fppt_base.join("current_value"))
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
"15"
|
||||||
|
);
|
||||||
|
|
||||||
|
manager.set_tdp_limit(25).await.unwrap_err();
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 15);
|
||||||
|
|
||||||
|
manager.set_tdp_limit(2).await.unwrap_err();
|
||||||
|
assert_eq!(manager.get_tdp_limit().await.unwrap(), 15);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue