1use std::collections::HashMap;
39use std::sync::Arc;
40use std::sync::LazyLock;
41use std::sync::OnceLock;
42use std::sync::RwLock;
43
44use async_trait::async_trait;
45use hyperactor::Actor;
46use hyperactor::ActorEnvironment;
47use hyperactor::ActorHandle;
48use hyperactor::ActorRef;
49use hyperactor::Context;
50use hyperactor::HandleClient;
51use hyperactor::Handler;
52use hyperactor::Instance;
53use hyperactor::OncePortHandle;
54use hyperactor::OncePortRef;
55use hyperactor::ProcAddr;
56use hyperactor::RefClient;
57use hyperactor::RemoteSpawn;
58use hyperactor::context;
59use serde::Deserialize;
60use serde::Serialize;
61use tokio::sync::OnceCell;
62use typeuri::Named;
63
64use crate::backend::RdmaBackendHandle;
65use crate::backend::RdmaBackends;
66use crate::backend::RdmaConfig;
67use crate::backend::ibverbs::primitives::IbvConfig;
68use crate::backend::tcp::manager_actor::TcpManagerActor;
69use crate::local_memory::KeepaliveLocalMemory;
70use crate::rdma_components::RdmaRemoteBuffer;
71use crate::rdma_manager_owner::EntryId;
72use crate::rdma_manager_owner::RdmaManagerOwnerActor;
73use crate::rdma_manager_owner::RdmaManagerReady;
74use crate::rdma_manager_owner::RdmaManagerReadyHandler;
75use crate::rdma_manager_owner::ReadyAckClient;
76
77static BACKEND_HANDLE_CACHE: LazyLock<
80 RwLock<HashMap<ProcAddr, Arc<OnceCell<Vec<RdmaBackendHandle>>>>>,
81> = LazyLock::new(|| RwLock::new(HashMap::new()));
82
83pub fn get_rdmaxcel_error_message(error_code: i32) -> String {
85 unsafe {
86 let c_str = rdmaxcel_sys::rdmaxcel_error_string(error_code);
87 std::ffi::CStr::from_ptr(c_str)
88 .to_string_lossy()
89 .into_owned()
90 }
91}
92
93#[derive(Handler, HandleClient, Debug)]
98pub enum RdmaManagerMessage {
99 RequestBuffer {
102 local: KeepaliveLocalMemory,
103 #[reply]
104 reply: OncePortHandle<RdmaRemoteBuffer>,
105 },
106 RequestLocalMemory {
109 remote_buf_id: usize,
110 #[reply]
111 reply: OncePortHandle<Option<KeepaliveLocalMemory>>,
112 },
113 GetBackendHandles {
115 #[reply]
116 reply: OncePortHandle<Vec<RdmaBackendHandle>>,
117 },
118}
119
120#[derive(Handler, HandleClient, RefClient, Debug, Serialize, Deserialize, Named)]
125pub struct ReleaseBuffer {
126 pub id: usize,
127}
128wirevalue::register_type!(ReleaseBuffer);
129
130#[derive(Handler, HandleClient, RefClient, Debug, Serialize, Deserialize, Named)]
133pub struct GetTcpActorRef {
134 #[reply]
135 pub reply: OncePortRef<ActorRef<TcpManagerActor>>,
136}
137wirevalue::register_type!(GetTcpActorRef);
138
139#[derive(Debug)]
140#[hyperactor::export(
141 handlers = [
142 GetTcpActorRef,
143 ReleaseBuffer,
144 RdmaManagerReady,
145 ],
146)]
147#[hyperactor::spawnable]
148pub struct RdmaManagerActor {
149 next_remote_buf_id: usize,
150 buffers: HashMap<usize, KeepaliveLocalMemory>,
151 params: Option<IbvConfig>,
152 backends: OnceLock<RdmaBackends>,
153}
154
155impl RdmaManagerActor {
156 pub fn local_handle(client: &impl context::Actor) -> ActorHandle<Self> {
159 let actor_ref = ActorRef::attest(
160 client
161 .mailbox()
162 .actor_addr()
163 .proc_addr()
164 .actor_addr("rdma_manager"),
165 );
166 actor_ref
167 .downcast_handle(client)
168 .expect("RdmaManagerActor is not in the local process")
169 }
170
171 pub async fn local_backend_handles(
172 client: &(impl context::Actor + Send + Sync),
173 ) -> Result<Vec<RdmaBackendHandle>, anyhow::Error> {
174 let key = client.mailbox().actor_addr().proc_addr();
175 let cell = BACKEND_HANDLE_CACHE
176 .read()
177 .expect("backend handle cache lock should not be poisoned")
178 .get(&key)
179 .cloned();
180 let cell = cell.unwrap_or_else(|| {
181 Arc::clone(
182 BACKEND_HANDLE_CACHE
183 .write()
184 .expect("backend handle cache lock should not be poisoned")
185 .entry(key)
186 .or_insert_with(|| Arc::new(OnceCell::new())),
187 )
188 });
189 cell.get_or_try_init(async || Self::local_handle(client).get_backend_handles(client).await)
190 .await
191 .cloned()
192 }
193}
194
195#[async_trait]
196impl RemoteSpawn for RdmaManagerActor {
197 type Params = Option<IbvConfig>;
198
199 async fn new(
200 params: Self::Params,
201 _environment: &ActorEnvironment,
202 ) -> Result<Self, anyhow::Error> {
203 Ok(Self {
204 next_remote_buf_id: 0,
205 buffers: HashMap::new(),
206 params,
207 backends: OnceLock::new(),
208 })
209 }
210}
211
212#[async_trait]
213impl Actor for RdmaManagerActor {
214 async fn init(&mut self, this: &Instance<Self>) -> Result<(), anyhow::Error> {
215 this.set_system();
216 if self
221 .params
222 .as_ref()
223 .and_then(|config| config.target.as_ref())
224 .is_none()
225 {
226 let _ = crate::backend::ibverbs::device_selection::configured_ibverbs_target()?;
227 }
228 let _ = crate::backend::ibverbs::device_selection::configured_peer_device_affinity()?;
231
232 let backends = RdmaBackends::spawn_available(
235 this,
236 &RdmaConfig {
237 ibv: self.params.clone(),
238 },
239 )
240 .await?;
241 self.backends.set(backends).expect("backends set once");
242 Ok(())
243 }
244
245 fn spawn_server_task<F>(future: F) -> tokio::task::JoinHandle<F::Output>
249 where
250 F: std::future::Future + Send + 'static,
251 F::Output: Send + 'static,
252 {
253 crate::rdma_runtime::spawn_on_rdma_runtime(future)
254 }
255}
256
257#[async_trait]
258#[hyperactor::handle(GetTcpActorRef)]
259impl GetTcpActorRefHandler for RdmaManagerActor {
260 async fn get_tcp_actor_ref(
261 &mut self,
262 _cx: &Context<Self>,
263 ) -> Result<ActorRef<TcpManagerActor>, anyhow::Error> {
264 self.backends
265 .get()
266 .expect("backends set in init")
267 .handles()
268 .into_iter()
269 .find_map(|h| match h {
270 RdmaBackendHandle::Tcp(backend) => Some(backend.bind()),
271 _ => None,
272 })
273 .ok_or_else(|| anyhow::anyhow!("TCP backend not available"))
274 }
275}
276
277#[async_trait]
278#[hyperactor::handle(ReleaseBuffer)]
279impl ReleaseBufferHandler for RdmaManagerActor {
280 async fn release_buffer(&mut self, cx: &Context<Self>, id: usize) -> Result<(), anyhow::Error> {
281 self.buffers.remove(&id);
282 self.backends
283 .get()
284 .expect("backends set in init")
285 .release_all(cx, id)
286 .await?;
287 Ok(())
288 }
289}
290
291#[async_trait]
292#[hyperactor::handle(RdmaManagerMessage)]
293impl RdmaManagerMessageHandler for RdmaManagerActor {
294 async fn request_buffer(
295 &mut self,
296 cx: &Context<Self>,
297 local: KeepaliveLocalMemory,
298 ) -> Result<RdmaRemoteBuffer, anyhow::Error> {
299 let remote_buf_id = self.next_remote_buf_id;
300 self.next_remote_buf_id += 1;
301 let size = local.size();
302
303 let backends = self
304 .backends
305 .get()
306 .expect("backends set in init")
307 .register_all(cx, remote_buf_id, local.clone())
308 .await?;
309 self.buffers.insert(remote_buf_id, local);
310
311 Ok(RdmaRemoteBuffer {
312 id: remote_buf_id,
313 size,
314 owner: cx.bind().clone(),
315 backends,
316 })
317 }
318
319 async fn request_local_memory(
320 &mut self,
321 _cx: &Context<Self>,
322 remote_buf_id: usize,
323 ) -> Result<Option<KeepaliveLocalMemory>, anyhow::Error> {
324 Ok(self.buffers.get(&remote_buf_id).cloned())
325 }
326
327 async fn get_backend_handles(
328 &mut self,
329 _cx: &Context<Self>,
330 ) -> Result<Vec<RdmaBackendHandle>, anyhow::Error> {
331 Ok(self.backends.get().expect("backends set in init").handles())
332 }
333}
334
335#[async_trait]
336#[hyperactor::handle(RdmaManagerReady)]
337impl RdmaManagerReadyHandler for RdmaManagerActor {
338 async fn rdma_manager_ready(
339 &mut self,
340 cx: &Context<Self>,
341 owner: ActorRef<RdmaManagerOwnerActor>,
342 entry: EntryId,
343 ) -> Result<(), anyhow::Error> {
344 owner.ready_ack(cx, entry).await?;
347 Ok(())
348 }
349}