use std::fmt::Display;
use std::{hint, io, mem};
use thiserror::Error;
use crate::bindings::*;
use crate::rdma::context::IbvContext;
use crate::rdma::gid::*;
#[derive(Clone)]
pub struct Port {
num: u8,
attr: ibv_port_attr,
gids: Vec<GidTyped>,
}
unsafe impl Send for Port {}
unsafe impl Sync for Port {}
#[derive(Debug, Error)]
pub enum PortQueryError {
#[error("ibv_query_port error")]
IoError(#[from] io::Error),
#[error("GID query error")]
GidQueryError(#[from] GidQueryError),
}
impl Port {
pub(crate) fn new(ctx: IbvContext, num: u8) -> Result<Self, PortQueryError> {
let attr = {
let mut attr = unsafe { mem::zeroed() };
let ret = unsafe { ___ibv_query_port(ctx.as_ptr(), num, &mut attr) };
if ret != 0 {
eprintln!("query port error: {}", ret);
return Err(io::Error::from_raw_os_error(ret).into());
}
attr
};
let num_gids = attr.gid_tbl_len;
let mut gids = Vec::with_capacity(num_gids as usize);
for i in 0..num_gids {
match GidTyped::query(ctx, num, &attr, i as _) {
Ok(gid) => gids.push(gid),
Err(GidQueryError::AttributeQueryError) => break,
Err(e) => return Err(e.into()),
}
}
Ok(Self { num, attr, gids })
}
#[inline]
pub fn num(&self) -> u8 {
self.num
}
#[inline]
pub fn attr(&self) -> &ibv_port_attr {
&self.attr
}
#[inline]
pub fn state(&self) -> PortState {
match self.attr.state {
ibv_port_state::IBV_PORT_DOWN => PortState::Down,
ibv_port_state::IBV_PORT_INIT => PortState::Init,
ibv_port_state::IBV_PORT_ARMED => PortState::Armed,
ibv_port_state::IBV_PORT_ACTIVE => PortState::Active,
ibv_port_state::IBV_PORT_ACTIVE_DEFER => PortState::ActiveDefer,
_ => unsafe { hint::unreachable_unchecked() },
}
}
#[inline]
pub fn lid(&self) -> u16 {
self.attr.lid
}
#[inline]
pub fn link_layer(&self) -> PortLinkLayer {
match self.attr.link_layer as i32 {
IBV_LINK_LAYER_UNSPECIFIED | IBV_LINK_LAYER_INFINIBAND => PortLinkLayer::Infiniband,
IBV_LINK_LAYER_ETHERNET => PortLinkLayer::Ethernet,
_ => unsafe { hint::unreachable_unchecked() },
}
}
#[inline]
pub fn mtu(&self) -> PortMtu {
match self.attr.active_mtu {
ibv_mtu::IBV_MTU_256 => PortMtu::Mtu256,
ibv_mtu::IBV_MTU_512 => PortMtu::Mtu512,
ibv_mtu::IBV_MTU_1024 => PortMtu::Mtu1024,
ibv_mtu::IBV_MTU_2048 => PortMtu::Mtu2048,
ibv_mtu::IBV_MTU_4096 => PortMtu::Mtu4096,
_ => unsafe { hint::unreachable_unchecked() },
}
}
#[inline]
pub fn speed(&self) -> PortSpeed {
let width: u32 = match self.attr.active_width {
1 => 1,
2 => 4,
4 => 8,
8 => 12,
_ => unsafe { hint::unreachable_unchecked() },
};
let speed10x: u32 = match self.attr.active_speed {
1 => 25,
2 => 50,
4 | 8 => 100,
16 => 140,
32 => 250,
64 => 500,
_ => unsafe { hint::unreachable_unchecked() },
};
PortSpeed(width * speed10x)
}
pub fn gids(&self) -> &[GidTyped] {
&self.gids
}
pub fn recommended_gid(&self) -> (GidTyped, u8) {
use std::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct GidIndexed {
idx: u8,
gid: GidTyped,
}
impl Ord for GidIndexed {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
use std::net::Ipv6Addr;
if self.gid.ty != other.gid.ty {
return self.gid.ty.cmp(&other.gid.ty);
}
match (
Ipv6Addr::from(self.gid).to_ipv4(),
Ipv6Addr::from(other.gid).to_ipv4(),
) {
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
_ => self.idx.cmp(&other.idx),
}
}
}
impl PartialOrd for GidIndexed {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
let mut gids = self
.gids
.iter()
.enumerate()
.map(|(idx, &gid)| GidIndexed { idx: idx as _, gid })
.collect::<Vec<_>>();
gids.sort_unstable();
gids.last().map(|g| (g.gid, g.idx)).expect("no GIDs found")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortState {
Down = ibv_port_state::IBV_PORT_DOWN as _,
Init = ibv_port_state::IBV_PORT_INIT as _,
Armed = ibv_port_state::IBV_PORT_ARMED as _,
Active = ibv_port_state::IBV_PORT_ACTIVE as _,
ActiveDefer = ibv_port_state::IBV_PORT_ACTIVE_DEFER as _,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PortLinkLayer {
Infiniband,
Ethernet,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum PortMtu {
Mtu256 = ibv_mtu::IBV_MTU_256 as _,
Mtu512 = ibv_mtu::IBV_MTU_512 as _,
Mtu1024 = ibv_mtu::IBV_MTU_1024 as _,
Mtu2048 = ibv_mtu::IBV_MTU_2048 as _,
Mtu4096 = ibv_mtu::IBV_MTU_4096 as _,
}
impl PortMtu {
#[inline]
pub fn bytes(&self) -> usize {
match self {
Self::Mtu256 => 256,
Self::Mtu512 => 512,
Self::Mtu1024 => 1024,
Self::Mtu2048 => 2048,
Self::Mtu4096 => 4096,
}
}
}
impl Display for PortMtu {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}B", self.bytes())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct PortSpeed(pub(crate) u32);
impl PortSpeed {
pub const MAX_GBPS: f32 = 600.0;
#[inline]
pub fn gbps(&self) -> f32 {
self.0 as f32 / 10.0
}
}
impl Display for PortSpeed {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:.1}Gbps", self.gbps())
}
}