use std::io::{self, Error as IoError};
use std::ptr::NonNull;
use std::sync::Arc;
use std::{fmt, mem};
use crate::bindings::*;
#[cfg(mlnx4)]
use crate::rdma::dct::Dct;
use crate::rdma::{gid::Gid, pd::Pd, qp::Qp, type_alias::*};
use crate::utils::interop::from_c_ret;
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct QpEndpoint {
pub gid: Option<Gid>,
pub lid: Lid,
pub port_num: PortNum,
pub num: Qpn,
}
impl QpEndpoint {
pub fn of_qp(qp: &Qp) -> Option<Self> {
let (port, gid_idx) = qp.port()?;
if qp.use_global_routing() {
let gid = port.gids()[*gid_idx as usize];
Some(Self {
gid: Some(gid.gid),
port_num: port.num(),
lid: port.lid(),
num: qp.qp_num(),
})
} else {
Some(Self {
gid: None,
port_num: port.num(),
lid: port.lid(),
num: qp.qp_num(),
})
}
}
#[cfg(mlnx4)]
pub fn of_dct(dct: &Dct) -> Self {
let init_attr = dct.init_attr();
let gid = init_attr.port.gids()[init_attr.gid_index as usize];
Self {
gid: Some(gid.gid),
port_num: init_attr.port.num(),
lid: init_attr.port.lid(),
num: dct.dct_num(),
}
}
pub fn new(gid: Option<Gid>, lid: Lid, port_num: PortNum, num: Qpn) -> Self {
Self {
gid,
lid,
port_num,
num,
}
}
pub fn is_global(&self) -> bool {
self.gid.is_some()
}
pub fn as_local(self) -> Self {
Self { gid: None, ..self }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct IbvAh(Option<NonNull<ibv_ah>>);
impl IbvAh {
pub unsafe fn destroy(self) -> io::Result<()> {
let ret = ibv_destroy_ah(self.as_ptr());
from_c_ret(ret)
}
}
impl_ibv_wrapper_traits!(ibv_ah, IbvAh);
struct QpPeerInner {
_pd: Pd,
ah: IbvAh,
ep: QpEndpoint,
}
impl Drop for QpPeerInner {
fn drop(&mut self) {
unsafe { self.ah.destroy() }.expect("cannot destroy AH on drop");
}
}
#[derive(Clone)]
pub struct QpPeer {
ah: IbvAh,
num: Qpn,
inner: Arc<QpPeerInner>,
}
impl fmt::Debug for QpPeer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("QpPeer")
.field("endpoint", &self.inner.ep)
.finish()
}
}
impl QpPeer {
pub(crate) fn new(pd: &Pd, sgid_index: GidIndex, ep: QpEndpoint) -> io::Result<Self> {
let mut ah_attr = ibv_ah_attr {
dlid: ep.lid,
sl: 0,
src_path_bits: 0,
static_rate: 0,
port_num: ep.port_num,
..unsafe { mem::zeroed() }
};
if ep.is_global() {
ah_attr = ibv_ah_attr {
grh: ibv_global_route {
dgid: ep.gid.unwrap().into(),
flow_label: 0,
sgid_index,
hop_limit: 0xFF,
traffic_class: 0,
},
is_global: 1,
..ah_attr
};
}
let ah = unsafe { ibv_create_ah(pd.as_raw(), &mut ah_attr) };
let ah = NonNull::new(ah).ok_or_else(IoError::last_os_error)?;
let ah = IbvAh::from(ah);
Ok(Self {
inner: Arc::new(QpPeerInner {
_pd: pd.clone(),
ah,
ep,
}),
ah,
num: ep.num,
})
}
#[inline]
pub fn ud(&self) -> ud_t {
ud_t {
ah: self.ah.as_ptr(),
remote_qpn: self.num,
remote_qkey: Qp::GLOBAL_QKEY,
}
}
#[cfg(mlnx4)]
#[inline]
pub fn dc(&self) -> dc_t {
dc_t {
ah: self.ah.as_ptr(),
dct_number: self.num,
dct_access_key: Dct::GLOBAL_DC_KEY,
}
}
}
impl QpPeer {
#[inline]
pub fn endpoint(&self) -> &QpEndpoint {
&self.inner.ep
}
#[inline]
pub fn set_ud_peer(&self, wr: &mut ibv_send_wr) {
wr.wr.ud = self.ud();
}
}