Added clockspeed information and unpriveleged service execution

This commit is contained in:
Ilya Zlobintsev
2020-10-20 17:03:00 +03:00
parent 8c042108b9
commit 3463b0f3b5
11 changed files with 450 additions and 150 deletions

View File

@@ -1,3 +1,4 @@
use daemon::daemon_connection::DaemonConnection;
use structopt::StructOpt;
#[derive(StructOpt)]
@@ -10,13 +11,17 @@ enum Opt {
fn main() {
let opt = Opt::from_args();
let mut d = DaemonConnection::new().unwrap();
match opt {
Opt::Stats => {
let gpu_stats = daemon::get_gpu_stats();
let gpu_stats = d.get_gpu_stats();
println!("VRAM: {}/{}", gpu_stats.mem_used, gpu_stats.mem_total);
println!("{:?}", gpu_stats);
}
Opt::Info => {
let gpu_info = daemon::get_gpu_info().unwrap();
let gpu_info = d.get_gpu_info();
println!("GPU Vendor: {}", gpu_info.gpu_vendor);
println!("GPU Model: {}", gpu_info.card_model);
println!("Driver in use: {}", gpu_info.driver);

View File

@@ -9,4 +9,5 @@ edition = "2018"
[dependencies]
bincode = "1.3"
serde = { version = "1.0", features = ["derive"] }
vulkano = "0.19"
vulkano = "0.19"
glob = "0.3"

View File

@@ -1,68 +0,0 @@
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::{Read, Write};
use std::os::unix::net::{UnixListener, UnixStream};
use std::process::Command;
use std::thread;
use crate::gpu_controller::GpuController;
use crate::SOCK_PATH;
#[derive(Clone)]
pub struct Daemon {
gpu_controller: GpuController,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Action {
GetInfo,
GetStats,
}
impl Daemon {
pub fn new(gpu_controller: GpuController) -> Daemon {
Daemon { gpu_controller }
}
pub fn run(self) {
if fs::metadata(SOCK_PATH).is_ok() {
fs::remove_file(SOCK_PATH).expect("Failed to take control over socket");
}
let listener = UnixListener::bind(SOCK_PATH).unwrap();
Command::new("chmod")
.arg("666")
.arg(SOCK_PATH)
.output()
.expect("Failed to chmod");
for stream in listener.incoming() {
let d = self.clone();
match stream {
Ok(stream) => {
thread::spawn(move || d.handle_connection(stream));
}
Err(err) => {
println!("Error: {}", err);
break;
}
}
}
}
fn handle_connection(self, mut stream: UnixStream) {
let mut buffer = Vec::<u8>::new();
stream.read_to_end(&mut buffer).unwrap();
println!("finished reading, buffer size {}", buffer.len());
let action: Action = bincode::deserialize(&buffer).unwrap();
let response: Vec<u8> = match action {
Action::GetStats => bincode::serialize(&self.gpu_controller.get_stats()).unwrap(),
Action::GetInfo => bincode::serialize(&self.gpu_controller.gpu_info).unwrap(),
};
stream
.write_all(&response)
.expect("Failed writing response");
}
}

View File

@@ -0,0 +1,60 @@
use crate::{Action, gpu_controller::GpuStats, SOCK_PATH, gpu_controller::GpuInfo};
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
#[derive(Debug)]
pub enum DaemonConnectionError {
ConnectionFailed,
}
#[derive(Clone, Copy)]
pub struct DaemonConnection {
}
impl DaemonConnection {
pub fn new() -> Result<Self, DaemonConnectionError> {
match UnixStream::connect(SOCK_PATH) {
Ok(mut stream) => {
stream.write(&bincode::serialize(&Action::CheckAlive).unwrap()).unwrap();
let mut buffer = Vec::<u8>::new();
stream.read_to_end(&mut buffer).unwrap();
if buffer[0] == 1 {
Ok(DaemonConnection { })
}
else {
Err(DaemonConnectionError::ConnectionFailed)
}
}
Err(_) => Err(DaemonConnectionError::ConnectionFailed),
}
}
pub fn get_gpu_stats(&self) -> GpuStats {
let mut stream = UnixStream::connect(SOCK_PATH).expect("Failed to connect to daemon");
stream
.write(&bincode::serialize(&Action::GetStats).unwrap())
.unwrap();
/*stream
.shutdown(std::net::Shutdown::Write)
.expect("Could not shut down");*/
let mut buffer = Vec::<u8>::new();
stream.read_to_end(&mut buffer).unwrap();
bincode::deserialize(&buffer).unwrap()
}
pub fn get_gpu_info(&self) -> GpuInfo {
let mut s = UnixStream::connect(SOCK_PATH).unwrap();
s.write_all(&bincode::serialize(&Action::GetInfo).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();
bincode::deserialize(&buffer).unwrap()
}
}

View File

@@ -1,14 +0,0 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FanController {
hwmon_path: String,
}
impl FanController {
pub fn new(hwmon_path: &str) -> FanController {
FanController {
hwmon_path: hwmon_path.to_string(),
}
}
}

View File

@@ -1,19 +1,21 @@
use crate::fan_controller::FanController;
use crate::hw_mon::HWMon;
use serde::{Deserialize, Serialize};
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
use std::fs;
use std::{collections::HashMap, fs};
use std::path::PathBuf;
use vulkano::instance::{Instance, InstanceExtensions, PhysicalDevice};
#[derive(Serialize, Deserialize, Debug)]
pub struct GpuStats {
pub mem_used: u64,
pub mem_total: u64,
pub mem_freq: i32,
pub gpu_freq: i32,
}
#[derive(Clone)]
pub struct GpuController {
pub struct GpuController {
hw_path: PathBuf,
fan_controller: FanController,
hw_mon: HWMon,
pub gpu_info: GpuInfo,
}
@@ -41,10 +43,10 @@ pub struct GpuInfo {
}
impl GpuController {
pub fn new(hw_path: &str) -> Self {
pub fn new(hw_path: PathBuf) -> Self {
let mut controller = GpuController {
hw_path: PathBuf::from(hw_path),
fan_controller: FanController::new(hw_path),
hw_path: hw_path.clone(),
hw_mon: HWMon::new(&hw_path.join("hwmon/hwmon0")),
gpu_info: Default::default(),
};
controller.gpu_info = controller.get_info();
@@ -53,7 +55,8 @@ impl GpuController {
}
fn get_info(&self) -> GpuInfo {
let uevent = fs::read_to_string(self.hw_path.join("uevent")).expect("Failed to read uevent");
let uevent =
fs::read_to_string(self.hw_path.join("uevent")).expect("Failed to read uevent");
let mut driver = String::new();
let mut vendor_id = String::new();
@@ -128,7 +131,7 @@ impl GpuController {
.expect("Failed to read link speed")
.trim()
.to_string();
let link_width = fs::read_to_string(self.hw_path.join("current_link_width"))
.expect("Failed to read link width")
.trim()
@@ -169,9 +172,14 @@ impl GpuController {
/ 1024
/ 1024;
let (mem_freq, gpu_freq) = (self.hw_mon.get_mem_freq(), self.hw_mon.get_gpu_freq());
GpuStats {
mem_total,
mem_used,
mem_freq,
gpu_freq,
}
}
@@ -184,7 +192,7 @@ impl GpuController {
let api_version = physical.api_version().to_string();
let device_name = physical.name().to_string();
let features = format!("{:?}", physical.supported_features());
return VulkanInfo {
device_name,
api_version,
@@ -193,7 +201,10 @@ impl GpuController {
}
}
VulkanInfo { device_name: "Not supported".to_string(), api_version: "".to_string(), features: "".to_string()}
VulkanInfo {
device_name: "Not supported".to_string(),
api_version: "".to_string(),
features: "".to_string(),
}
}
}

52
daemon/src/hw_mon.rs Normal file
View File

@@ -0,0 +1,52 @@
use glob::glob;
use std::{collections::HashMap, fs, path::PathBuf};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct HWMon {
hwmon_path: PathBuf,
freqs: HashMap<String, u8>,
}
impl HWMon {
pub fn new(hwmon_path: &PathBuf) -> HWMon {
let mut freqs: HashMap<String, u8> = HashMap::new();
for entry in glob(&format!("{}/freq*_label", hwmon_path.to_str().unwrap())).expect("Couldnt read glob pattern")
{
let entry = entry.unwrap();
let filename = entry.file_name().unwrap().to_str().unwrap();
let index = filename.chars().nth(4).unwrap().to_digit(10).unwrap() as u8;
let label = fs::read_to_string(hwmon_path.join(&format!("freq{}_label", index)))
.unwrap()
.trim()
.to_string();
freqs.insert(label, index);
}
println!("{:?}", freqs);
HWMon {
hwmon_path: hwmon_path.clone(),
freqs,
}
}
pub fn get_fan_speed(&self) -> i32 {
fs::read_to_string(self.hwmon_path.join("fan1_input"))
.expect("Couldn't read fan speed")
.parse::<i32>()
.unwrap()
}
pub fn get_mem_freq(&self) -> i32 {
let filename = self.hwmon_path.join(format!("freq{}_input", self.freqs.get("mclk").unwrap()));
fs::read_to_string(filename).unwrap().trim().parse::<i32>().unwrap() / 1000 / 1000
}
pub fn get_gpu_freq(&self) -> i32 {
let filename = self.hwmon_path.join(format!("freq{}_input", self.freqs.get("sclk").unwrap()));
fs::read_to_string(filename).unwrap().trim().parse::<i32>().unwrap() / 1000 / 1000
}
}

View File

@@ -1,20 +1,89 @@
use std::io::{Read, Write};
use std::os::unix::net::UnixStream;
use gpu_controller::{GpuInfo, GpuStats};
pub mod daemon;
pub mod fan_controller;
pub mod gpu_controller;
pub mod hw_mon;
pub mod daemon_connection;
use std::{path::PathBuf, io::{Read, Write}};
use serde::{Deserialize, Serialize};
use std::fs;
use std::os::unix::net::{UnixListener, UnixStream};
use std::process::Command;
use std::thread;
use crate::gpu_controller::GpuController;
pub const SOCK_PATH: &str = "/tmp/amdgpu-configurator.sock";
pub struct Daemon {
gpu_controller: GpuController,
listener: UnixListener,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Action {
CheckAlive,
GetInfo,
GetStats,
}
impl Daemon {
pub fn new() -> Daemon {
if fs::metadata(SOCK_PATH).is_ok() {
fs::remove_file(SOCK_PATH).expect("Failed to take control over socket");
}
let listener = UnixListener::bind(SOCK_PATH).unwrap();
Command::new("chmod")
.arg("666")
.arg(SOCK_PATH)
.output()
.expect("Failed to chmod");
Daemon { listener, gpu_controller: GpuController::new(PathBuf::from("/sys/class/drm/card0/device"))}
}
pub fn listen(self) {
for stream in self.listener.incoming() {
match stream {
Ok(stream) => {
let controller = self.gpu_controller.clone();
thread::spawn(move || Daemon::handle_connection(controller, stream));
}
Err(err) => {
println!("Error: {}", err);
break;
}
}
}
}
fn handle_connection(gpu_controller: GpuController, mut stream: UnixStream) {
let mut buffer: [u8; 4] = [0; 4];
stream.read(&mut buffer).unwrap();
println!("finished reading, buffer size {}", buffer.len());
let action: Action = bincode::deserialize(&buffer).expect("Failed to deserialize buffer");
let response: Vec<u8> = match action {
Action::GetStats => bincode::serialize(&gpu_controller.get_stats()).unwrap(),
Action::GetInfo => bincode::serialize(&gpu_controller.gpu_info).unwrap(),
Action::CheckAlive => vec![1],
};
println!("responding with {:?}", response);
stream
.write_all(&response)
.expect("Failed writing response");
}
}
#[derive(Debug)]
pub enum DaemonError {
ConnectionFailed,
}
pub fn get_gpu_stats() -> GpuStats {
/*pub fn get_gpu_stats() -> GpuStats {
let mut stream = UnixStream::connect(SOCK_PATH).expect("Failed to connect to daemon");
stream
.write_all(&bincode::serialize(&daemon::Action::GetStats).unwrap())
@@ -44,6 +113,4 @@ pub fn get_gpu_info() -> Result<GpuInfo, DaemonError> {
}
Err(_) => Err(DaemonError::ConnectionFailed),
}
}
}*/

View File

@@ -1,8 +1,6 @@
use daemon::daemon::Daemon;
use daemon::gpu_controller::GpuController;
use daemon::Daemon;
fn main() {
let gpu_controller = GpuController::new("/sys/class/drm/card0/device");
let d = Daemon::new(gpu_controller);
d.run();
let d = Daemon::new();
d.listen();
}

View File

@@ -2,16 +2,18 @@ extern crate gdk;
extern crate gio;
extern crate gtk;
use daemon::{Daemon, daemon_connection::DaemonConnection};
use gio::prelude::*;
use gtk::{prelude::*, ButtonsType, DialogFlags, MessageType};
use gtk::{ButtonsType, DialogFlags, Label, LevelBar, MessageType, prelude::*};
use gtk::{Builder, MessageDialog, TextBuffer, Window};
use std::env::args;
use std::{process::Command, env::args, thread, time::Duration};
fn build_ui(application: &gtk::Application) {
let glade_src = include_str!("main_window.glade");
let builder = Builder::from_string(glade_src);
println!("Getting elements");
let main_window: Window = builder
.get_object("main_window")
@@ -41,7 +43,7 @@ fn build_ui(application: &gtk::Application) {
.get_object("link_speed_text_buffer")
.expect("Couldn't get textbuffer");
let vulkan_device_name_text_buffer: TextBuffer = builder
let vulkan_device_name_text_buffer: TextBuffer = builder
.get_object("vulkan_device_name_text_buffer")
.expect("Couldn't get textbuffer");
@@ -53,32 +55,82 @@ fn build_ui(application: &gtk::Application) {
.get_object("vulkan_features_text_buffer")
.expect("Couldn't get textbuffer");
let vram_usage_level_bar: LevelBar = builder
.get_object("vram_usage_level_bar")
.expect("Couldnt get levelbar");
match daemon::get_gpu_info() {
Ok(gpu_info) => {
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);
driver_text_buffer.set_text(&gpu_info.driver);
vram_size_text_buffer.set_text(&format!("{} MiB", &gpu_info.vram_size));
link_speed_text_buffer.set_text(&format!("{} x{}", &gpu_info.link_speed, &gpu_info.link_width));
let vram_usage_label: Label = builder
.get_object("vram_usage_label")
.expect("Couldn't get label");
let gpu_clock_text_buffer: TextBuffer = builder
.get_object("gpu_clock_text_buffer").unwrap();
vulkan_device_name_text_buffer.set_text(&gpu_info.vulkan_info.device_name);
vulkan_version_text_buffer.set_text(&gpu_info.vulkan_info.api_version);
vulkan_features_text_buffer.set_text(&gpu_info.vulkan_info.features);
}
let vram_clock_text_buffer: TextBuffer = builder
.get_object("vram_clock_text_buffer").unwrap();
let d = match DaemonConnection::new() {
Ok(a) => a,
Err(_) => {
MessageDialog::new(
let daemon = Daemon::new();
thread::spawn(move || {
daemon.listen();
});
let diag = MessageDialog::new(
None::<&Window>,
DialogFlags::empty(),
MessageType::Error,
ButtonsType::Ok,
"Unable to connect to service",
)
.run();
application.quit();
"Running in unpriveleged mode",
);
diag.run();
diag.hide();
DaemonConnection::new().unwrap()
}
}
};
println!("Connected");
let gpu_info = d.get_gpu_info();
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);
driver_text_buffer.set_text(&gpu_info.driver);
vram_size_text_buffer.set_text(&format!("{} MiB", &gpu_info.vram_size));
link_speed_text_buffer.set_text(&format!(
"{} x{}",
&gpu_info.link_speed, &gpu_info.link_width
));
vulkan_device_name_text_buffer.set_text(&gpu_info.vulkan_info.device_name);
vulkan_version_text_buffer.set_text(&gpu_info.vulkan_info.api_version);
vulkan_features_text_buffer.set_text(&gpu_info.vulkan_info.features);
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
thread::spawn(move || {
loop {
let gpu_stats = d.get_gpu_stats();
tx.send(gpu_stats).expect("Couldn't send text");
thread::sleep(Duration::from_millis(500));
}
});
rx.attach(None, move |gpu_stats| {
vram_usage_level_bar.set_max_value(gpu_stats.mem_total as f64);
vram_usage_level_bar.set_value(gpu_stats.mem_used as f64);
let text = format!("{}/{} MiB", gpu_stats.mem_used, gpu_stats.mem_total);
vram_usage_label.set_text(&text);
gpu_clock_text_buffer.set_text(&format!("{}MHz", gpu_stats.gpu_freq));
vram_clock_text_buffer.set_text(&format!("{}MHz", gpu_stats.mem_freq));
glib::Continue(true)
});
main_window.set_application(Some(application));

View File

@@ -5,6 +5,9 @@
<object class="GtkTextBuffer" id="driver_text_buffer">
<property name="text" translatable="yes">driver</property>
</object>
<object class="GtkTextBuffer" id="gpu_clock_text_buffer">
<property name="text" translatable="yes">1000MHz</property>
</object>
<object class="GtkTextBuffer" id="gpu_model_text_buffer">
<property name="text">gpu_model</property>
</object>
@@ -17,6 +20,9 @@
<object class="GtkTextBuffer" id="vbios_version_text_buffer">
<property name="text" translatable="yes">vbios_version</property>
</object>
<object class="GtkTextBuffer" id="vram_clock_text_buffer">
<property name="text" translatable="yes">1500MHz</property>
</object>
<object class="GtkTextBuffer" id="vram_size_text_buffer">
<property name="text" translatable="yes">1024 MiB</property>
</object>
@@ -401,30 +407,160 @@
</packing>
</child>
<child>
<!-- n-columns=3 n-rows=3 -->
<!-- n-columns=2 n-rows=3 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<placeholder/>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="label" translatable="yes">VRAM Usage</property>
<property name="justify">center</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<placeholder/>
<object class="GtkOverlay">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLevelBar" id="vram_usage_level_bar">
<property name="height-request">30</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="valign">start</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="hexpand">True</property>
<property name="value">0.5</property>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
<child type="overlay">
<object class="GtkLabel" id="vram_usage_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">512/1024 MiB</property>
<attributes>
<attribute name="style" value="normal"/>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<placeholder/>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="label" translatable="yes">GPU Clock</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">6</property>
<property name="editable">False</property>
<property name="cursor-visible">False</property>
<property name="buffer">gpu_clock_text_buffer</property>
<property name="accepts-tab">False</property>
<property name="monospace">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">center</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">5</property>
<property name="margin-bottom">5</property>
<property name="label" translatable="yes">VRAM Clock</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTextView">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-start">5</property>
<property name="margin-end">5</property>
<property name="margin-top">6</property>
<property name="editable">False</property>
<property name="cursor-visible">False</property>
<property name="buffer">vram_clock_text_buffer</property>
<property name="accepts-tab">False</property>
<property name="monospace">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<placeholder/>
@@ -441,7 +577,7 @@
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">page 2</property>
<property name="label" translatable="yes">OC</property>
</object>
<packing>
<property name="position">1</property>