use std::ffi::CStr;
use std::sync::Arc;
use rdma_io_sys::ibverbs::*;
use rdma_io_sys::wrapper::rdma_wrap____ibv_query_port;
use crate::error::{from_ptr, from_ret};
use crate::{Error, Result};
#[derive(Debug)]
pub struct Device {
list: Arc<DeviceList>,
index: usize,
}
#[derive(Debug)]
struct DeviceList {
list: *mut *mut ibv_device,
#[allow(dead_code)]
count: usize,
}
unsafe impl Send for DeviceList {}
unsafe impl Sync for DeviceList {}
impl Drop for DeviceList {
fn drop(&mut self) {
unsafe { ibv_free_device_list(self.list) };
}
}
pub fn devices() -> Result<Vec<Device>> {
let mut num_devices: i32 = 0;
let list = unsafe { ibv_get_device_list(&mut num_devices) };
if list.is_null() {
return Err(Error::Verbs(std::io::Error::last_os_error()));
}
let count = num_devices as usize;
let shared = Arc::new(DeviceList { list, count });
let devs = (0..count)
.map(|i| Device {
list: Arc::clone(&shared),
index: i,
})
.collect();
Ok(devs)
}
impl Device {
pub fn name(&self) -> &str {
let dev = self.as_ptr();
let name = unsafe { ibv_get_device_name(dev) };
if name.is_null() {
"<unknown>"
} else {
unsafe { CStr::from_ptr(name) }
.to_str()
.unwrap_or("<invalid utf8>")
}
}
pub fn guid(&self) -> u64 {
unsafe { ibv_get_device_guid(self.as_ptr()) }
}
pub fn transport_type(&self) -> ibv_transport_type {
unsafe { (*self.as_ptr()).transport_type }
}
pub fn is_iwarp(&self) -> bool {
self.transport_type() == IBV_TRANSPORT_IWARP
}
pub fn open(&self) -> Result<Context> {
let ctx = from_ptr(unsafe { ibv_open_device(self.as_ptr()) })?;
Ok(Context {
inner: ctx,
owned: true,
})
}
fn as_ptr(&self) -> *mut ibv_device {
unsafe { *self.list.list.add(self.index) }
}
}
pub struct Context {
pub(crate) inner: *mut ibv_context,
owned: bool,
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
impl Drop for Context {
fn drop(&mut self) {
if self.owned {
let ret = unsafe { ibv_close_device(self.inner) };
if ret != 0 {
tracing::error!(
"ibv_close_device failed: {}",
std::io::Error::from_raw_os_error(-ret)
);
}
}
}
}
impl Context {
pub unsafe fn from_raw(ctx: *mut ibv_context, owned: bool) -> Self {
Self { inner: ctx, owned }
}
pub fn query_device(&self) -> Result<ibv_device_attr> {
let mut attr = ibv_device_attr::default();
from_ret(unsafe { ibv_query_device(self.inner, &mut attr) })?;
Ok(attr)
}
pub fn query_port(&self, port_num: u8) -> Result<ibv_port_attr> {
let mut attr = ibv_port_attr::default();
from_ret(unsafe { rdma_wrap____ibv_query_port(self.inner, port_num, &mut attr) })?;
Ok(attr)
}
pub fn query_gid(&self, port_num: u8, index: i32) -> Result<ibv_gid> {
let mut gid = ibv_gid::default();
from_ret(unsafe { ibv_query_gid(self.inner, port_num, index, &mut gid) })?;
Ok(gid)
}
pub fn as_raw(&self) -> *mut ibv_context {
self.inner
}
}
pub fn open_first_device() -> Result<Context> {
let devs = devices()?;
if devs.is_empty() {
return Err(Error::NoDevices);
}
devs[0].open()
}
pub fn any_device_is_iwarp() -> bool {
devices()
.ok()
.map(|d| d.iter().any(|dev| dev.is_iwarp()))
.unwrap_or(false)
}
pub fn supports_mw_type2(pd: &Arc<crate::pd::ProtectionDomain>) -> bool {
let attr = match pd.context().query_device() {
Ok(attr) => attr,
Err(_) => return false,
};
let flags = attr.device_cap_flags;
flags
& (rdma_io_sys::ibverbs::IBV_DEVICE_MEM_WINDOW_TYPE_2A
| rdma_io_sys::ibverbs::IBV_DEVICE_MEM_WINDOW_TYPE_2B)
!= 0
}
pub fn open_device_by_name(name: &str) -> Result<Context> {
let devs = devices()?;
for d in &devs {
if d.name() == name {
return d.open();
}
}
Err(Error::DeviceNotFound(name.to_string()))
}