Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 31 additions & 39 deletions crates/cardwire-daemon/src/core/inode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use std::{
use anyhow::Result;
use log::{error, warn};

use cardwire_ebpf_userspace::InodeKey;

use crate::core::pci::PciDevice;

pub fn get_inodes(
Expand All @@ -15,8 +17,8 @@ pub fn get_inodes(
parent_pci: &Option<String>,
pci_list: &BTreeMap<String, PciDevice>,
nvidia_minor: Option<u32>,
) -> Result<Vec<u64>> {
let mut total_inodes: Vec<u64> = Vec::new();
) -> Result<Vec<InodeKey>> {
let mut total_inodes: Vec<InodeKey> = Vec::new();

match card_to_inode(card) {
Ok(inode_res) => total_inodes.push(inode_res),
Expand Down Expand Up @@ -84,48 +86,44 @@ pub fn get_inodes(
Ok(total_inodes)
}

pub fn render_to_inode(render: u32) -> Result<u64> {
pub fn render_to_inode(render: u32) -> Result<InodeKey> {
let render_path = format!("/dev/dri/renderD{}", render);
let metadata = fs::metadata(&render_path).map_err(|e| {
warn!("failed to get inode for {}: {}", render_path, e);
e
})?;
let inode = metadata.ino();

Ok(inode)
Ok(InodeKey::new(metadata.dev(), metadata.ino()))
}

pub fn card_to_inode(card: u32) -> Result<u64> {
pub fn card_to_inode(card: u32) -> Result<InodeKey> {
let card_path = format!("/dev/dri/card{}", card);
let metadata = fs::metadata(&card_path).map_err(|e| {
warn!("failed to get inode for {}: {}", card_path, e);
e
})?;
let inode = metadata.ino();

Ok(inode)
Ok(InodeKey::new(metadata.dev(), metadata.ino()))
}

// Here return a list of inode that contain the pci card, the audio card and their parents
pub fn pci_to_inode(
pci: String,
parent_pci: &Option<String>,
pci_list: &BTreeMap<String, PciDevice>,
) -> Result<Vec<u64>> {
let mut inodes: Vec<u64> = Vec::new();
) -> Result<Vec<InodeKey>> {
let mut inodes: Vec<InodeKey> = Vec::new();

// quick function that push the inodes into the vec
let push_pci_inode = |pci: &str, inodes: &mut Vec<u64>| {
let push_pci_inode = |pci: &str, inodes: &mut Vec<InodeKey>| {
// First get the link ino
let pci_path = format!("/sys/bus/pci/devices/{}", pci);
if let Ok(metadata) = fs::metadata(&pci_path) {
inodes.push(metadata.ino());
inodes.push(InodeKey::new(metadata.dev(), metadata.ino()));
}

// Now without following link
let pci_path = format!("/sys/bus/pci/devices/{}", pci);
if let Ok(metadata) = fs::symlink_metadata(&pci_path) {
inodes.push(metadata.ino());
inodes.push(InodeKey::new(metadata.dev(), metadata.ino()));
}
};

Expand All @@ -149,47 +147,41 @@ pub fn pci_to_inode(
}

/// Used to verify the block status of a single pci
pub fn single_pci_to_inode(pci: &str) -> Result<u64> {
pub fn single_pci_to_inode(pci: &str) -> Result<InodeKey> {
let pci_path = format!("/sys/bus/pci/devices/{}", pci);
let metadata = fs::metadata(&pci_path).map_err(|e| {
warn!("failed to get inode for {}: {}", pci_path, e);
e
})?;
let inode = metadata.ino();

Ok(inode)
Ok(InodeKey::new(metadata.dev(), metadata.ino()))
}

pub fn nvidia_to_inode(nvidia_minor: u32) -> Result<u64> {
pub fn nvidia_to_inode(nvidia_minor: u32) -> Result<InodeKey> {
let nvidia_path = format!("/dev/nvidia{}", nvidia_minor);
let metadata = fs::metadata(&nvidia_path).map_err(|e| {
warn!("failed to get inode for {}: {}", nvidia_path, e);
e
})?;
let inode = metadata.ino();

Ok(inode)
Ok(InodeKey::new(metadata.dev(), metadata.ino()))
}

/// The only gpu vendor that need it's backlight to be blocked is nvidia
pub fn backlight_to_inode(nvidia_minor: u32) -> Result<u64> {
pub fn backlight_to_inode(nvidia_minor: u32) -> Result<InodeKey> {
let nvidia_path = format!("/sys/class/backlight/nvidia_{}", nvidia_minor);
let metadata = fs::metadata(&nvidia_path).map_err(|e| {
warn!("failed to get inode for {}: {}", nvidia_path, e);
e
})?;
let inode = metadata.ino();

Ok(inode)
Ok(InodeKey::new(metadata.dev(), metadata.ino()))
}

pub fn exp_nvidia_inodes() -> Result<Vec<u64>> {
let mut inodes: Vec<u64> = Vec::new();
pub fn exp_nvidia_inodes() -> Result<Vec<InodeKey>> {
let mut inodes: Vec<InodeKey> = Vec::new();

// Get nvidiactl inode
let nvidiactl = "/dev/nvidiactl";
if let Ok(metadata) = fs::metadata(nvidiactl) {
inodes.push(metadata.ino());
inodes.push(InodeKey::new(metadata.dev(), metadata.ino()));
}

// Now try to find the vulkan file
Expand Down Expand Up @@ -218,16 +210,16 @@ pub fn exp_nvidia_inodes() -> Result<Vec<u64>> {
&& let Ok(metadata) = fs::metadata(entry.path())
&& metadata.is_file()
{
inodes.push(metadata.ino());
inodes.push(InodeKey::new(metadata.dev(), metadata.ino()));
}
}
}

Ok(inodes)
}

pub fn sys_drm_inodes(render: u32, card: u32) -> Result<Vec<u64>> {
let mut inodes = Vec::new();
pub fn sys_drm_inodes(render: u32, card: u32) -> Result<Vec<InodeKey>> {
let mut inodes: Vec<InodeKey> = Vec::new();
let sys_path = Path::new("/sys/class/drm");

let card = format!("card{}", card);
Expand All @@ -240,24 +232,24 @@ pub fn sys_drm_inodes(render: u32, card: u32) -> Result<Vec<u64>> {
// we matched with the blocked device, get the inodes without following the link
let inode_res = fs::symlink_metadata(entry.path());
if let Ok(meta) = inode_res {
inodes.push(meta.ino());
inodes.push(InodeKey::new(meta.dev(), meta.ino()));
}
}
}

Ok(inodes)
}

pub fn sys_hwmon(pci: &str) -> Result<Vec<u64>> {
let mut inodes = Vec::new();
pub fn sys_hwmon(pci: &str) -> Result<Vec<InodeKey>> {
let mut inodes: Vec<InodeKey> = Vec::new();
let sysfs_pci_path = format!("/sys/bus/pci/devices/{}/hwmon", pci);
let sysfs_pci_path = Path::new(&sysfs_pci_path);

for entry in fs::read_dir(sysfs_pci_path)? {
let entry = entry?;
// First add hwmon from the sysfs pci folder
if let Ok(meta) = fs::metadata(entry.path()) {
inodes.push(meta.ino());
inodes.push(InodeKey::new(meta.dev(), meta.ino()));
}
// Then add from /sys/class/hwmon
if let Ok(hwmon_entry) = entry.file_name().into_string() {
Expand All @@ -268,10 +260,10 @@ pub fn sys_hwmon(pci: &str) -> Result<Vec<u64>> {
continue;
}
if let Ok(meta) = fs::metadata(hwmon_path) {
inodes.push(meta.ino());
inodes.push(InodeKey::new(meta.dev(), meta.ino()));
}
if let Ok(meta) = fs::symlink_metadata(hwmon_path) {
inodes.push(meta.ino());
inodes.push(InodeKey::new(meta.dev(), meta.ino()));
}
}
}
Expand Down
78 changes: 73 additions & 5 deletions crates/cardwire-daemon/src/interface/debug.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::{
core::{
env::compute_switcheroo_env, gpu::GpuEnumerator, pci::{self, DbusPciDevice, PciDevice}
env::compute_switcheroo_env, gpu::{GpuEnumerator, GpuVendor}, inode::exp_nvidia_inodes, pci::{self, DbusPciDevice, PciDevice}
}, interface::SwitcherooInterface, tasks::watch_power_state
};
use cardwire_ebpf_userspace::EbpfBlocker;
use log::{info, warn};
use std::{collections::BTreeMap, sync::Arc};
use anyhow::Context;
use cardwire_ebpf_userspace::{EbpfBlocker, InodeKey};
use log::{error, info, warn};
use std::{
collections::{BTreeMap, HashSet}, sync::Arc
};
use tokio::{sync::RwLock, task};
use zbus::{fdo, interface};

Expand Down Expand Up @@ -46,6 +49,61 @@ impl DebugInterface {
switcheroo,
})
}

/// Reconcile CW_EXP_BLK_INO with the nvidia files currently on disk
///
/// Missing inodes only warn, the map keeps what it holds. A failed map
/// write is returned: the block would be advertised but not enforced
pub async fn sync_nvidia_inodes(&self) -> anyhow::Result<()> {
let target = {
let gpu_list = self.gpu_list.read().await;
gpu_list
.iter()
.find(|(_, gpu)| {
gpu.device.gpu_vendor() == GpuVendor::Nvidia && !gpu.device.is_default()
})
.map(|(id, _)| *id as u32)
};

let inodes = match target {
Some(_) => match exp_nvidia_inodes() {
Ok(inodes) => inodes,
Err(err) => {
warn!(
"failed to read nvidia inodes, leaving the map as is: {:#}",
err
);
return Ok(());
}
},
None => Vec::new(),
};

let mut blocker = self.blocker.write().await;
match target {
Some(gpu_id) => blocker.sync_exp_inodes(inodes, gpu_id),
None => blocker.clear_exp_inodes(),
}
.context("failed to write the CW_EXP_BLK_INO map")
}

async fn drop_unclaimed_inodes(&self, previous: Vec<InodeKey>) {
let claimed: HashSet<InodeKey> = {
let gpu_interfaces = self.gpu_list.read().await;
let mut claimed = HashSet::new();
for gpu in gpu_interfaces.values() {
claimed.extend(gpu.pushed_inodes().await);
}
claimed
};

let mut blocker = self.blocker.write().await;
for stale in previous.iter().filter(|key| !claimed.contains(key)) {
if let Err(err) = blocker.remove_inode(*stale) {
warn!("failed to drop stale inode {:?}: {}", stale, err);
}
}
}
}

#[interface(name = "org.opengamingcollective.cardwire.Debug")]
Expand Down Expand Up @@ -76,13 +134,15 @@ impl DebugInterface {
let mut power_tasks = self.power_tasks.write().await;

// get rid of the old gpu api and the old tasks
for id in gpu_interfaces.keys() {
let mut previous_inodes: Vec<InodeKey> = Vec::new();
for (id, gpu) in gpu_interfaces.iter() {
let path = format!("/org/opengamingcollective/cardwire/Gpu/{}", id);
let _ = object_server.remove::<GpuInterface, &str>(&path).await;
// if task is present, abort
if let Some(handle) = power_tasks.remove(id) {
handle.abort();
}
previous_inodes.extend(gpu.take_pushed_inodes().await);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Empty the current gpu_interfaces
Expand Down Expand Up @@ -165,6 +225,14 @@ impl DebugInterface {
warn!("failed to fall back to hybrid mode on hotplug: {fb}");
}
}

self.drop_unclaimed_inodes(previous_inodes).await;
if let Err(err) = self.sync_nvidia_inodes().await {
error!(
"nvidia block is out of date after the gpu refresh: {:#}",
err
);
}
self.switcheroo.emit_gpu_list_changed().await;
}

Expand Down
Loading
Loading