pub(crate) mod arena;
pub(crate) mod backend;
pub(crate) mod region;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::{Duration, Instant};
use dashmap::DashMap;
use tokio_util::sync::CancellationToken;
use velo_ext::ShutdownState;
use crate::observability::VeloMetrics;
pub use arena::{PinnedBuf, RdmaPoolConfig};
pub use backend::RdmaError;
pub use region::{Deregistered, RegionGuard, RegionWatch};
pub(crate) use arena::{ArenaSet, Budget, TransferHold};
pub(crate) use backend::{BackendGet, RdmaBackend, UcxBackend};
use region::{RegionInner, RegionParts};
#[derive(Debug, Clone)]
pub struct RdmaConfig {
pub pool: RdmaPoolConfig,
pub rendezvous: RdmaRendezvousConfig,
pub shutdown_timeout: Duration,
pub drop_dereg_timeout: Duration,
}
impl Default for RdmaConfig {
fn default() -> Self {
Self {
pool: RdmaPoolConfig::default(),
rendezvous: RdmaRendezvousConfig::default(),
shutdown_timeout: Duration::from_secs(30),
drop_dereg_timeout: Duration::from_secs(30),
}
}
}
#[derive(Debug, Clone)]
pub struct RdmaRendezvousConfig {
pub enabled: bool,
pub rdma_min_bytes: u64,
pub lease_timeout: Duration,
}
impl Default for RdmaRendezvousConfig {
fn default() -> Self {
Self {
enabled: true,
rdma_min_bytes: 64 << 10,
lease_timeout: Duration::from_secs(30),
}
}
}
#[non_exhaustive]
pub struct RegisterOwnedError {
pub buffer: Option<Box<[u8]>>,
pub cause: RdmaError,
}
impl RegisterOwnedError {
pub fn into_parts(self) -> (Option<Box<[u8]>>, RdmaError) {
(self.buffer, self.cause)
}
}
impl std::fmt::Debug for RegisterOwnedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RegisterOwnedError")
.field("buffer_len", &self.buffer.as_ref().map(|b| b.len()))
.field("cause", &self.cause)
.finish()
}
}
impl std::fmt::Display for RegisterOwnedError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.cause, f)
}
}
impl std::error::Error for RegisterOwnedError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.cause)
}
}
pub(crate) struct RegistryShared {
pub(crate) backend: Arc<dyn RdmaBackend>,
pub(crate) cfg: RdmaConfig,
pub(crate) runtime: tokio::runtime::Handle,
regions: DashMap<u64, Arc<RegionInner>>,
budget: Arc<Budget>,
metrics: Option<Arc<VeloMetrics>>,
admission: ShutdownState,
gate_closed: AtomicBool,
shutdown_token: CancellationToken,
next_region_id: AtomicU64,
generations: Arc<AtomicU64>,
sweeps: std::sync::atomic::AtomicUsize,
}
impl RegistryShared {
pub(crate) fn forget_region(&self, inner: &Arc<RegionInner>) {
if self.regions.remove(&inner.id).is_some() {
self.budget.release(inner.charged());
}
}
}
pub(crate) struct RdmaRegistry {
shared: Arc<RegistryShared>,
pool: ArenaSet,
}
impl RdmaRegistry {
pub(crate) fn new(
backend: Arc<dyn RdmaBackend>,
cfg: RdmaConfig,
runtime: tokio::runtime::Handle,
metrics: Option<Arc<VeloMetrics>>,
) -> Self {
let budget = Arc::new(Budget::new(
cfg.pool.registered_bytes_budget,
metrics.clone(),
));
let generations = Arc::new(AtomicU64::new(1));
let pool = ArenaSet::new(
Arc::clone(&backend),
cfg.pool.clone(),
Arc::clone(&budget),
Arc::clone(&generations),
metrics.clone(),
);
let shared = Arc::new(RegistryShared {
backend,
cfg,
runtime,
regions: DashMap::new(),
budget,
metrics,
admission: ShutdownState::new(),
gate_closed: AtomicBool::new(false),
shutdown_token: CancellationToken::new(),
next_region_id: AtomicU64::new(1),
generations,
sweeps: std::sync::atomic::AtomicUsize::new(0),
});
Self { shared, pool }
}
fn admit(&self) -> Result<velo_ext::InFlightGuard, RdmaError> {
let ticket = self.shared.admission.acquire();
if self.shared.gate_closed.load(Ordering::SeqCst) {
drop(ticket);
return Err(RdmaError::ShuttingDown);
}
Ok(ticket)
}
pub(crate) async unsafe fn register_external(
&self,
ptr: NonNull<u8>,
len: usize,
) -> Result<RegionGuard, RdmaError> {
self.register(ptr.as_ptr() as usize, len, None)
.await
.map_err(|e| e.cause)
}
pub(crate) async fn register_owned(
&self,
buf: Box<[u8]>,
) -> Result<RegionGuard, RegisterOwnedError> {
let ptr = buf.as_ptr() as usize;
let len = buf.len();
self.register(ptr, len, Some(buf)).await
}
async fn register(
&self,
ptr: usize,
len: usize,
owned: Option<Box<[u8]>>,
) -> Result<RegionGuard, RegisterOwnedError> {
macro_rules! refuse {
($cause:expr) => {
return Err(RegisterOwnedError {
buffer: owned,
cause: $cause,
})
};
}
if ptr == 0 || len == 0 {
refuse!(RdmaError::OutOfRange);
}
let Some(enclosing) = arena::page_enclosing_len(ptr, len) else {
refuse!(RdmaError::OutOfRange);
};
let ticket = match self.admit() {
Ok(ticket) => ticket,
Err(e) => refuse!(e),
};
let mut reservation = match self.shared.budget.try_reserve(enclosing) {
Ok(reservation) => reservation,
Err(e) => refuse!(e),
};
let mapped = match self.shared.backend.map(ptr, len).await {
Ok(mapped) => mapped,
Err(e) => refuse!(e),
};
reservation.raise_to(mapped.effective_len.max(enclosing));
let inner = Arc::new(RegionInner::new(RegionParts {
id: self.shared.next_region_id.fetch_add(1, Ordering::Relaxed),
generation: self.shared.generations.fetch_add(1, Ordering::Relaxed),
backend_region_id: mapped.backend_region_id,
ptr,
len,
packed_key: mapped.packed_key,
effective_addr: mapped.effective_addr,
effective_len: mapped.effective_len,
owned,
charged: reservation.bytes(),
shutdown: self.shared.shutdown_token.clone(),
}));
self.shared.regions.insert(inner.id, Arc::clone(&inner));
reservation.commit();
drop(ticket);
if let Some(m) = &self.shared.metrics {
m.record_rdma_registration(crate::observability::RdmaRegistrationKind::External);
}
Ok(RegionGuard::new(inner, Arc::clone(&self.shared)))
}
pub(crate) async fn alloc_pinned(&self, len: usize) -> Result<PinnedBuf, RdmaError> {
let _ticket = self.admit()?;
self.pool.alloc(len).await
}
pub(crate) fn try_alloc_pinned(&self, len: usize) -> Option<PinnedBuf> {
let _ticket = self.admit().ok()?;
self.pool.try_alloc_existing(len)
}
pub(crate) async fn get(&self, req: BackendGet) -> Result<(), RdmaError> {
let _ticket = self.admit()?;
self.shared.backend.get(req).await
}
pub(crate) async fn reclaim_idle_arenas(&self) -> usize {
let Ok(_ticket) = self.admit() else {
return 0;
};
self.pool.reclaim_idle().await
}
pub(crate) fn arena_reclaim_after(&self) -> Option<Duration> {
self.shared.cfg.pool.arena_reclaim_after
}
pub(crate) fn runtime(&self) -> &tokio::runtime::Handle {
&self.shared.runtime
}
pub(crate) fn live_regions(&self) -> Option<usize> {
self.shared.backend.live_registrations()
}
pub(crate) async fn shutdown(&self, budget: Duration) {
self.shared.sweeps.fetch_add(1, Ordering::Relaxed);
let deadline = Instant::now() + budget;
let remaining = |deadline: Instant| deadline.saturating_duration_since(Instant::now());
self.shared.gate_closed.store(true, Ordering::SeqCst);
self.shared.admission.begin_drain();
self.shared.shutdown_token.cancel();
let admitted = self.shared.admission.wait_for_drain();
let landed = tokio::time::timeout(remaining(deadline), admitted).await;
if landed.is_err() {
tracing::warn!(
"rdma: registrations in progress outlasted the shutdown budget; one may \
register memory this sweep will not unmap"
);
}
let regions: Vec<Arc<RegionInner>> = self
.shared
.regions
.iter()
.map(|e| Arc::clone(e.value()))
.collect();
for inner in regions {
let region = inner.id;
match region::deregister(&self.shared, &inner, remaining(deadline)).await {
Ok(region::Deregistered::Drained) => {}
Ok(region::Deregistered::DrainTimedOut) => {
tracing::warn!(
region,
"rdma: region unmapped without waiting out its in-flight work; the \
memory is released and the latch resolved, but a straggling remote \
transfer may now fail at its own end"
);
}
Err(e) => {
tracing::warn!(
region,
%e,
"rdma: region unmap unconfirmed; the deregistered() latch stays \
unresolved until the end of velo shutdown"
);
}
}
}
let leaked = self.pool.unmap_all(deadline).await;
if leaked != 0 {
tracing::warn!(
arenas = leaked,
"rdma: arenas left pinned at shutdown; their pages are freed once velo \
shutdown completes"
);
}
}
pub(crate) fn latch_all_deregistered(&self) {
match self.shared.backend.live_registrations() {
Some(0) | None => {}
Some(live) => {
tracing::error!(
live,
"rdma: refusing to declare registrations released while the backend still \
holds {live} of them; their memory stays pinned and their deregistered() \
latches stay unresolved. Shutdown ran out of order."
);
return;
}
}
let regions: Vec<Arc<RegionInner>> = self
.shared
.regions
.iter()
.map(|e| Arc::clone(e.value()))
.collect();
for inner in regions {
self.shared.forget_region(&inner);
inner.latch_deregistered();
}
self.pool.release_unconfirmed();
}
pub(crate) fn registered_bytes(&self) -> u64 {
self.shared.budget.registered()
}
#[cfg(feature = "test-helpers")]
pub(crate) fn in_flight_transfers(&self) -> usize {
self.pool.in_flight_transfers()
}
#[allow(dead_code)]
pub(crate) fn region_count(&self) -> usize {
self.shared.regions.len()
}
#[cfg(test)]
pub(crate) fn pool(&self) -> &ArenaSet {
&self.pool
}
#[cfg(test)]
pub(crate) fn sweep_count(&self) -> usize {
self.shared.sweeps.load(Ordering::Relaxed)
}
pub(crate) fn shutdown_timeout(&self) -> Duration {
self.shared.cfg.shutdown_timeout
}
pub(crate) fn backend_key(&self) -> &str {
self.shared.backend.key()
}
}
#[cfg(test)]
#[path = "tests.rs"]
mod tests;