mirror of
https://github.com/ilya-zlobintsev/LACT.git
synced 2025-02-25 18:55:26 -06:00
Implemented GPU power cap modification
This commit is contained in:
parent
69aafd2a54
commit
239af5a2af
@ -148,6 +148,22 @@ impl DaemonConnection {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_power_cap(&self, gpu_id: u32, cap: i32) -> Result<(), DaemonError> {
|
||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||
s.write_all(&bincode::serialize(&Action::SetPowerCap(gpu_id, cap)).unwrap())
|
||||
.unwrap();
|
||||
s.shutdown(std::net::Shutdown::Write).expect("Could not shut down");
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
s.read_to_end(&mut buffer).unwrap();
|
||||
|
||||
let result: Result<DaemonResponse, DaemonError> = bincode::deserialize(&buffer).unwrap();
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_gpus(&self) -> Result<HashMap<u32, String>, DaemonError> {
|
||||
log::trace!("sending request");
|
||||
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::config::{Config, GpuConfig, GpuIdentifier};
|
||||
use crate::config::{GpuConfig, GpuIdentifier};
|
||||
use crate::hw_mon::{HWMon, HWMonError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -13,7 +13,8 @@ pub struct GpuStats {
|
||||
pub gpu_freq: i32,
|
||||
pub gpu_temp: i32,
|
||||
pub power_avg: i32,
|
||||
pub power_max: i32,
|
||||
pub power_cap: i32,
|
||||
pub power_cap_max: i32,
|
||||
pub fan_speed: i32,
|
||||
pub max_fan_speed: i32,
|
||||
}
|
||||
@ -54,6 +55,8 @@ pub struct GpuInfo {
|
||||
pub link_width: u8,
|
||||
pub vulkan_info: VulkanInfo,
|
||||
pub pci_slot: String,
|
||||
pub power_cap: i32,
|
||||
pub power_cap_max: i32,
|
||||
}
|
||||
|
||||
impl GpuController {
|
||||
@ -210,6 +213,11 @@ impl GpuController {
|
||||
|
||||
let vulkan_info = GpuController::get_vulkan_info(&model_id);
|
||||
|
||||
let (power_cap, power_cap_max) = match &self.hw_mon {
|
||||
Some(hw_mon) => (hw_mon.get_power_cap(), hw_mon.get_power_cap_max()),
|
||||
None => (0, 0),
|
||||
};
|
||||
|
||||
GpuInfo {
|
||||
gpu_vendor: vendor,
|
||||
gpu_model: model,
|
||||
@ -224,6 +232,8 @@ impl GpuController {
|
||||
link_width,
|
||||
vulkan_info,
|
||||
pci_slot,
|
||||
power_cap,
|
||||
power_cap_max,
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,9 +248,9 @@ impl GpuController {
|
||||
Err(_) => 0,
|
||||
};
|
||||
|
||||
let (mem_freq, gpu_freq, gpu_temp, power_avg, power_max, fan_speed, max_fan_speed) = match &self.hw_mon {
|
||||
Some(hw_mon) => (hw_mon.get_mem_freq(), hw_mon.get_gpu_freq(), hw_mon.get_gpu_temp(), hw_mon.get_power_avg(), hw_mon.get_power_cap(), hw_mon.get_fan_speed(), hw_mon.fan_max_speed),
|
||||
None => (0, 0, 0, 0, 0, 0, 0),
|
||||
let (mem_freq, gpu_freq, gpu_temp, power_avg, power_cap, power_cap_max, fan_speed, max_fan_speed) = match &self.hw_mon {
|
||||
Some(hw_mon) => (hw_mon.get_mem_freq(), hw_mon.get_gpu_freq(), hw_mon.get_gpu_temp(), hw_mon.get_power_avg(), hw_mon.get_power_cap(), hw_mon.get_power_cap_max(), hw_mon.get_fan_speed(), hw_mon.fan_max_speed),
|
||||
None => (0, 0, 0, 0, 0, 0, 0, 0),
|
||||
};
|
||||
|
||||
GpuStats {
|
||||
@ -250,7 +260,8 @@ impl GpuController {
|
||||
gpu_freq,
|
||||
gpu_temp,
|
||||
power_avg,
|
||||
power_max,
|
||||
power_cap,
|
||||
power_cap_max,
|
||||
fan_speed,
|
||||
max_fan_speed,
|
||||
}
|
||||
@ -312,6 +323,17 @@ impl GpuController {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_power_cap(&mut self, cap: i32) -> Result<(), HWMonError> {
|
||||
match &mut self.hw_mon {
|
||||
Some(hw_mon) => {
|
||||
hw_mon.set_power_cap(cap).unwrap();
|
||||
self.gpu_info.power_cap = cap;
|
||||
Ok(())
|
||||
},
|
||||
None => Err(HWMonError::NoHWMon),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_vulkan_info(pci_id: &str) -> VulkanInfo {
|
||||
let mut device_name = String::from("Not supported");
|
||||
let mut api_version = String::new();
|
||||
|
@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum HWMonError {
|
||||
PermissionDenied,
|
||||
InvalidValue,
|
||||
NoHWMon,
|
||||
}
|
||||
|
||||
@ -24,6 +25,7 @@ pub struct HWMon {
|
||||
pub fan_max_speed: i32,
|
||||
fan_control: Arc<AtomicBool>,
|
||||
fan_curve: Arc<RwLock<BTreeMap<i32, f64>>>,
|
||||
power_cap: i32,
|
||||
}
|
||||
|
||||
impl HWMon {
|
||||
@ -37,16 +39,18 @@ impl HWMon {
|
||||
Err(_) => 0,
|
||||
};
|
||||
|
||||
let mon = HWMon {
|
||||
let mut mon = HWMon {
|
||||
hwmon_path: hwmon_path.clone(),
|
||||
fan_max_speed,
|
||||
fan_control: Arc::new(AtomicBool::new(false)),
|
||||
fan_curve: Arc::new(RwLock::new(fan_curve)),
|
||||
power_cap: 0,
|
||||
};
|
||||
|
||||
if fan_control_enabled {
|
||||
mon.start_fan_control().unwrap();
|
||||
}
|
||||
mon.power_cap = mon.get_power_cap();
|
||||
|
||||
mon
|
||||
}
|
||||
@ -115,6 +119,19 @@ impl HWMon {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_power_cap_max(&self) -> i32 {
|
||||
let filename = self.hwmon_path.join("power1_cap_max");
|
||||
|
||||
match fs::read_to_string(filename) {
|
||||
Ok(a) => a
|
||||
.trim()
|
||||
.parse::<i32>()
|
||||
.unwrap()
|
||||
/ 1000000,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_power_cap(&self) -> i32 {
|
||||
let filename = self.hwmon_path.join("power1_cap");
|
||||
|
||||
@ -128,6 +145,24 @@ impl HWMon {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_power_cap(&mut self, cap: i32) -> Result<(), HWMonError> {
|
||||
|
||||
if cap > self.get_power_cap_max() {
|
||||
return Err(HWMonError::InvalidValue);
|
||||
}
|
||||
|
||||
let cap = cap * 1000000;
|
||||
log::trace!("setting power cap to {}", cap);
|
||||
|
||||
match fs::write(self.hwmon_path.join("power1_cap"), cap.to_string()) {
|
||||
Ok(_) => {
|
||||
self.power_cap = cap;
|
||||
Ok(())
|
||||
},
|
||||
Err(_) => Err(HWMonError::PermissionDenied),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_power_avg(&self) -> i32 {
|
||||
let filename = self.hwmon_path.join("power1_average");
|
||||
|
||||
|
@ -3,7 +3,7 @@ pub mod daemon_connection;
|
||||
pub mod gpu_controller;
|
||||
pub mod hw_mon;
|
||||
|
||||
use config::{Config, GpuConfig, GpuIdentifier};
|
||||
use config::{Config, GpuConfig};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::{BTreeMap, HashMap}, fs};
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
@ -35,6 +35,7 @@ pub enum Action {
|
||||
StopFanControl(u32),
|
||||
GetFanControl(u32),
|
||||
SetFanCurve(u32, BTreeMap<i32, f64>),
|
||||
SetPowerCap(u32, i32),
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
@ -192,9 +193,6 @@ impl Daemon {
|
||||
Action::SetFanCurve(i, curve) => match self.gpu_controllers.get_mut(&i) {
|
||||
Some(controller) => {
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
stream.read_to_end(&mut buffer).unwrap();
|
||||
|
||||
match controller.set_fan_curve(curve) {
|
||||
Ok(_) => {
|
||||
self.config.gpu_configs.insert(i, (controller.get_identifier(), controller.get_config()));
|
||||
@ -207,6 +205,17 @@ impl Daemon {
|
||||
},
|
||||
None => Err(DaemonError::InvalidID),
|
||||
}
|
||||
Action::SetPowerCap(i, cap) => match self.gpu_controllers.get_mut(&i) {
|
||||
Some(controller) => {
|
||||
match controller.set_power_cap(cap) {
|
||||
Ok(_) => {
|
||||
Ok(DaemonResponse::OK)
|
||||
},
|
||||
Err(_) => Err(DaemonError::HWMonError),
|
||||
}
|
||||
},
|
||||
None => Err(DaemonError::InvalidID),
|
||||
}
|
||||
Action::Shutdown => std::process::exit(0),
|
||||
};
|
||||
|
||||
|
@ -40,6 +40,8 @@ fn build_ui(application: >k::Application) {
|
||||
|
||||
let fan_speed_text_buffer: TextBuffer = builder.get_object("fan_speed_text_buffer").unwrap();
|
||||
|
||||
let power_cap_label: Label = builder.get_object("power_cap_label").unwrap();
|
||||
|
||||
let automatic_fan_control_switch: Switch =
|
||||
builder.get_object("automatic_fan_control_switch").unwrap();
|
||||
|
||||
@ -47,6 +49,8 @@ fn build_ui(application: >k::Application) {
|
||||
|
||||
let fan_curve_frame: Frame = builder.get_object("fan_curve_frame").unwrap();
|
||||
|
||||
let gpu_power_adjustment: Adjustment = builder.get_object("gpu_power_adjustment").unwrap();
|
||||
|
||||
let mut unpriviliged: bool = false;
|
||||
|
||||
let d = match DaemonConnection::new() {
|
||||
@ -94,8 +98,15 @@ fn build_ui(application: >k::Application) {
|
||||
|
||||
let fan_curv_frm = fan_curve_frame.clone();
|
||||
let auto_fan_ctrl_swtch = automatic_fan_control_switch.clone();
|
||||
let b = apply_button.clone();
|
||||
|
||||
let b = apply_button.clone();
|
||||
gpu_power_adjustment.connect_value_changed(move |adjustment| {
|
||||
println!("changed adjustment value to {}/{}", adjustment.get_value(), adjustment.get_upper());
|
||||
b.set_sensitive(true);
|
||||
power_cap_label.set_text(&format!("{}/{}", adjustment.get_value().floor(), adjustment.get_upper()));
|
||||
});
|
||||
|
||||
let b = apply_button.clone();
|
||||
gpu_select_comboboxtext.connect_changed(move |combobox| {
|
||||
let mut current_gpu_id = cur_id.write().unwrap();
|
||||
*current_gpu_id = combobox.get_active_id().unwrap().parse::<u32>().expect("invalid id");
|
||||
@ -166,7 +177,7 @@ fn build_ui(application: >k::Application) {
|
||||
gpu_temp_text_buffer.set_text(&format!("{}°C", gpu_stats.gpu_temp));
|
||||
|
||||
gpu_power_text_buffer
|
||||
.set_text(&format!("{}/{}W", gpu_stats.power_avg, gpu_stats.power_max));
|
||||
.set_text(&format!("{}/{}W", gpu_stats.power_avg, gpu_stats.power_cap));
|
||||
|
||||
fan_speed_text_buffer.set_text(&format!(
|
||||
"{}RPM({}%)",
|
||||
@ -174,6 +185,7 @@ fn build_ui(application: >k::Application) {
|
||||
(gpu_stats.fan_speed as f64 / gpu_stats.max_fan_speed as f64 * 100 as f64) as i32
|
||||
));
|
||||
|
||||
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
||||
@ -268,6 +280,9 @@ fn build_ui(application: >k::Application) {
|
||||
d.start_fan_control(current_gpu_id).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let power_cap = gpu_power_adjustment.get_value().floor() as i32;
|
||||
d.set_power_cap(current_gpu_id, power_cap).unwrap();
|
||||
});
|
||||
},
|
||||
Err(_) => (),
|
||||
@ -316,6 +331,11 @@ fn set_info(builder: &Builder, gpu_info: &GpuInfo) {
|
||||
.get_object("vulkan_features_text_buffer")
|
||||
.expect("Couldn't get textbuffer");
|
||||
|
||||
let power_cap_label: Label = builder.get_object("power_cap_label").unwrap();
|
||||
|
||||
let gpu_power_adjustment: Adjustment = builder.get_object("gpu_power_adjustment").unwrap();
|
||||
//let power_cap_scale: Scale = builder.get_object("power_cap_scale").unwrap();
|
||||
|
||||
gpu_model_text_buffer.set_text(&gpu_info.card_model);
|
||||
manufacturer_text_buffer.set_text(&gpu_info.card_vendor);
|
||||
vbios_version_text_buffer.set_text(&gpu_info.vbios_version);
|
||||
@ -330,6 +350,10 @@ fn set_info(builder: &Builder, gpu_info: &GpuInfo) {
|
||||
vulkan_version_text_buffer.set_text(&gpu_info.vulkan_info.api_version);
|
||||
vulkan_features_text_buffer.set_text(&gpu_info.vulkan_info.features);
|
||||
|
||||
power_cap_label.set_text(&format!("{}/{}", gpu_info.power_cap, gpu_info.power_cap_max));
|
||||
gpu_power_adjustment.set_upper(gpu_info.power_cap_max as f64);
|
||||
gpu_power_adjustment.set_value(gpu_info.power_cap as f64);
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -39,6 +39,11 @@
|
||||
<object class="GtkTextBuffer" id="gpu_model_text_buffer">
|
||||
<property name="text">gpu_model</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="gpu_power_adjustment">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="gpu_power_text_buffer">
|
||||
<property name="text" translatable="yes">50W/100W</property>
|
||||
</object>
|
||||
@ -65,6 +70,9 @@
|
||||
<object class="GtkTextBuffer" id="manufacturer_text_buffer">
|
||||
<property name="text" translatable="yes">Manufacturer</property>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="power_cap_text_buffer">
|
||||
<property name="text" translatable="yes">50/100</property>
|
||||
</object>
|
||||
<object class="GtkTextBuffer" id="vbios_version_text_buffer">
|
||||
<property name="text" translatable="yes">vbios_version</property>
|
||||
</object>
|
||||
@ -85,6 +93,7 @@
|
||||
</object>
|
||||
<object class="GtkWindow" id="main_window">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">LACT</property>
|
||||
<property name="default-width">350</property>
|
||||
<property name="default-height">500</property>
|
||||
<child>
|
||||
@ -672,6 +681,68 @@
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.10000000149011612</property>
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="power_cap_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">50/100</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale" id="power_cap_scale">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="adjustment">gpu_power_adjustment</property>
|
||||
<property name="restrict-to-fill-level">False</property>
|
||||
<property name="fill-level">0</property>
|
||||
<property name="round-digits">0</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="draw-value">False</property>
|
||||
<property name="value-pos">left</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Power Cap (Watts)</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="semibold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
|
Loading…
Reference in New Issue
Block a user