use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Weak};
use dma_libfabric_protocol::DmaError;
use crate::sys::{FI_ADDR_EFA, fi_addr_t, fi_av_insert, fi_av_remove, fid_av};
const MINIMUM_SWEEP: usize = 64;
const EFA_ADDRESS_LEN: usize = 32;
const EFA_IDENTITY_LEN: usize = 18;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum AddressFormat {
Efa,
Opaque,
}
impl AddressFormat {
pub(crate) fn from_fi(addr_format: u32) -> Self {
if FI_ADDR_EFA == addr_format {
Self::Efa
} else {
Self::Opaque
}
}
fn identity_len(self, bytes: &[u8]) -> Result<usize, DmaError> {
match self {
Self::Efa if EFA_ADDRESS_LEN != bytes.len() => Err(DmaError::Fabric(format!(
"efa address is {} bytes, not {EFA_ADDRESS_LEN}",
bytes.len()
))),
Self::Efa => Ok(EFA_IDENTITY_LEN),
Self::Opaque => Ok(bytes.len()),
}
}
}
#[derive(Debug)]
struct PeerAddress {
bytes: Vec<u8>,
identity_len: usize,
}
impl PeerAddress {
fn new(format: AddressFormat, bytes: &[u8]) -> Result<Self, DmaError> {
Ok(Self {
bytes: bytes.to_vec(),
identity_len: format.identity_len(bytes)?,
})
}
fn bytes(&self) -> &[u8] {
&self.bytes
}
fn identity(&self) -> &[u8] {
&self.bytes[..self.identity_len]
}
}
impl Borrow<[u8]> for PeerAddress {
fn borrow(&self) -> &[u8] {
self.identity()
}
}
impl PartialEq for PeerAddress {
fn eq(&self, other: &Self) -> bool {
self.identity() == other.identity()
}
}
impl Eq for PeerAddress {}
impl Hash for PeerAddress {
fn hash<H: Hasher>(&self, state: &mut H) {
self.identity().hash(state);
}
}
pub struct RegisteredAddress {
address_vector: *mut fid_av,
peer: fi_addr_t,
}
unsafe impl Send for RegisteredAddress {}
unsafe impl Sync for RegisteredAddress {}
impl RegisteredAddress {
pub fn handle(&self) -> fi_addr_t {
self.peer
}
}
impl std::fmt::Debug for RegisteredAddress {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("RegisteredAddress")
.field("peer", &self.peer)
.finish()
}
}
impl Drop for RegisteredAddress {
fn drop(&mut self) {
let mut address = self.peer;
let code = unsafe { fi_av_remove(self.address_vector, &raw mut address, 1, 0) };
if 0 != code {
tracing::warn!("fi_av_remove failed releasing a peer address: {code}");
}
}
}
#[derive(Debug)]
pub(crate) struct PeerAddresses {
address_vector: *mut fid_av,
format: AddressFormat,
by_address: HashMap<PeerAddress, Weak<RegisteredAddress>>,
by_client: HashMap<u64, Arc<RegisteredAddress>>,
sweep_at: usize,
}
impl PeerAddresses {
pub(crate) fn new(address_vector: *mut fid_av, format: AddressFormat) -> Self {
Self {
address_vector,
format,
by_address: HashMap::new(),
by_client: HashMap::new(),
sweep_at: MINIMUM_SWEEP,
}
}
pub(crate) fn handle(
&mut self,
client_id: u64,
address: &[u8],
) -> Result<Arc<RegisteredAddress>, DmaError> {
let identity = &address[..self.format.identity_len(address)?];
let held = self
.by_address
.get_key_value(identity)
.and_then(|(known, entry)| {
entry
.upgrade()
.map(|entry| (known.bytes() == address, entry))
});
match held {
Some((true, entry)) => {
self.by_client.insert(client_id, Arc::clone(&entry));
return Ok(entry);
}
Some((false, stale)) => {
let holders = self.by_client.len();
self.by_client.retain(|_, held| !Arc::ptr_eq(held, &stale));
let evicted = holders - self.by_client.len();
tracing::warn!(
"client {client_id} advertised a recycled id; released the stale endpoint and {evicted} clients"
);
}
None => {}
}
self.by_client.remove(&client_id);
self.by_address.remove(identity);
let key = PeerAddress::new(self.format, address)?;
let entry = Arc::new(RegisteredAddress {
address_vector: self.address_vector,
peer: self.insert(key.bytes())?,
});
self.sweep();
self.by_address.insert(key, Arc::downgrade(&entry));
self.by_client.insert(client_id, Arc::clone(&entry));
Ok(entry)
}
pub(crate) fn release(&mut self, client_id: u64) {
self.by_client.remove(&client_id);
}
pub(crate) fn clear(&mut self) {
self.by_client.clear();
self.by_address.clear();
}
fn insert(&self, address: &[u8]) -> Result<fi_addr_t, DmaError> {
let mut peer: fi_addr_t = 0;
let inserted = unsafe {
fi_av_insert(
self.address_vector,
address.as_ptr().cast(),
1,
&raw mut peer,
0,
std::ptr::null_mut(),
)
};
if 1 != inserted {
return Err(DmaError::Fabric(format!(
"fi_av_insert inserted {inserted} of 1 addresses"
)));
}
Ok(peer)
}
fn sweep(&mut self) {
if self.by_address.len() < self.sweep_at {
return;
}
self.by_address.retain(|_, entry| 0 < entry.strong_count());
self.sweep_at = MINIMUM_SWEEP.max(self.by_address.len() * 2);
}
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::{AddressFormat, EFA_ADDRESS_LEN, MINIMUM_SWEEP, PeerAddress, PeerAddresses};
use crate::configuration::{Configuration, Provider};
use crate::endpoint::LibfabricEndpoint;
use std::sync::Arc;
fn endpoint() -> LibfabricEndpoint {
let configuration = Configuration {
providers: vec![Provider::Tcp],
bind: Some("127.0.0.1".to_string()),
..Configuration::default()
};
LibfabricEndpoint::open(&configuration, Some("127.0.0.1")).expect("tcp endpoint")
}
#[test]
fn clients_sharing_an_address_share_one_entry() {
let endpoint = endpoint();
let address = endpoint.local_address().expect("local address");
let mut peers = PeerAddresses::new(endpoint.address_vector(), AddressFormat::Opaque);
let first = peers.handle(1, &address).expect("client 1");
let second = peers.handle(2, &address).expect("client 2");
assert!(Arc::ptr_eq(&first, &second), "one address, one entry");
assert_eq!(first.handle(), second.handle());
}
#[test]
fn one_client_leaving_keeps_an_entry_another_still_uses() {
let endpoint = endpoint();
let address = endpoint.local_address().expect("local address");
let mut peers = PeerAddresses::new(endpoint.address_vector(), AddressFormat::Opaque);
let shared = peers.handle(1, &address).expect("client 1");
peers.handle(2, &address).expect("client 2");
peers.release(1);
assert_eq!(
2,
Arc::strong_count(&shared),
"client 2 and this test hold it; the entry must still be in the vector"
);
}
#[test]
fn the_last_client_out_removes_the_entry() {
let endpoint = endpoint();
let address = endpoint.local_address().expect("local address");
let mut peers = PeerAddresses::new(endpoint.address_vector(), AddressFormat::Opaque);
peers.handle(1, &address).expect("client 1");
peers.handle(2, &address).expect("client 2");
peers.release(1);
peers.release(2);
assert!(peers.by_client.is_empty(), "no client holds it");
let key = PeerAddress::new(AddressFormat::Opaque, &address).expect("opaque takes anything");
assert_eq!(
0,
peers
.by_address
.get(&key)
.map_or(0, std::sync::Weak::strong_count),
"the last release must have dropped the entry, removing it from the vector"
);
}
#[test]
fn releasing_a_client_twice_is_harmless() {
let endpoint = endpoint();
let address = endpoint.local_address().expect("local address");
let mut peers = PeerAddresses::new(endpoint.address_vector(), AddressFormat::Opaque);
let shared = peers.handle(1, &address).expect("client 1");
peers.handle(2, &address).expect("client 2");
peers.release(1);
peers.release(1);
assert_eq!(
2,
Arc::strong_count(&shared),
"the repeat release must not have taken client 2's entry"
);
}
#[test]
fn re_asking_for_the_same_address_holds_one_reference() {
let endpoint = endpoint();
let address = endpoint.local_address().expect("local address");
let mut peers = PeerAddresses::new(endpoint.address_vector(), AddressFormat::Opaque);
let first = peers.handle(1, &address).expect("first ask");
let again = peers.handle(1, &address).expect("second ask");
assert!(Arc::ptr_eq(&first, &again));
assert_eq!(3, Arc::strong_count(&first));
peers.release(1);
assert_eq!(2, Arc::strong_count(&first), "one release is enough");
}
#[test]
fn dead_index_entries_are_swept() {
let mut peers = PeerAddresses::new(std::ptr::null_mut(), AddressFormat::Opaque);
for index in 0..(MINIMUM_SWEEP * 3) {
let key = PeerAddress::new(AddressFormat::Opaque, &index.to_ne_bytes())
.expect("opaque takes anything");
peers.by_address.insert(key, std::sync::Weak::new());
}
peers.sweep();
assert!(
peers.by_address.is_empty(),
"dead index keys accumulated: {}",
peers.by_address.len()
);
assert_eq!(
MINIMUM_SWEEP, peers.sweep_at,
"the threshold must fall back to its floor once the index empties"
);
}
#[test]
fn sweeping_keeps_live_entries() {
let endpoint = endpoint();
let address = endpoint.local_address().expect("local address");
let mut peers = PeerAddresses::new(endpoint.address_vector(), AddressFormat::Opaque);
peers.handle(1, &address).expect("client 1");
peers.sweep_at = 0; peers.sweep();
assert_eq!(1, peers.by_address.len(), "a held entry must survive");
}
fn efa_address(qpn: u16, qkey: u32) -> Vec<u8> {
let mut bytes = vec![0; EFA_ADDRESS_LEN];
bytes[..16].fill(0xfe);
bytes[16..18].copy_from_slice(&qpn.to_ne_bytes());
bytes[20..24].copy_from_slice(&qkey.to_ne_bytes());
bytes
}
#[test]
fn efa_addresses_differing_only_in_qkey_are_one_slot() {
let old =
PeerAddress::new(AddressFormat::Efa, &efa_address(0, 0x9bce1061)).expect("32 bytes");
let new =
PeerAddress::new(AddressFormat::Efa, &efa_address(0, 0x961f0203)).expect("32 bytes");
assert_eq!(old, new);
assert_ne!(old.bytes(), new.bytes());
let mut index = std::collections::HashMap::new();
index.insert(old, ());
assert!(index.contains_key(new.identity()));
}
#[test]
fn efa_addresses_with_different_qpns_are_different_slots() {
let one =
PeerAddress::new(AddressFormat::Efa, &efa_address(0, 0x9bce1061)).expect("32 bytes");
let other =
PeerAddress::new(AddressFormat::Efa, &efa_address(1, 0x9bce1061)).expect("32 bytes");
assert_ne!(one, other);
}
#[test]
fn efa_rejects_any_length_but_the_providers() {
assert!(PeerAddress::new(AddressFormat::Efa, &[0; EFA_ADDRESS_LEN - 1]).is_err());
assert!(PeerAddress::new(AddressFormat::Efa, &[0; EFA_ADDRESS_LEN + 1]).is_err());
assert!(PeerAddress::new(AddressFormat::Efa, &[0; EFA_ADDRESS_LEN]).is_ok());
}
#[test]
fn opaque_addresses_compare_every_byte() {
let one = PeerAddress::new(AddressFormat::Opaque, &[1, 2, 3]).expect("any length");
let other = PeerAddress::new(AddressFormat::Opaque, &[1, 2, 4]).expect("any length");
assert_ne!(one, other);
}
#[test]
#[ignore = "needs an efa device"]
fn a_recycled_queue_pair_evicts_its_dead_incarnation() {
let configuration = Configuration {
providers: vec![Provider::EfaDirect],
..Configuration::default()
};
let endpoint = LibfabricEndpoint::open(&configuration, None).expect("efa-direct endpoint");
let format = AddressFormat::from_fi(endpoint.describe().addr_format);
assert_eq!(AddressFormat::Efa, format);
let mut peers = PeerAddresses::new(endpoint.address_vector(), format);
let old = endpoint.local_address().expect("local address");
let mut new = old.clone();
new[20] ^= 1;
peers.handle(1, &old).expect("first incarnation");
peers.handle(2, &old).expect("client 2 shares it");
let second = peers
.handle(1, &new)
.expect("second incarnation, with the first removed first");
assert_eq!(
vec![&1],
peers.by_client.keys().collect::<Vec<_>>(),
"client 2 was posting at a dead endpoint and must have been evicted"
);
assert_eq!(2, Arc::strong_count(&second), "the map and this test");
peers.release(1);
}
}