From 863d1b18c4cddb0b500e35df95d119774d45ae55 Mon Sep 17 00:00:00 2001 From: nokyan Date: Thu, 12 Dec 2024 16:11:33 +0100 Subject: [PATCH 1/5] Add hint to warn print statement --- src/ui/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/window.rs b/src/ui/window.rs index 8e7862a..07a5d79 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -495,7 +495,7 @@ impl MainWindow { Process::all_data() .inspect_err(|e| { warn!( - "Unable to update process and app data!\n{e}\n{}", + "Unable to update process and app data! Is resources-processes running?\n{e}\n{}", e.backtrace() ); }) From bfe7717551f76f6b78b86235b80fcbded1ddecb6 Mon Sep 17 00:00:00 2001 From: nokyan Date: Thu, 12 Dec 2024 16:11:58 +0100 Subject: [PATCH 2/5] Don't extract DRM client ID since we don't use it --- lib/process_data/src/lib.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/process_data/src/lib.rs b/lib/process_data/src/lib.rs index 4bc932f..bbc73f2 100644 --- a/lib/process_data/src/lib.rs +++ b/lib/process_data/src/lib.rs @@ -49,8 +49,6 @@ static RE_IO_WRITE: Lazy = lazy_regex!(r"write_bytes:\s*(\d+)"); static RE_DRM_PDEV: Lazy = lazy_regex!(r"drm-pdev:\s*([0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}\.[0-9A-Fa-f])"); -static RE_DRM_CLIENT_ID: Lazy = lazy_regex!(r"drm-client-id:\s*(\d+)"); - // AMD only static RE_DRM_ENGINE_GFX: Lazy = lazy_regex!(r"drm-engine-gfx:\s*(\d+)\s*ns"); @@ -486,10 +484,7 @@ impl ProcessData { Ok(return_map) } - fn read_fdinfo( - fdinfo_file: &mut File, - file_size: usize, - ) -> Result<(PciSlot, GpuUsageStats, i64)> { + fn read_fdinfo(fdinfo_file: &mut File, file_size: usize) -> Result<(PciSlot, GpuUsageStats)> { let mut content = String::with_capacity(file_size); fdinfo_file.read_to_string(&mut content)?; fdinfo_file.flush()?; @@ -499,12 +494,7 @@ impl ProcessData { .and_then(|captures| captures.get(1)) .and_then(|capture| PciSlot::from_str(capture.as_str()).ok()); - let client_id = RE_DRM_CLIENT_ID - .captures(&content) - .and_then(|captures| captures.get(1)) - .and_then(|capture| capture.as_str().parse::().ok()); - - if let (Some(pci_slot), Some(client_id)) = (pci_slot, client_id) { + if let Some(pci_slot) = pci_slot { let gfx = RE_DRM_ENGINE_GFX // amd .captures(&content) .and_then(|captures| captures.get(1)) @@ -565,7 +555,7 @@ impl ProcessData { nvidia: false, }; - return Ok((pci_slot, stats, client_id)); + return Ok((pci_slot, stats)); } bail!("unable to find gpu information in this fdinfo"); From 58b7b62ac7afb89f116a839450ee60a867d324ba Mon Sep 17 00:00:00 2001 From: nokyan Date: Thu, 12 Dec 2024 18:03:07 +0100 Subject: [PATCH 3/5] Add support for Raspberry PI GPUs (Broadcom VideoCore) --- lib/process_data/src/lib.rs | 102 +++++++++++++++++++++--------- src/ui/pages/gpu.rs | 11 +++- src/ui/window.rs | 16 ++--- src/utils/app.rs | 24 +++---- src/utils/gpu/amd.rs | 12 ++-- src/utils/gpu/intel.rs | 12 ++-- src/utils/gpu/mod.rs | 122 ++++++++++++++++++++++++------------ src/utils/gpu/nvidia.rs | 14 ++--- src/utils/gpu/other.rs | 12 ++-- src/utils/gpu/v3d.rs | 110 ++++++++++++++++++++++++++++++++ src/utils/process.rs | 4 +- 11 files changed, 321 insertions(+), 118 deletions(-) create mode 100644 src/utils/gpu/v3d.rs diff --git a/lib/process_data/src/lib.rs b/lib/process_data/src/lib.rs index bbc73f2..db60fd1 100644 --- a/lib/process_data/src/lib.rs +++ b/lib/process_data/src/lib.rs @@ -11,6 +11,7 @@ use nvml_wrapper::{Device, Nvml}; use pci_slot::PciSlot; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fmt::Display; use std::fs::File; use std::io::{Read, Write}; use std::os::linux::fs::MetadataExt; @@ -46,6 +47,8 @@ static RE_IO_READ: Lazy = lazy_regex!(r"read_bytes:\s*(\d+)"); static RE_IO_WRITE: Lazy = lazy_regex!(r"write_bytes:\s*(\d+)"); +static RE_DRM_DRIVER: Lazy = lazy_regex!(r"drm-driver:\s*(.+)"); + static RE_DRM_PDEV: Lazy = lazy_regex!(r"drm-pdev:\s*([0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}\.[0-9A-Fa-f])"); @@ -67,12 +70,15 @@ static RE_DRM_MEMORY_VRAM: Lazy = lazy_regex!(r"drm-memory-vram:\s*(\d+)\ // AMD only static RE_DRM_MEMORY_GTT: Lazy = lazy_regex!(r"drm-memory-gtt:\s*(\d+)\s*KiB"); -// Intel only +// Intel and v3d only static RE_DRM_ENGINE_RENDER: Lazy = lazy_regex!(r"drm-engine-render:\s*(\d+)\s*ns"); // Intel only static RE_DRM_ENGINE_VIDEO: Lazy = lazy_regex!(r"drm-engine-video:\s*(\d+)\s*ns"); +// v3d only +static RE_DRM_TOTAL_MEMORY: Lazy = lazy_regex!(r"drm-total-memory:\s*(\d+)\s*KiB"); + static NVML: Lazy> = Lazy::new(Nvml::init); static NVML_DEVICES: Lazy> = Lazy::new(|| { @@ -115,6 +121,27 @@ pub enum Containerization { Snap, } +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, Copy, PartialOrd, Ord)] +pub enum GpuIdentifier { + PciSlot(PciSlot), + Enumerator(usize), +} + +impl Default for GpuIdentifier { + fn default() -> Self { + GpuIdentifier::Enumerator(0) + } +} + +impl Display for GpuIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + GpuIdentifier::PciSlot(pci_slot) => write!(f, "{}", pci_slot), + GpuIdentifier::Enumerator(e) => write!(f, "{}", e), + } + } +} + /// Represents GPU usage statistics per-process. Depending on the GPU manufacturer (which should be determined in /// Resources itself), these numbers need to interpreted differently /// @@ -156,7 +183,7 @@ pub struct ProcessData { pub write_bytes: Option, pub timestamp: u64, /// Key: PCI Slot ID of the GPU - pub gpu_usage_stats: BTreeMap, + pub gpu_usage_stats: BTreeMap, } impl ProcessData { @@ -387,7 +414,7 @@ impl ProcessData { }) } - fn gpu_usage_stats(proc_path: &Path, pid: i32) -> BTreeMap { + fn gpu_usage_stats(proc_path: &Path, pid: i32) -> BTreeMap { let nvidia_stats = Self::nvidia_gpu_stats_all(pid); let mut other_stats = Self::other_gpu_usage_stats(proc_path, pid).unwrap_or_default(); other_stats.extend(nvidia_stats); @@ -397,7 +424,7 @@ impl ProcessData { fn other_gpu_usage_stats( proc_path: &Path, pid: i32, - ) -> Result> { + ) -> Result> { let fdinfo_dir = proc_path.join("fdinfo"); let mut seen_fds = HashSet::new(); @@ -484,28 +511,37 @@ impl ProcessData { Ok(return_map) } - fn read_fdinfo(fdinfo_file: &mut File, file_size: usize) -> Result<(PciSlot, GpuUsageStats)> { + fn read_fdinfo( + fdinfo_file: &mut File, + file_size: usize, + ) -> Result<(GpuIdentifier, GpuUsageStats)> { let mut content = String::with_capacity(file_size); fdinfo_file.read_to_string(&mut content)?; fdinfo_file.flush()?; - let pci_slot = RE_DRM_PDEV + let driver = RE_DRM_DRIVER .captures(&content) .and_then(|captures| captures.get(1)) - .and_then(|capture| PciSlot::from_str(capture.as_str()).ok()); + .map(|capture| capture.as_str()); + + if driver.is_some() { + let gpu_identifier = RE_DRM_PDEV + .captures(&content) + .and_then(|captures| captures.get(1)) + .and_then(|capture| PciSlot::from_str(capture.as_str()).ok()) + .map(|pci_slot| GpuIdentifier::PciSlot(pci_slot)) + .unwrap_or_default(); - if let Some(pci_slot) = pci_slot { - let gfx = RE_DRM_ENGINE_GFX // amd + let gfx = RE_DRM_ENGINE_GFX + .captures(&content) + .and_then(|captures| captures.get(1)) + .and_then(|capture| capture.as_str().parse::().ok()) + .unwrap_or_default(); + + let render = RE_DRM_ENGINE_RENDER .captures(&content) .and_then(|captures| captures.get(1)) .and_then(|capture| capture.as_str().parse::().ok()) - .or_else(|| { - // intel - RE_DRM_ENGINE_RENDER - .captures(&content) - .and_then(|captures| captures.get(1)) - .and_then(|capture| capture.as_str().parse::().ok()) - }) .unwrap_or_default(); let compute = RE_DRM_ENGINE_COMPUTE @@ -514,17 +550,16 @@ impl ProcessData { .and_then(|capture| capture.as_str().parse::().ok()) .unwrap_or_default(); - let enc = RE_DRM_ENGINE_ENC // amd + let enc = RE_DRM_ENGINE_ENC + .captures(&content) + .and_then(|captures| captures.get(1)) + .and_then(|capture| capture.as_str().parse::().ok()) + .unwrap_or_default(); + + let video = RE_DRM_ENGINE_VIDEO .captures(&content) .and_then(|captures| captures.get(1)) .and_then(|capture| capture.as_str().parse::().ok()) - .or_else(|| { - // intel - RE_DRM_ENGINE_VIDEO - .captures(&content) - .and_then(|captures| captures.get(1)) - .and_then(|capture| capture.as_str().parse::().ok()) - }) .unwrap_or_default(); let dec = RE_DRM_ENGINE_DEC @@ -547,26 +582,33 @@ impl ProcessData { .unwrap_or_default() .saturating_mul(1024); + let total_memory = RE_DRM_TOTAL_MEMORY + .captures(&content) + .and_then(|captures| captures.get(1)) + .and_then(|capture| capture.as_str().parse::().ok()) + .unwrap_or_default() + .saturating_mul(1024); + let stats = GpuUsageStats { - gfx: gfx.saturating_add(compute), - mem: vram.saturating_add(gtt), - enc, + gfx: gfx.saturating_add(render).saturating_add(compute), + mem: vram.saturating_add(gtt).saturating_add(total_memory), + enc: enc.saturating_add(video), dec, nvidia: false, }; - return Ok((pci_slot, stats)); + return Ok((gpu_identifier, stats)); } bail!("unable to find gpu information in this fdinfo"); } - fn nvidia_gpu_stats_all(pid: i32) -> BTreeMap { + fn nvidia_gpu_stats_all(pid: i32) -> BTreeMap { let mut return_map = BTreeMap::new(); for (pci_slot, _) in NVML_DEVICES.iter() { if let Ok(stats) = Self::nvidia_gpu_stats(pid, *pci_slot) { - return_map.insert(pci_slot.to_owned(), stats); + return_map.insert(GpuIdentifier::PciSlot(pci_slot.to_owned()), stats); } } diff --git a/src/ui/pages/gpu.rs b/src/ui/pages/gpu.rs index 21f5f21..9499d9d 100644 --- a/src/ui/pages/gpu.rs +++ b/src/ui/pages/gpu.rs @@ -192,7 +192,7 @@ impl ResGPU { pub fn setup_widgets(&self, gpu: &Gpu) { let imp = self.imp(); - let tab_id = format!("{}-{}", TAB_ID_PREFIX, &gpu.pci_slot().to_string()); + let tab_id = format!("{}-{}", TAB_ID_PREFIX, &gpu.gpu_identifier()); imp.set_tab_id(&tab_id); imp.gpu_usage.set_title_label(&i18n("Total Usage")); @@ -231,7 +231,12 @@ impl ResGPU { .map_or_else(|_| i18n("N/A"), |vendor| vendor.name().to_string()), ); - imp.pci_slot.set_subtitle(&gpu.pci_slot().to_string()); + match gpu.gpu_identifier() { + process_data::GpuIdentifier::PciSlot(pci_slot) => { + imp.pci_slot.set_subtitle(&pci_slot.to_string()) + } + process_data::GpuIdentifier::Enumerator(_) => imp.pci_slot.set_subtitle(&i18n("N/A")), + } imp.driver_used.set_subtitle(&gpu.driver()); @@ -252,7 +257,7 @@ impl ResGPU { let imp = self.imp(); let GpuData { - pci_slot: _, + gpu_identifier: _, usage_fraction, encode_fraction, decode_fraction, diff --git a/src/ui/window.rs b/src/ui/window.rs index 07a5d79..6c35bca 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -59,7 +59,7 @@ mod imp { use async_channel::{unbounded, Receiver, Sender}; use gtk::CompositeTemplate; - use process_data::pci_slot::PciSlot; + use process_data::{pci_slot::PciSlot, GpuIdentifier}; #[derive(Debug, CompositeTemplate)] #[template(resource = "/net/nokyan/Resources/ui/window.ui")] @@ -95,7 +95,7 @@ mod imp { pub battery_pages: RefCell>, - pub gpu_pages: RefCell>, + pub gpu_pages: RefCell>, pub npu_pages: RefCell>, @@ -325,7 +325,7 @@ impl MainWindow { imp.gpu_pages .borrow_mut() - .insert(gpu.pci_slot(), (gpu.clone(), added_page)); + .insert(gpu.gpu_identifier(), (gpu.clone(), added_page)); } } @@ -391,7 +391,7 @@ impl MainWindow { *imp.apps_context.borrow_mut() = AppsContext::new( gpus.iter() .filter(|gpu| gpu.combined_media_engine().unwrap_or_default()) - .map(Gpu::pci_slot) + .map(Gpu::gpu_identifier) .collect(), ); imp.applications.init(imp.sender.clone()); @@ -556,19 +556,21 @@ impl MainWindow { // average usage during now and the last refresh, while gpu_busy_percent is a snapshot of the current // usage, which might not be what we want - let processes_gpu_fraction = apps_context.gpu_fraction(gpu_data.pci_slot); + let processes_gpu_fraction = apps_context.gpu_fraction(gpu_data.gpu_identifier); gpu_data.usage_fraction = Some(f64::max( gpu_data.usage_fraction.unwrap_or(0.0), processes_gpu_fraction.into(), )); - let processes_encode_fraction = apps_context.encoder_fraction(gpu_data.pci_slot); + let processes_encode_fraction = + apps_context.encoder_fraction(gpu_data.gpu_identifier); gpu_data.encode_fraction = Some(f64::max( gpu_data.encode_fraction.unwrap_or(0.0), processes_encode_fraction.into(), )); - let processes_decode_fraction = apps_context.decoder_fraction(gpu_data.pci_slot); + let processes_decode_fraction = + apps_context.decoder_fraction(gpu_data.gpu_identifier); gpu_data.decode_fraction = Some(f64::max( gpu_data.decode_fraction.unwrap_or(0.0), processes_decode_fraction.into(), diff --git a/src/utils/app.rs b/src/utils/app.rs index 3e684a9..43aeb07 100644 --- a/src/utils/app.rs +++ b/src/utils/app.rs @@ -12,7 +12,7 @@ use gtk::{ }; use lazy_regex::{lazy_regex, Lazy, Regex}; use log::{debug, info}; -use process_data::{pci_slot::PciSlot, Containerization, ProcessData}; +use process_data::{Containerization, GpuIdentifier, ProcessData}; use crate::i18n::i18n; @@ -163,7 +163,7 @@ static MESSAGE_LOCALES: LazyLock> = LazyLock::new(|| { pub struct AppsContext { apps: HashMap, App>, processes: HashMap, - gpus_with_combined_media_engine: Vec, + gpus_with_combined_media_engine: Vec, } /// Represents an application installed on the system. It doesn't @@ -511,7 +511,7 @@ impl AppsContext { /// Creates a new `AppsContext` object, this operation is quite expensive /// so try to do it only one time during the lifetime of the program. /// Please call `refresh()` immediately after this function. - pub fn new(gpus_with_combined_media_engine: Vec) -> AppsContext { + pub fn new(gpus_with_combined_media_engine: Vec) -> AppsContext { let apps: HashMap, App> = App::all() .into_iter() .map(|app| (app.id.clone(), app)) @@ -524,7 +524,7 @@ impl AppsContext { } } - pub fn gpu_fraction(&self, pci_slot: PciSlot) -> f32 { + pub fn gpu_fraction(&self, gpu_identifier: GpuIdentifier) -> f32 { self.processes_iter() .map(|process| { ( @@ -536,8 +536,8 @@ impl AppsContext { }) .map(|(new, old, timestamp, timestamp_last)| { ( - new.get(&pci_slot), - old.get(&pci_slot), + new.get(&gpu_identifier), + old.get(&gpu_identifier), timestamp, timestamp_last, ) @@ -562,7 +562,7 @@ impl AppsContext { .clamp(0.0, 1.0) } - pub fn encoder_fraction(&self, pci_slot: PciSlot) -> f32 { + pub fn encoder_fraction(&self, gpu_identifier: GpuIdentifier) -> f32 { self.processes_iter() .map(|process| { ( @@ -574,8 +574,8 @@ impl AppsContext { }) .map(|(new, old, timestamp, timestamp_last)| { ( - new.get(&pci_slot), - old.get(&pci_slot), + new.get(&gpu_identifier), + old.get(&gpu_identifier), timestamp, timestamp_last, ) @@ -600,7 +600,7 @@ impl AppsContext { .clamp(0.0, 1.0) } - pub fn decoder_fraction(&self, pci_slot: PciSlot) -> f32 { + pub fn decoder_fraction(&self, gpu_identifier: GpuIdentifier) -> f32 { self.processes_iter() .map(|process| { ( @@ -612,8 +612,8 @@ impl AppsContext { }) .map(|(new, old, timestamp, timestamp_last)| { ( - new.get(&pci_slot), - old.get(&pci_slot), + new.get(&gpu_identifier), + old.get(&gpu_identifier), timestamp, timestamp_last, ) diff --git a/src/utils/gpu/amd.rs b/src/utils/gpu/amd.rs index a2676a5..9cf850b 100644 --- a/src/utils/gpu/amd.rs +++ b/src/utils/gpu/amd.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Result}; use lazy_regex::{lazy_regex, Lazy, Regex}; use log::{debug, warn}; -use process_data::pci_slot::PciSlot; +use process_data::GpuIdentifier; use std::{collections::HashMap, path::PathBuf, sync::LazyLock, time::Instant}; @@ -24,7 +24,7 @@ static AMDGPU_IDS: LazyLock> = LazyLock::new(|| { pub struct AmdGpu { pub device: Option<&'static Device>, - pub pci_slot: PciSlot, + pub gpu_identifier: GpuIdentifier, pub driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, @@ -34,14 +34,14 @@ pub struct AmdGpu { impl AmdGpu { pub fn new( device: Option<&'static Device>, - pci_slot: PciSlot, + gpu_identifier: GpuIdentifier, driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, ) -> Self { let mut gpu = Self { device, - pci_slot, + gpu_identifier, driver, sysfs_path, first_hwmon_path, @@ -100,8 +100,8 @@ impl GpuImpl for AmdGpu { self.device } - fn pci_slot(&self) -> PciSlot { - self.pci_slot + fn gpu_identifier(&self) -> GpuIdentifier { + self.gpu_identifier } fn driver(&self) -> String { diff --git a/src/utils/gpu/intel.rs b/src/utils/gpu/intel.rs index 7140b5a..d32e3be 100644 --- a/src/utils/gpu/intel.rs +++ b/src/utils/gpu/intel.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use process_data::pci_slot::PciSlot; +use process_data::GpuIdentifier; use std::path::PathBuf; @@ -11,7 +11,7 @@ use super::GpuImpl; pub struct IntelGpu { pub device: Option<&'static Device>, - pub pci_slot: PciSlot, + pub gpu_identifier: GpuIdentifier, pub driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, @@ -20,14 +20,14 @@ pub struct IntelGpu { impl IntelGpu { pub fn new( device: Option<&'static Device>, - pci_slot: PciSlot, + gpu_identifier: GpuIdentifier, driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, ) -> Self { Self { device, - pci_slot, + gpu_identifier, driver, sysfs_path, first_hwmon_path, @@ -40,8 +40,8 @@ impl GpuImpl for IntelGpu { self.device } - fn pci_slot(&self) -> PciSlot { - self.pci_slot + fn gpu_identifier(&self) -> GpuIdentifier { + self.gpu_identifier } fn driver(&self) -> String { diff --git a/src/utils/gpu/mod.rs b/src/utils/gpu/mod.rs index c269ab0..4ffd01b 100644 --- a/src/utils/gpu/mod.rs +++ b/src/utils/gpu/mod.rs @@ -2,10 +2,13 @@ mod amd; mod intel; mod nvidia; mod other; +mod v3d; use anyhow::{bail, Context, Result}; +use lazy_regex::{lazy_regex, Lazy, Regex}; use log::{debug, info}; -use process_data::pci_slot::PciSlot; +use process_data::{pci_slot::PciSlot, GpuIdentifier}; +use v3d::V3dGpu; use std::{ path::{Path, PathBuf}, @@ -27,9 +30,11 @@ pub const VID_AMD: u16 = 4098; pub const VID_INTEL: u16 = 32902; pub const VID_NVIDIA: u16 = 4318; +const RE_CARD_ENUMARATOR: Lazy = lazy_regex!(r"(\d+)\/?$"); + #[derive(Debug)] pub struct GpuData { - pub pci_slot: PciSlot, + pub gpu_identifier: GpuIdentifier, pub usage_fraction: Option, @@ -54,7 +59,7 @@ pub struct GpuData { impl GpuData { pub fn new(gpu: &Gpu) -> Self { - let pci_slot = gpu.pci_slot(); + let gpu_identifier = gpu.gpu_identifier(); let usage_fraction = gpu.usage().map(|usage| usage.clamp(0.0, 1.0)).ok(); @@ -77,7 +82,7 @@ impl GpuData { let nvidia = matches!(gpu, Gpu::Nvidia(_)); Self { - pci_slot, + gpu_identifier, usage_fraction, encode_fraction, decode_fraction, @@ -97,8 +102,9 @@ impl GpuData { #[derive(Debug, Clone)] pub enum Gpu { Amd(AmdGpu), - Nvidia(NvidiaGpu), Intel(IntelGpu), + Nvidia(NvidiaGpu), + V3d(V3dGpu), Other(OtherGpu), } @@ -110,7 +116,7 @@ impl Default for Gpu { pub trait GpuImpl { fn device(&self) -> Option<&'static Device>; - fn pci_slot(&self) -> PciSlot; + fn gpu_identifier(&self) -> GpuIdentifier; fn driver(&self) -> String; fn sysfs_path(&self) -> PathBuf; fn first_hwmon(&self) -> Option; @@ -215,8 +221,8 @@ impl Gpu { debug!("Searching for GPUs…"); let mut gpu_vec: Vec = Vec::new(); - for entry in glob("/sys/class/drm/card?")?.flatten() { - if let Ok(gpu) = Self::from_sysfs_path(entry) { + for (i, entry) in glob("/sys/class/drm/card?")?.flatten().enumerate() { + if let Ok(gpu) = Self::from_sysfs_path(entry, i) { gpu_vec.push(gpu); } } @@ -226,8 +232,15 @@ impl Gpu { Ok(gpu_vec) } - fn from_sysfs_path>(path: P) -> Result { - let sysfs_device_path = path.as_ref().join("device"); + fn from_sysfs_path>(path: P, i: usize) -> Result { + let path = path.as_ref().to_path_buf(); + let enumarator = RE_CARD_ENUMARATOR + .captures(&path.to_string_lossy()) + .and_then(|captures| captures.get(1)) + .and_then(|capture| capture.as_str().parse().ok()) + .unwrap_or(i); + + let sysfs_device_path = path.join("device"); let uevent_contents = read_uevent(sysfs_device_path.join("uevent"))?; let (device, vid, pid) = if let Some(pci_line) = uevent_contents.get("PCI_ID") { @@ -256,7 +269,13 @@ impl Gpu { .get("PCI_SLOT_NAME") .map_or_else(|| i18n("N/A"), std::string::ToString::to_string), ) - .context("can't turn PCI string to struct")?; + .context("can't turn PCI string to struct"); + + let gpu_identifier = if let Ok(pci_slot) = pci_slot { + GpuIdentifier::PciSlot(pci_slot) + } else { + GpuIdentifier::Enumerator(enumarator) + }; let driver = uevent_contents .get("DRIVER") @@ -267,13 +286,11 @@ impl Gpu { bail!("this is a simple framebuffer"); } - let path = path.as_ref().to_path_buf(); - let (gpu, gpu_category) = if vid == VID_AMD || driver == "amdgpu" { ( Gpu::Amd(AmdGpu::new( device, - pci_slot, + gpu_identifier, driver, path, hwmon_vec.first().cloned(), @@ -284,7 +301,7 @@ impl Gpu { ( Gpu::Intel(IntelGpu::new( device, - pci_slot, + gpu_identifier, driver, path, hwmon_vec.first().cloned(), @@ -295,18 +312,29 @@ impl Gpu { ( Gpu::Nvidia(NvidiaGpu::new( device, - pci_slot, + gpu_identifier, driver, path, hwmon_vec.first().cloned(), )), "NVIDIA", ) + } else if driver == "v3d" { + ( + Gpu::V3d(V3dGpu::new( + device, + gpu_identifier, + driver, + path, + hwmon_vec.first().cloned(), + )), + "v3d", + ) } else { ( Gpu::Other(OtherGpu::new( device, - pci_slot, + gpu_identifier, driver, path, hwmon_vec.first().cloned(), @@ -316,9 +344,9 @@ impl Gpu { }; info!( - "Found GPU \"{}\" (PCI slot: {} · PCI ID: {vid:x}:{pid:x} · Category: {gpu_category})", + "Found GPU \"{}\" (Identifier: {} · PCI ID: {vid:x}:{pid:x} · Category: {gpu_category})", gpu.name().unwrap_or("".into()), - gpu.pci_slot(), + gpu.gpu_identifier(), ); Ok(gpu) @@ -327,28 +355,31 @@ impl Gpu { pub fn get_vendor(&self) -> Result<&'static Vendor> { Ok(match self { Gpu::Amd(gpu) => gpu.device(), - Gpu::Nvidia(gpu) => gpu.device(), Gpu::Intel(gpu) => gpu.device(), + Gpu::Nvidia(gpu) => gpu.device(), + Gpu::V3d(gpu) => gpu.device(), Gpu::Other(gpu) => gpu.device(), } .context("no device")? .vendor()) } - pub fn pci_slot(&self) -> PciSlot { + pub fn gpu_identifier(&self) -> GpuIdentifier { match self { - Gpu::Amd(gpu) => gpu.pci_slot(), - Gpu::Nvidia(gpu) => gpu.pci_slot(), - Gpu::Intel(gpu) => gpu.pci_slot(), - Gpu::Other(gpu) => gpu.pci_slot(), + Gpu::Amd(gpu) => gpu.gpu_identifier(), + Gpu::Intel(gpu) => gpu.gpu_identifier(), + Gpu::Nvidia(gpu) => gpu.gpu_identifier(), + Gpu::V3d(gpu) => gpu.gpu_identifier(), + Gpu::Other(gpu) => gpu.gpu_identifier(), } } pub fn driver(&self) -> String { match self { Gpu::Amd(gpu) => gpu.driver(), - Gpu::Nvidia(gpu) => gpu.driver(), Gpu::Intel(gpu) => gpu.driver(), + Gpu::Nvidia(gpu) => gpu.driver(), + Gpu::V3d(gpu) => gpu.driver(), Gpu::Other(gpu) => gpu.driver(), } } @@ -356,8 +387,9 @@ impl Gpu { pub fn name(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.name(), - Gpu::Nvidia(gpu) => gpu.name(), Gpu::Intel(gpu) => gpu.name(), + Gpu::Nvidia(gpu) => gpu.name(), + Gpu::V3d(gpu) => gpu.name(), Gpu::Other(gpu) => gpu.name(), } } @@ -365,8 +397,9 @@ impl Gpu { pub fn usage(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.usage(), - Gpu::Nvidia(gpu) => gpu.usage(), Gpu::Intel(gpu) => gpu.usage(), + Gpu::Nvidia(gpu) => gpu.usage(), + Gpu::V3d(gpu) => gpu.usage(), Gpu::Other(gpu) => gpu.usage(), } } @@ -374,8 +407,9 @@ impl Gpu { pub fn encode_usage(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.encode_usage(), - Gpu::Nvidia(gpu) => gpu.encode_usage(), Gpu::Intel(gpu) => gpu.encode_usage(), + Gpu::Nvidia(gpu) => gpu.encode_usage(), + Gpu::V3d(gpu) => gpu.encode_usage(), Gpu::Other(gpu) => gpu.encode_usage(), } } @@ -383,8 +417,9 @@ impl Gpu { pub fn decode_usage(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.decode_usage(), - Gpu::Nvidia(gpu) => gpu.decode_usage(), Gpu::Intel(gpu) => gpu.decode_usage(), + Gpu::Nvidia(gpu) => gpu.decode_usage(), + Gpu::V3d(gpu) => gpu.decode_usage(), Gpu::Other(gpu) => gpu.decode_usage(), } } @@ -392,8 +427,9 @@ impl Gpu { pub fn combined_media_engine(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.combined_media_engine(), - Gpu::Nvidia(gpu) => gpu.combined_media_engine(), Gpu::Intel(gpu) => gpu.combined_media_engine(), + Gpu::Nvidia(gpu) => gpu.combined_media_engine(), + Gpu::V3d(gpu) => gpu.combined_media_engine(), Gpu::Other(gpu) => gpu.combined_media_engine(), } } @@ -401,8 +437,9 @@ impl Gpu { pub fn used_vram(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.used_vram(), - Gpu::Nvidia(gpu) => gpu.used_vram(), Gpu::Intel(gpu) => gpu.used_vram(), + Gpu::Nvidia(gpu) => gpu.used_vram(), + Gpu::V3d(gpu) => gpu.used_vram(), Gpu::Other(gpu) => gpu.used_vram(), } } @@ -410,8 +447,9 @@ impl Gpu { pub fn total_vram(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.total_vram(), - Gpu::Nvidia(gpu) => gpu.total_vram(), Gpu::Intel(gpu) => gpu.total_vram(), + Gpu::Nvidia(gpu) => gpu.total_vram(), + Gpu::V3d(gpu) => gpu.total_vram(), Gpu::Other(gpu) => gpu.total_vram(), } } @@ -419,8 +457,9 @@ impl Gpu { pub fn temperature(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.temperature(), - Gpu::Nvidia(gpu) => gpu.temperature(), Gpu::Intel(gpu) => gpu.temperature(), + Gpu::Nvidia(gpu) => gpu.temperature(), + Gpu::V3d(gpu) => gpu.temperature(), Gpu::Other(gpu) => gpu.temperature(), } } @@ -428,8 +467,9 @@ impl Gpu { pub fn power_usage(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.power_usage(), - Gpu::Nvidia(gpu) => gpu.power_usage(), Gpu::Intel(gpu) => gpu.power_usage(), + Gpu::Nvidia(gpu) => gpu.power_usage(), + Gpu::V3d(gpu) => gpu.power_usage(), Gpu::Other(gpu) => gpu.power_usage(), } } @@ -437,8 +477,9 @@ impl Gpu { pub fn core_frequency(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.core_frequency(), - Gpu::Nvidia(gpu) => gpu.core_frequency(), Gpu::Intel(gpu) => gpu.core_frequency(), + Gpu::Nvidia(gpu) => gpu.core_frequency(), + Gpu::V3d(gpu) => gpu.core_frequency(), Gpu::Other(gpu) => gpu.core_frequency(), } } @@ -446,8 +487,9 @@ impl Gpu { pub fn vram_frequency(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.vram_frequency(), - Gpu::Nvidia(gpu) => gpu.vram_frequency(), Gpu::Intel(gpu) => gpu.vram_frequency(), + Gpu::Nvidia(gpu) => gpu.vram_frequency(), + Gpu::V3d(gpu) => gpu.vram_frequency(), Gpu::Other(gpu) => gpu.vram_frequency(), } } @@ -455,8 +497,9 @@ impl Gpu { pub fn power_cap(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.power_cap(), - Gpu::Nvidia(gpu) => gpu.power_cap(), Gpu::Intel(gpu) => gpu.power_cap(), + Gpu::Nvidia(gpu) => gpu.power_cap(), + Gpu::V3d(gpu) => gpu.power_cap(), Gpu::Other(gpu) => gpu.power_cap(), } } @@ -464,8 +507,9 @@ impl Gpu { pub fn power_cap_max(&self) -> Result { match self { Gpu::Amd(gpu) => gpu.power_cap_max(), - Gpu::Nvidia(gpu) => gpu.power_cap_max(), Gpu::Intel(gpu) => gpu.power_cap_max(), + Gpu::Nvidia(gpu) => gpu.power_cap_max(), + Gpu::V3d(gpu) => gpu.power_cap_max(), Gpu::Other(gpu) => gpu.power_cap_max(), } } diff --git a/src/utils/gpu/nvidia.rs b/src/utils/gpu/nvidia.rs index f1668f3..b40aa26 100644 --- a/src/utils/gpu/nvidia.rs +++ b/src/utils/gpu/nvidia.rs @@ -5,7 +5,7 @@ use nvml_wrapper::{ error::NvmlError, Nvml, }; -use process_data::pci_slot::PciSlot; +use process_data::GpuIdentifier; use std::{path::PathBuf, sync::LazyLock}; @@ -29,7 +29,7 @@ use super::GpuImpl; pub struct NvidiaGpu { pub device: Option<&'static Device>, - pub pci_slot: PciSlot, + pub gpu_identifier: GpuIdentifier, pub driver: String, pci_slot_string: String, sysfs_path: PathBuf, @@ -39,16 +39,16 @@ pub struct NvidiaGpu { impl NvidiaGpu { pub fn new( device: Option<&'static Device>, - pci_slot: PciSlot, + gpu_identifier: GpuIdentifier, driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, ) -> Self { Self { device, - pci_slot, + gpu_identifier, driver, - pci_slot_string: pci_slot.to_string(), + pci_slot_string: gpu_identifier.to_string(), sysfs_path, first_hwmon_path, } @@ -69,8 +69,8 @@ impl GpuImpl for NvidiaGpu { self.device } - fn pci_slot(&self) -> PciSlot { - self.pci_slot + fn gpu_identifier(&self) -> GpuIdentifier { + self.gpu_identifier } fn driver(&self) -> String { diff --git a/src/utils/gpu/other.rs b/src/utils/gpu/other.rs index 5ce5526..d69de7f 100644 --- a/src/utils/gpu/other.rs +++ b/src/utils/gpu/other.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use process_data::pci_slot::PciSlot; +use process_data::GpuIdentifier; use std::path::PathBuf; @@ -11,7 +11,7 @@ use super::GpuImpl; pub struct OtherGpu { pub device: Option<&'static Device>, - pub pci_slot: PciSlot, + pub gpu_identifier: GpuIdentifier, pub driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, @@ -20,14 +20,14 @@ pub struct OtherGpu { impl OtherGpu { pub fn new( device: Option<&'static Device>, - pci_slot: PciSlot, + gpu_identifier: GpuIdentifier, driver: String, sysfs_path: PathBuf, first_hwmon_path: Option, ) -> Self { Self { device, - pci_slot, + gpu_identifier, driver, sysfs_path, first_hwmon_path, @@ -40,8 +40,8 @@ impl GpuImpl for OtherGpu { self.device } - fn pci_slot(&self) -> PciSlot { - self.pci_slot + fn gpu_identifier(&self) -> GpuIdentifier { + self.gpu_identifier } fn driver(&self) -> String { diff --git a/src/utils/gpu/v3d.rs b/src/utils/gpu/v3d.rs new file mode 100644 index 0000000..740c33d --- /dev/null +++ b/src/utils/gpu/v3d.rs @@ -0,0 +1,110 @@ +use anyhow::{bail, Result}; +use process_data::GpuIdentifier; + +use std::path::PathBuf; + +use crate::utils::pci::Device; + +use super::GpuImpl; + +#[derive(Debug, Clone, Default)] + +pub struct V3dGpu { + pub device: Option<&'static Device>, + pub gpu_identifier: GpuIdentifier, + pub driver: String, + sysfs_path: PathBuf, + first_hwmon_path: Option, +} + +impl V3dGpu { + pub fn new( + device: Option<&'static Device>, + gpu_identifier: GpuIdentifier, + driver: String, + sysfs_path: PathBuf, + first_hwmon_path: Option, + ) -> Self { + Self { + device, + gpu_identifier, + driver, + sysfs_path, + first_hwmon_path, + } + } +} + +impl GpuImpl for V3dGpu { + fn device(&self) -> Option<&'static Device> { + self.device + } + + fn gpu_identifier(&self) -> GpuIdentifier { + self.gpu_identifier + } + + fn driver(&self) -> String { + self.driver.clone() + } + + fn sysfs_path(&self) -> PathBuf { + self.sysfs_path.clone() + } + + fn first_hwmon(&self) -> Option { + self.first_hwmon_path.clone() + } + + fn name(&self) -> Result { + self.drm_name() + } + + fn usage(&self) -> Result { + self.drm_usage().map(|usage| usage as f64 / 100.0) + } + + fn encode_usage(&self) -> Result { + bail!("encode usage not implemented for v3d") + } + + fn decode_usage(&self) -> Result { + bail!("decode usage not implemented for v3d") + } + + fn combined_media_engine(&self) -> Result { + Ok(true) + } + + fn used_vram(&self) -> Result { + self.drm_used_vram().map(|usage| usage as usize) + } + + fn total_vram(&self) -> Result { + self.drm_total_vram().map(|usage| usage as usize) + } + + fn temperature(&self) -> Result { + self.hwmon_temperature() + } + + fn power_usage(&self) -> Result { + self.hwmon_power_usage() + } + + fn core_frequency(&self) -> Result { + Ok(self.read_sysfs_int("gt_cur_freq_mhz")? as f64 * 1_000_000.0) + } + + fn vram_frequency(&self) -> Result { + self.hwmon_vram_frequency() + } + + fn power_cap(&self) -> Result { + self.hwmon_power_cap() + } + + fn power_cap_max(&self) -> Result { + self.hwmon_power_cap_max() + } +} diff --git a/src/utils/process.rs b/src/utils/process.rs index fa24ac0..6d409be 100644 --- a/src/utils/process.rs +++ b/src/utils/process.rs @@ -1,7 +1,7 @@ use anyhow::{bail, Context, Result}; use config::LIBEXECDIR; use log::{debug, error, info}; -use process_data::{pci_slot::PciSlot, GpuUsageStats, Niceness, ProcessData}; +use process_data::{GpuIdentifier, GpuUsageStats, Niceness, ProcessData}; use std::{ collections::BTreeMap, ffi::{OsStr, OsString}, @@ -68,7 +68,7 @@ pub struct Process { pub timestamp_last: u64, pub read_bytes_last: Option, pub write_bytes_last: Option, - pub gpu_usage_stats_last: BTreeMap, + pub gpu_usage_stats_last: BTreeMap, pub display_name: String, } From 81ce8e18bdc8506ffc2c3559116ade0f1d892406 Mon Sep 17 00:00:00 2001 From: nokyan Date: Tue, 24 Dec 2024 06:39:49 +0100 Subject: [PATCH 4/5] Add Raspberry Pi hwmon and thermal zone names --- src/utils/cpu.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/cpu.rs b/src/utils/cpu.rs index df3e871..519ff7b 100644 --- a/src/utils/cpu.rs +++ b/src/utils/cpu.rs @@ -7,9 +7,9 @@ use std::{ sync::LazyLock, }; -const KNOWN_HWMONS: &[&str] = &["zenpower", "coretemp", "k10temp"]; +const KNOWN_HWMONS: &[&str] = &["rp1_adc", "zenpower", "coretemp", "k10temp"]; -const KNOWN_THERMAL_ZONES: &[&str] = &["x86_pkg_temp", "acpitz"]; +const KNOWN_THERMAL_ZONES: &[&str] = &["cpu-thermal", "x86_pkg_temp", "acpitz"]; static RE_LSCPU_MODEL_NAME: Lazy = lazy_regex!(r"Model name:\s*(.*)"); From 7992ddab07b58007d47ff0601cb09fb32a4993b6 Mon Sep 17 00:00:00 2001 From: nokyan Date: Wed, 25 Dec 2024 12:33:37 +0100 Subject: [PATCH 5/5] Remove rp1 sensor --- src/utils/cpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/cpu.rs b/src/utils/cpu.rs index 519ff7b..9a26af4 100644 --- a/src/utils/cpu.rs +++ b/src/utils/cpu.rs @@ -7,7 +7,7 @@ use std::{ sync::LazyLock, }; -const KNOWN_HWMONS: &[&str] = &["rp1_adc", "zenpower", "coretemp", "k10temp"]; +const KNOWN_HWMONS: &[&str] = &["zenpower", "coretemp", "k10temp"]; const KNOWN_THERMAL_ZONES: &[&str] = &["cpu-thermal", "x86_pkg_temp", "acpitz"];