use crate::Device;
use crate::enum_wrappers::{
bool_from_state,
nv_link::{Capability, ErrorCounter},
state_from_bool,
};
use crate::enums::nv_link::Counter;
use crate::error::{nvml_sym, nvml_try, NvmlError};
use crate::ffi::bindings::*;
use crate::struct_wrappers::{device::PciInfo, nv_link::UtilizationControl};
use crate::structs::nv_link::UtilizationCounter;
use std::{
convert::TryFrom,
mem,
os::raw::{c_uint, c_ulonglong},
};
use static_assertions::assert_impl_all;
#[derive(Debug)]
pub struct NvLink<'device, 'nvml: 'device> {
pub(crate) device: &'device Device<'nvml>,
pub(crate) link: c_uint,
}
assert_impl_all!(NvLink: Send, Sync);
impl<'device, 'nvml: 'device> NvLink<'device, 'nvml> {
pub fn device(&self) -> &Device {
self.device
}
pub fn link(&self) -> u32 {
self.link
}
pub fn is_active(&self) -> Result<bool, NvmlError> {
let sym = nvml_sym(self.device.nvml().lib.nvmlDeviceGetNvLinkState.as_ref())?;
unsafe {
let mut state: nvmlEnableState_t = mem::zeroed();
nvml_try(sym(self.device.handle(), self.link, &mut state))?;
bool_from_state(state)
}
}
pub fn version(&self) -> Result<u32, NvmlError> {
let sym = nvml_sym(self.device.nvml().lib.nvmlDeviceGetNvLinkVersion.as_ref())?;
unsafe {
let mut version: c_uint = mem::zeroed();
nvml_try(sym(self.device.handle(), self.link, &mut version))?;
Ok(version)
}
}
pub fn has_capability(&self, cap_type: Capability) -> Result<bool, NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkCapability
.as_ref(),
)?;
unsafe {
let mut capability: c_uint = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
cap_type.as_c(),
&mut capability,
))?;
#[allow(clippy::match_like_matches_macro)]
Ok(match capability {
0 => false,
_ => true,
})
}
}
pub fn remote_pci_info(&self) -> Result<PciInfo, NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkRemotePciInfo_v2
.as_ref(),
)?;
unsafe {
let mut pci_info: nvmlPciInfo_t = mem::zeroed();
nvml_try(sym(self.device.handle(), self.link, &mut pci_info))?;
PciInfo::try_from(pci_info, false)
}
}
pub fn error_counter(&self, counter: ErrorCounter) -> Result<u64, NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkErrorCounter
.as_ref(),
)?;
unsafe {
let mut value: c_ulonglong = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
counter.as_c(),
&mut value,
))?;
Ok(value)
}
}
pub fn reset_error_counters(&mut self) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceResetNvLinkErrorCounters
.as_ref(),
)?;
unsafe { nvml_try(sym(self.device.handle(), self.link)) }
}
pub fn set_utilization_control(
&mut self,
counter: Counter,
settings: UtilizationControl,
reset_counters: bool,
) -> Result<(), NvmlError> {
let reset: c_uint = if reset_counters { 1 } else { 0 };
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceSetNvLinkUtilizationControl
.as_ref(),
)?;
unsafe {
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
&mut settings.as_c(),
reset,
))
}
}
pub fn utilization_control(&self, counter: Counter) -> Result<UtilizationControl, NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkUtilizationControl
.as_ref(),
)?;
unsafe {
let mut controls: nvmlNvLinkUtilizationControl_t = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
&mut controls,
))?;
UtilizationControl::try_from(controls)
}
}
pub fn utilization_counter(&self, counter: Counter) -> Result<UtilizationCounter, NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceGetNvLinkUtilizationCounter
.as_ref(),
)?;
unsafe {
let mut receive: c_ulonglong = mem::zeroed();
let mut send: c_ulonglong = mem::zeroed();
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
&mut receive,
&mut send,
))?;
Ok(UtilizationCounter { receive, send })
}
}
pub fn freeze_utilization_counter(&mut self, counter: Counter) -> Result<(), NvmlError> {
self.set_utilization_counter_frozen(counter, true)
}
pub fn unfreeze_utilization_counter(&mut self, counter: Counter) -> Result<(), NvmlError> {
self.set_utilization_counter_frozen(counter, false)
}
fn set_utilization_counter_frozen(
&mut self,
counter: Counter,
frozen: bool,
) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceFreezeNvLinkUtilizationCounter
.as_ref(),
)?;
unsafe {
nvml_try(sym(
self.device.handle(),
self.link,
counter as c_uint,
state_from_bool(frozen),
))
}
}
pub fn reset_utilization_counter(&mut self, counter: Counter) -> Result<(), NvmlError> {
let sym = nvml_sym(
self.device
.nvml()
.lib
.nvmlDeviceResetNvLinkUtilizationCounter
.as_ref(),
)?;
unsafe { nvml_try(sym(self.device.handle(), self.link, counter as c_uint)) }
}
}
#[cfg(test)]
#[deny(unused_mut)]
mod test {
use crate::bitmasks::nv_link::*;
use crate::enum_wrappers::nv_link::*;
use crate::enums::nv_link::*;
use crate::struct_wrappers::nv_link::*;
use crate::test_utils::*;
#[test]
#[ignore = "my machine does not support this call"]
fn is_active() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.is_active())
}
#[test]
#[ignore = "my machine does not support this call"]
fn version() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.version())
}
#[test]
#[ignore = "my machine does not support this call"]
fn has_capability() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.has_capability(Capability::P2p))
}
#[test]
#[ignore = "my machine does not support this call"]
fn remote_pci_info() {
let nvml = nvml();
test_with_link(3, &nvml, |link| {
let info = link.remote_pci_info()?;
assert_eq!(info.pci_sub_system_id, None);
Ok(info)
})
}
#[test]
#[ignore = "my machine does not support this call"]
fn error_counter() {
let nvml = nvml();
test_with_link(3, &nvml, |link| {
link.error_counter(ErrorCounter::DlRecovery)
})
}
#[allow(dead_code)]
fn reset_error_counters() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.reset_error_counters().unwrap();
}
#[allow(dead_code)]
fn set_utilization_control() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
let settings = UtilizationControl {
units: UtilizationCountUnit::Cycles,
packet_filter: PacketTypes::NO_OP
| PacketTypes::READ
| PacketTypes::WRITE
| PacketTypes::RATOM
| PacketTypes::WITH_DATA,
};
link.set_utilization_control(Counter::One, settings, false)
.unwrap()
}
#[test]
#[ignore = "my machine does not support this call"]
fn utilization_control() {
let nvml = nvml();
test_with_link(3, &nvml, |link| link.utilization_control(Counter::One))
}
#[allow(dead_code)]
fn utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let link = device.link_wrapper_for(0);
link.utilization_counter(Counter::One).unwrap();
}
#[allow(dead_code)]
fn freeze_utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.freeze_utilization_counter(Counter::One).unwrap();
}
#[allow(dead_code)]
fn unfreeze_utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.unfreeze_utilization_counter(Counter::One).unwrap();
}
#[allow(dead_code)]
fn reset_utilization_counter() {
let nvml = nvml();
let device = device(&nvml);
let mut link = device.link_wrapper_for(0);
link.reset_utilization_counter(Counter::One).unwrap();
}
}