diff --git a/crates/cardwire-daemon/src/analyzer/models.rs b/crates/cardwire-daemon/src/analyzer/models.rs index 4a9280af..7cf36588 100644 --- a/crates/cardwire-daemon/src/analyzer/models.rs +++ b/crates/cardwire-daemon/src/analyzer/models.rs @@ -75,8 +75,8 @@ impl CardwireAnalyzer { let exec_ring = blocker.get_exec_ring()?; let close_ring = blocker.get_close_ring()?; let report_ring = blocker.get_report_ring()?; - let pid_map = blocker.get_pid_map()?; - let forced_map = blocker.get_forced_pid_map()?; + let pid_map = Arc::clone(&blocker.pid_map); + let forced_map = Arc::clone(&blocker.forced_map); let ebpf_logger = blocker.get_ebpf_logger()?; let exec_ring = AsyncFd::new(exec_ring)?; @@ -85,8 +85,6 @@ impl CardwireAnalyzer { // Now Rwlock -> Arc let exec_ring = Arc::new(Mutex::new(exec_ring)); - let pid_map = Arc::new(RwLock::new(pid_map)); - let forced_map = Arc::new(RwLock::new(forced_map)); let close_ring = Arc::new(Mutex::new(close_ring)); let report_ring = Arc::new(Mutex::new(report_ring)); let ebpf_logger: Arc>>> = diff --git a/crates/cardwire-daemon/src/daemon.rs b/crates/cardwire-daemon/src/daemon.rs index 27e9ca6a..bc784abf 100644 --- a/crates/cardwire-daemon/src/daemon.rs +++ b/crates/cardwire-daemon/src/daemon.rs @@ -120,6 +120,12 @@ async fn spawn_dbus_api( .interface::<_, crate::interface::LoggerInterface>(path) .await?; daemon.logger_signal = Some(logger_ref.signal_emitter().clone()); + + // Cardwire Smart Policy + object_server + .at(path, daemon.smart_policy_interface.clone()) + .await?; + drop(power_tasks); // drop gpu list to prevent deadlock drop(gpu_interfaces); diff --git a/crates/cardwire-daemon/src/interface/mod.rs b/crates/cardwire-daemon/src/interface/mod.rs index 91a28347..d12d5a34 100644 --- a/crates/cardwire-daemon/src/interface/mod.rs +++ b/crates/cardwire-daemon/src/interface/mod.rs @@ -3,6 +3,7 @@ mod debug; mod gpu; mod logger; mod mode; +mod smart; mod switcheroo; pub use config::{ConfigInterface, ConfigMemory}; @@ -10,4 +11,5 @@ pub use debug::DebugInterface; pub use gpu::{GpuInterface, GpuInterfaceSignals}; pub use logger::{LogEntry, LoggerInterface, LoggerInterfaceSignals}; pub use mode::{ModeInterface, Modes}; +pub use smart::SmartPolicyInterface; pub use switcheroo::SwitcherooInterface; diff --git a/crates/cardwire-daemon/src/interface/smart.rs b/crates/cardwire-daemon/src/interface/smart.rs new file mode 100644 index 00000000..73f4acb5 --- /dev/null +++ b/crates/cardwire-daemon/src/interface/smart.rs @@ -0,0 +1,136 @@ +use aya::maps::{HashMap as AyaHashMap, MapError as AyaMapError}; +use cardwire_ebpf_userspace::EbpfBlocker; +use std::{path::Path, sync::Arc}; + +use tokio::sync::RwLock; +use zbus::{ + fdo::{self, Error::Failed}, interface +}; + +#[derive(Clone, Debug)] +pub struct SmartPolicyInterface { + pid_map: Arc>>, + forced_map: Arc>>, +} + +impl SmartPolicyInterface { + pub fn build(blocker: &mut EbpfBlocker) -> Self { + let pid_map = Arc::clone(&blocker.pid_map); + let forced_map = Arc::clone(&blocker.forced_map); + + Self { + pid_map, + forced_map, + } + } +} + +#[interface(name = "org.opengamingcollective.cardwire.SmartPolicy")] +impl SmartPolicyInterface { + /// Authorized a pid to access a specific GPU + pub async fn request_process_access( + &self, + pid: u32, + policy: String, + value: u32, + ) -> Result<(), fdo::Error> { + // Check if the process exists, leave if it doesnt + if !Path::new(&format!("/proc/{}", pid)).exists() { + return Err(fdo::Error::Failed("process doesn't exist".to_string())); + } + // Match the policy and add the pid to the corresponding ebpf map + match policy.as_str() { + // Default, do nothing + "Default" => Ok(()), + // Equivalent to CARDWIRE_ALLOW=1, show both iGPU and dGPU + "Allow_dGPU" => { + { + // First remove the PID from the other map if present + let mut forced_map = self.forced_map.write().await; + forced_map + .remove(&pid) + .map_err(|err| fdo::Error::Failed(err.to_string()))?; + } + let mut pid_map = self.pid_map.write().await; + pid_map + .insert(pid, 0, 0) + .map_err(|err| fdo::Error::Failed(err.to_string())) + } + // Equivalent to CARDWIRE_FORCE_DGPU=value + "Force_dGPU" => { + { + // First remove the PID from the other map if present + let mut pid_map = self.pid_map.write().await; + pid_map + .remove(&pid) + .map_err(|err| fdo::Error::Failed(err.to_string()))?; + } + let mut force_map = self.forced_map.write().await; + force_map + .insert(pid, value, 0) + .map_err(|err| Failed(err.to_string())) + } + // Equivalent to CARDWIRE_FORCE_GPU=value + "Force_GPU" => { + { + // First remove the PID from the other map if present + let mut pid_map = self.pid_map.write().await; + pid_map + .remove(&pid) + .map_err(|err| fdo::Error::Failed(err.to_string()))?; + } + let mut force_map = self.forced_map.write().await; + force_map + .insert(pid, value, 0) + .map_err(|err| Failed(err.to_string())) + } + _ => Err(fdo::Error::InvalidArgs(format!("invalid arg: {}", policy))), + } + } + + /// Check if the process is inside PID or FORCED map, and return the map type with the gpu_id + /// associed + pub async fn get_process_status(&self, pid: u32) -> Result<(String, Option), fdo::Error> { + let mut status = String::new(); + let mut gpu_id: Option = None; + + { + let pid_map = self.pid_map.read().await; + match pid_map.get(&pid, 0) { + Ok(id) => { + status = "Allowed".to_string(); + gpu_id = Some(id) + } + Err(err) => match err { + AyaMapError::KeyNotFound => {} + _ => { + return Err(fdo::Error::Failed(format!( + "Couldn't read PID MAP: {}", + err + ))); + } + }, + } + } + { + let forced_map = self.forced_map.read().await; + match forced_map.get(&pid, 0) { + Ok(id) => { + status = "Forced".to_string(); + gpu_id = Some(id) + } + Err(err) => match err { + AyaMapError::KeyNotFound => {} + _ => { + return Err(fdo::Error::Failed(format!( + "Couldn't read FORCED MAP: {}", + err + ))); + } + }, + } + } + + Ok((status, gpu_id)) + } +} diff --git a/crates/cardwire-daemon/src/models.rs b/crates/cardwire-daemon/src/models.rs index b674242c..ace81472 100644 --- a/crates/cardwire-daemon/src/models.rs +++ b/crates/cardwire-daemon/src/models.rs @@ -3,7 +3,7 @@ use crate::{ analyzer::CardwireAnalyzer, core::{ gpu::{GpuEnumerator, GpuVendor}, inode::exp_nvidia_inodes, pci::{self} }, file::{CardwireConfig, CardwireGpuState, CardwireModeState}, interface::{ - ConfigInterface, ConfigMemory, DebugInterface, GpuInterface, LoggerInterface, ModeInterface, Modes, SwitcherooInterface + ConfigInterface, ConfigMemory, DebugInterface, GpuInterface, LoggerInterface, ModeInterface, Modes, SmartPolicyInterface, SwitcherooInterface }, tasks }; use anyhow::{Context, Result}; @@ -35,6 +35,7 @@ pub struct DaemonManager { pub switcheroo_interface: SwitcherooInterface, pub logger_interface: LoggerInterface, pub logger_signal: Option>, + pub smart_policy_interface: SmartPolicyInterface, pub inner: DaemonInner, } @@ -59,7 +60,11 @@ impl DaemonManager { let pci_list: Arc>> = Arc::new(RwLock::new(pci_devices)); - let blocker = Arc::new(RwLock::new(EbpfBlocker::new()?)); + let mut blocker = EbpfBlocker::new()?; + + let smart_policy_interface = SmartPolicyInterface::build(&mut blocker); + + let blocker = Arc::new(RwLock::new(blocker)); let power_tasks = Arc::new(RwLock::new(BTreeMap::new())); @@ -112,6 +117,7 @@ impl DaemonManager { switcheroo_interface: SwitcherooInterface::build(Arc::clone(&gpu_interfaces)), logger_interface, logger_signal: None, + smart_policy_interface, inner: DaemonInner { mode_state: Arc::clone(&mode_state), gpu_state: Arc::clone(&gpu_state), diff --git a/crates/cardwire-ebpf-userspace/src/lib.rs b/crates/cardwire-ebpf-userspace/src/lib.rs index 07d603d9..68f89283 100644 --- a/crates/cardwire-ebpf-userspace/src/lib.rs +++ b/crates/cardwire-ebpf-userspace/src/lib.rs @@ -1,7 +1,7 @@ //! main lib code of cardwire-ebpf mod errors; -use std::{fs, path::Path}; +use std::{fs, path::Path, sync::Arc}; pub use crate::errors::{CardwireEbpfError, CardwireEbpfResult}; use aya::{ @@ -9,7 +9,9 @@ use aya::{ }; use aya_log::EbpfLogger; use log::{Log, error, info, warn}; -use tokio::io::{Interest, unix::AsyncFd}; +use tokio::{ + io::{Interest, unix::AsyncFd}, sync::RwLock +}; pub enum EbpfSettings { ExperimentalNvidia, @@ -17,6 +19,8 @@ pub enum EbpfSettings { pub struct EbpfBlocker { ebpf: Ebpf, + pub pid_map: Arc>>, + pub forced_map: Arc>>, } impl EbpfBlocker { @@ -131,7 +135,18 @@ impl EbpfBlocker { } }; } - Ok(Self { ebpf }) + + let pid_map = Self::get_pid_map(&mut ebpf)?; + let forced_map = Self::get_forced_pid_map(&mut ebpf)?; + + let pid_map = Arc::new(RwLock::new(pid_map)); + let forced_map = Arc::new(RwLock::new(forced_map)); + + Ok(Self { + ebpf, + pid_map, + forced_map, + }) } /// whitelist cardwire's pid to prevent self-locking in ebpf @@ -322,9 +337,11 @@ impl EbpfBlocker { } /// take the CW_ALLOWED_PID HashMap map from the blocker - pub fn get_pid_map(&mut self) -> CardwireEbpfResult> { + pub fn get_pid_map( + ebpf: &mut Ebpf, + ) -> CardwireEbpfResult> { let map_str = "CW_ALLOWED_PID"; - let map = match self.ebpf.take_map(map_str) { + let map = match ebpf.take_map(map_str) { Some(map) => map, None => { error!("error while trying to take map {}", map_str); @@ -345,10 +362,10 @@ impl EbpfBlocker { /// take the CW_FORCED_PID HashMap map from the blocker pub fn get_forced_pid_map( - &mut self, + ebpf: &mut Ebpf, ) -> CardwireEbpfResult> { let map_str = "CW_FORCED_PID"; - let map = match self.ebpf.take_map(map_str) { + let map = match ebpf.take_map(map_str) { Some(map) => map, None => { error!("error while trying to take map {}", map_str);