use crate::device::DeviceList;
use crate::error_utilities::{log_last_os_err, log_ret, log_ret_last_os_err_with_note};
use crate::{
completion_queue::CompletionQueue, event_channel::EventChannel, gid::Gid,
protection_domain::ProtectionDomain,
};
use clippy_utilities::Cast;
use rdma_sys::{
ibv_close_device, ibv_context, ibv_gid, ibv_open_device, ibv_port_attr, ibv_query_gid,
};
use std::mem::MaybeUninit;
use std::{fmt::Debug, io, ptr::NonNull, sync::Arc};
pub(crate) struct Context {
inner_ctx: NonNull<ibv_context>,
inner_port_attr: ibv_port_attr,
gid: Gid,
}
impl Debug for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Context")
.field("inner_ctx", &self.inner_ctx)
.field("gid", &self.gid)
.finish()
}
}
impl Context {
pub(crate) const fn as_ptr(&self) -> *mut ibv_context {
self.inner_ctx.as_ptr()
}
pub(crate) fn open(dev_name: Option<&str>, port_num: u8, gid_index: usize) -> io::Result<Self> {
let dev_list = log_ret(
DeviceList::available(),
"This is a basic verb that shouldn't fail, check if the module ib_uverbs is loaded.",
)?;
let dev = match dev_name {
Some(name) => dev_list.iter().find(|&d| d.name() == name),
None => dev_list.get(0),
}
.ok_or(io::ErrorKind::NotFound)?;
let inner_ctx = NonNull::new(unsafe { ibv_open_device(dev.ffi_ptr()) })
.ok_or_else(|| log_ret_last_os_err_with_note("ibv_open_device failed"))?;
drop(dev_list);
let gid = {
let mut gid = MaybeUninit::<ibv_gid>::uninit();
let gid_index = gid_index.cast();
let errno =
unsafe { ibv_query_gid(inner_ctx.as_ptr(), port_num, gid_index, gid.as_mut_ptr()) };
if errno != 0_i32 {
return Err(log_ret_last_os_err_with_note("ibv_query_gid failed"));
}
Gid::from(unsafe { gid.assume_init() })
};
let mut inner_port_attr = unsafe { std::mem::zeroed() };
let errno = unsafe {
rdma_sys::___ibv_query_port(inner_ctx.as_ptr(), port_num, &mut inner_port_attr)
};
if errno != 0_i32 {
return Err(log_ret_last_os_err_with_note("ibv_query_port failed"));
}
Ok(Context {
inner_ctx,
inner_port_attr,
gid,
})
}
pub(crate) fn create_event_channel(self: &Arc<Self>) -> io::Result<EventChannel> {
EventChannel::new(Arc::<Self>::clone(self))
}
pub(crate) fn create_completion_queue(
&self,
cq_size: u32,
event_channel: EventChannel,
max_cqe: i32,
) -> io::Result<CompletionQueue> {
CompletionQueue::create(self, cq_size, event_channel, max_cqe)
}
pub(crate) fn create_protection_domain(self: &Arc<Self>) -> io::Result<ProtectionDomain> {
ProtectionDomain::create(self)
}
pub(crate) fn get_gid(&self) -> Gid {
self.gid
}
pub(crate) fn get_lid(&self) -> u16 {
self.inner_port_attr.lid
}
pub(crate) fn get_active_mtu(&self) -> u32 {
self.inner_port_attr.active_mtu
}
}
impl Drop for Context {
fn drop(&mut self) {
let errno = unsafe { ibv_close_device(self.as_ptr()) };
if errno != 0_i32 {
log_last_os_err();
}
}
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
#[cfg(test)]
#[allow(unused, clippy::unwrap_used)]
mod tests {
use crate::Context;
use std::process::Command;
fn rdma_dev() -> String {
let out = Command::new("sh")
.arg("-c")
.arg("rdma link")
.output()
.unwrap();
String::from_utf8_lossy(&out.stdout)
.clone()
.split(' ')
.nth(1)
.unwrap()
.to_owned()
.split('/')
.next()
.unwrap()
.to_owned()
}
#[test]
fn test1() {
let ctx = Context::open(Some(&rdma_dev()), 1, 0).unwrap();
}
#[test]
fn test2() {
let ctx = Context::open(Some(""), 1, 0).err().unwrap();
}
#[test]
fn test3() {
let ctx = Context::open(None, 1, 0).unwrap();
}
}