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
88 changes: 56 additions & 32 deletions crates/cardwire-core/src/gpu/ebpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use std::collections::BTreeMap;

use crate::{errors::Error as CardwireError, gpu::models::GpuDevice, pci::PciDevice};
use cardwire_ebpf::EbpfBlocker;
use cardwire_ebpf::{BlockKind, EbpfBlocker};
use log::{info, warn};

pub struct GpuBlocker {
Expand All @@ -18,46 +18,36 @@ impl GpuBlocker {

pub fn set_nvidia_setting(&mut self, block: bool) -> Result<(), CardwireError> {
self.inner
.set_nvidia_block(block)
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?;
.block_kind(&block.to_string(), BlockKind::NvidiaSetting)?;
Ok(())
}
pub fn set_file_block(&mut self, file: &str) -> Result<(), CardwireError> {
self.inner
.set_file_block(file)
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?;
self.inner.block_kind(file, BlockKind::File)?;
Ok(())
}
pub fn set_nvidia_file_block(&mut self, file: &str) -> Result<(), CardwireError> {
self.inner
.set_nvidia_file_block(file)
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?;
self.inner.block_kind(file, BlockKind::NvidiaFile)?;
Ok(())
}
}

pub fn is_gpu_blocked(blocker: &GpuBlocker, gpu: &GpuDevice) -> Result<bool, CardwireError> {
let card_id = *gpu.card();
let render_id = *gpu.render();
// PCI -> Card -> Render -> Nvidia
Ok(blocker
.inner
.is_pci_blocked(gpu.pci.pci_address())
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?
.is_kind_blocked(gpu.pci.pci_address(), BlockKind::Pci)?
&& blocker
.inner
.is_card_blocked(card_id)
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?
.is_kind_blocked(&card_id.to_string(), BlockKind::Card)?
&& blocker
.inner
.is_render_blocked(render_id)
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?
&& if gpu.nvidia() {
// unwrap because it should be Some if it's an nvidia gpu, if not it's a bug and should
// be reported
.is_kind_blocked(&render_id.to_string(), BlockKind::Render)?
&& if let Some(minor) = gpu.nvidia_minor() {
blocker
.inner
.is_nvidia_blocked(gpu.nvidia_minor().unwrap())
.map_err(|err| CardwireError::UnknownBlockState(err.to_string()))?
.is_kind_blocked(&minor.to_string(), BlockKind::Nvidia)?
} else {
true
})
Expand All @@ -73,19 +63,40 @@ pub fn block_gpu(
let render_id = *gpu.render();

if block {
blocker.inner.block_card(card_id)?;
blocker.inner.block_render(render_id)?;
//blocker.inner.block_card(card_id)?;
// block card
blocker
.inner
.block_kind(&card_id.to_string(), BlockKind::Card)?;
// block render
blocker
.inner
.block_kind(&render_id.to_string(), BlockKind::Render)?;
// block pci
chain_block_pci(blocker, gpu, pci_list)?;
// block nvidia
if gpu.nvidia() {
blocker.inner.block_nvidia(gpu.nvidia_minor().unwrap())?
blocker
.inner
.block_kind(&gpu.nvidia_minor().unwrap().to_string(), BlockKind::Nvidia)?;
}
Ok(())
} else {
blocker.inner.unblock_card(card_id)?;
blocker.inner.unblock_render(render_id)?;
// unblock card
blocker
.inner
.unblock_kind(&card_id.to_string(), BlockKind::Card)?;
// unblock render
blocker
.inner
.unblock_kind(&render_id.to_string(), BlockKind::Render)?;
// unblock pci
chain_unblock_pci(blocker, gpu, pci_list)?;
// unblock nvidia
if gpu.nvidia() {
blocker.inner.unblock_nvidia(gpu.nvidia_minor().unwrap())?
blocker
.inner
.unblock_kind(&gpu.nvidia_minor().unwrap().to_string(), BlockKind::Nvidia)?;
}
Ok(())
}
Expand All @@ -97,12 +108,16 @@ fn chain_block_pci(
pci_list: &BTreeMap<String, PciDevice>,
) -> Result<(), CardwireError> {
// Block the gpu pci
blocker.inner.block_pci(gpu.pci.pci_address())?;
blocker
.inner
.block_kind(gpu.pci.pci_address(), BlockKind::Pci)?;
info!("blocking pci: {}", gpu.pci.pci_address());
// also block audio card
if gpu.pci.pci_address().ends_with(".0") {
let gpu_audio_adress = gpu.pci.pci_address().replace(".0", ".1");
blocker.inner.block_pci(&gpu_audio_adress)?;
blocker
.inner
.block_kind(&gpu_audio_adress, BlockKind::Pci)?;
}
// Check if gpu has a parent pci
// first pci to block
Expand All @@ -111,7 +126,9 @@ fn chain_block_pci(
while let Some(parent_pci) = current_parent {
if let Some(pci_device) = pci_list.get(&parent_pci) {
info!("chain blocking pci: {}", pci_device.pci_address());
blocker.inner.block_pci(pci_device.pci_address())?;
blocker
.inner
.block_kind(pci_device.pci_address(), BlockKind::Pci)?;
current_parent = pci_device.parent_pci().clone();
} else {
warn!("expected parent pci {} not found in pci_list", parent_pci);
Expand All @@ -127,11 +144,16 @@ fn chain_unblock_pci(
) -> Result<(), CardwireError> {
// Unblock the gpu pci
info!("unblocking pci: {}", gpu.pci.pci_address());
blocker.inner.unblock_pci(gpu.pci.pci_address())?;
blocker
.inner
.unblock_kind(gpu.pci.pci_address(), BlockKind::Pci)?;

// also unblock audio card
if gpu.pci.pci_address().ends_with(".0") {
let gpu_audio_adress = gpu.pci.pci_address().to_string().replace(".0", ".1");
blocker.inner.unblock_pci(&gpu_audio_adress)?;
blocker
.inner
.unblock_kind(&gpu_audio_adress, BlockKind::Pci)?;
}
// Check if gpu has a parent pci
// first pci to block
Expand All @@ -140,7 +162,9 @@ fn chain_unblock_pci(
while let Some(parent_pci) = current_parent {
if let Some(pci_device) = pci_list.get(&parent_pci) {
info!("chain unblocking pci: {}", pci_device.pci_address());
blocker.inner.unblock_pci(pci_device.pci_address())?;
blocker
.inner
.unblock_kind(pci_device.pci_address(), BlockKind::Pci)?;
current_parent = pci_device.parent_pci().clone();
} else {
warn!("expected parent pci {} not found in pci_list", parent_pci);
Expand Down
18 changes: 6 additions & 12 deletions crates/cardwire-daemon/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,15 @@ impl Modes {
}

pub struct DaemonState {
// these are file related
pub config: RwLock<CardwireConfig>,
pub gpu_state: RwLock<CardwireGpuState>,
pub mode_state: RwLock<CardwireModeState>,
// temp data
pub gpu_list: BTreeMap<usize, gpu::GpuDevice>,
pub ebpf_blocker: RwLock<GpuBlocker>,
// for future uses, related to vfio
pub pci_devices: BTreeMap<String, pci::PciDevice>,
pub _iommu: bool,
}
impl DaemonState {
pub async fn _iommu(&self) -> bool {
self._iommu
}
}

pub struct Daemon {
Expand All @@ -78,7 +74,6 @@ pub struct Daemon {

impl Daemon {
pub async fn new() -> Result<Self> {
let iommu: bool = pci::is_iommu_enabled();
let config = CardwireConfig::build().context("Error building config")?;
let mut gpu_state = CardwireGpuState::build().context("Error building gpu_state")?;
let mode_state = CardwireModeState::build().context("Error building mode")?;
Expand All @@ -90,17 +85,17 @@ impl Daemon {
if let Err(err) = check_default_drm_class(&mut gpu_list) {
warn!("Failed to determine default GPU: {}", err);
}
// TODO: Exit if ebpf returns an error, or try to recover from it?
// Exit if ebpf returns an error
let ebpf_blocker = GpuBlocker::new()?;
// Do not stop the program if there is no gpu, cardwire will also be usable as a pci manager
// in a near future

if !gpu_list.is_empty() && gpu_state.is_default_state() {
gpu_state
.save_state(&gpu_list, &ebpf_blocker)
.await
.context("Could not save gpu state")?;
} else if gpu_list.is_empty() {
warn!("could not detect gpus, daemon is still running for pci management usage")
// the daemon needs to be running to print out the pci list
warn!("could not detect gpus, daemon is still running for debugging")
}

Ok(Self {
Expand All @@ -109,7 +104,6 @@ impl Daemon {
gpu_state: RwLock::new(gpu_state),
mode_state: RwLock::new(mode_state),
pci_devices,
_iommu: iommu,
gpu_list,
ebpf_blocker: RwLock::new(ebpf_blocker),
},
Expand Down
18 changes: 15 additions & 3 deletions crates/cardwire-ebpf/src/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ struct qstr {
const unsigned char *name;
} __attribute__((preserve_access_index));

struct dentry {
struct dentry___old {
struct qstr d_name;
struct dentry *d_parent;
struct inode *d_inode;
Expand All @@ -45,6 +45,13 @@ struct dentry {
} d_u;
} __attribute__((preserve_access_index));

struct dentry {
struct qstr d_name;
struct dentry *d_parent;
struct inode *d_inode;
struct hlist_node d_alias;
} __attribute__((preserve_access_index));

struct path {
struct dentry *dentry;
} __attribute__((preserve_access_index));
Expand Down Expand Up @@ -250,8 +257,13 @@ int BPF_PROG(inode_permission, struct inode *inode, int mask)
return 0;
}

unsigned long offset =
bpf_core_field_offset(struct dentry, d_u.d_alias);
unsigned long offset;
if (bpf_core_field_exists(((struct dentry___old *)0)->d_u.d_alias)) {
offset =
bpf_core_field_offset(struct dentry___old, d_u.d_alias);
} else {
offset = bpf_core_field_offset(struct dentry, d_alias);
}
struct dentry *d = (struct dentry *)((void *)first - offset);
//
return is_blocked_device(d);
Expand Down
22 changes: 15 additions & 7 deletions crates/cardwire-ebpf/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,30 @@ pub enum CardwireEbpfError {
Io(#[from] io::Error),
#[error("couldn't load ebpf: {0}")]
EbpfLoadError(String),
#[error("missing {kind}: {name}")]
MissingEntity { kind: String, name: String },
#[error("aya error: {0}")]
#[error("missing lsm: {name}")]
MissingLsm { name: String },
#[error("missing map: {name}")]
MissingMap { name: String },
// for block/unblock, used if passed String is not in a pci format for example
#[error("wrong format, expected {kind} got: {input}")]
WrongFormat { kind: String, input: String },
#[error("{0}")]
Aya(String),
#[error("{0}")]
Other(String),
}

impl CardwireEbpfError {
pub fn missing_entity(kind: &str, name: &str) -> Self {
Self::MissingEntity {
kind: kind.to_string(),
pub fn missing_lsm(name: &str) -> Self {
Self::MissingLsm {
name: name.to_string(),
}
}
pub fn missing_map(name: &str) -> Self {
Self::MissingMap {
name: name.to_string(),
}
}

pub fn aya<E: fmt::Display>(err: E) -> Self {
Self::Aya(err.to_string())
}
Expand Down
Loading