metrale_storage/cache_peer/
server_impl.rs1use std::net::{TcpListener, TcpStream, ToSocketAddrs};
15
16use anyhow::{Context, Result, bail};
17
18#[derive(Clone, Debug)]
21pub struct RdmaConfig {
22 pub rails: Vec<(String, u32)>,
25 pub max_blade_bytes: u64,
28 pub swap_dir: Option<std::path::PathBuf>,
31 pub swap_cap_bytes: u64,
34 pub per_kind_swap_cap_bytes: std::collections::HashMap<u8, u64>,
37}
38
39impl Default for RdmaConfig {
40 fn default() -> Self {
41 Self {
42 rails: vec![("roceP2p1s0f1".into(), 3), ("rocep1s0f1".into(), 3)],
43 max_blade_bytes: 0,
44 swap_dir: None,
45 swap_cap_bytes: 50 * 1024 * 1024 * 1024,
46 per_kind_swap_cap_bytes: std::collections::HashMap::new(),
47 }
48 }
49}
50
51pub fn serve<A: ToSocketAddrs>(addr: A, rdma: RdmaConfig) -> Result<()> {
54 let listener = TcpListener::bind(addr).context("bind cache-peer listener")?;
55 let local = listener.local_addr().ok();
56 let ledger = std::sync::Arc::new(crate::blade_cap::CommitLedger::new(rdma.max_blade_bytes));
58 tracing::info!(
59 "cache-peer (RW RDMA overflow blade) listening on {:?} (rails {:?}, cap {})",
60 local,
61 rdma.rails,
62 if rdma.max_blade_bytes == 0 {
63 "unlimited".to_string()
64 } else {
65 format!(
66 "{:.1} GiB",
67 rdma.max_blade_bytes as f64 / (1024.0 * 1024.0 * 1024.0)
68 )
69 },
70 );
71 if rdma.max_blade_bytes == 0 && rdma.swap_dir.is_some() {
74 tracing::warn!(
75 "cache-peer paging registry active with NO blade ceiling (--max-blade-gb 0 = \
76 unlimited): each new (kind, shape) arena pins RDMA-registered RAM without bound. \
77 Set --max-blade-gb <G> to cap total memlocked blade RAM."
78 );
79 }
80 for conn in listener.incoming() {
81 let stream = match conn {
82 Ok(s) => s,
83 Err(e) => {
84 tracing::warn!("cache-peer accept error: {e}");
85 continue;
86 }
87 };
88 let rdma = rdma.clone();
89 let ledger = ledger.clone();
90 std::thread::spawn(move || {
91 if let Err(e) = handle_conn(stream, &rdma, &ledger) {
92 tracing::warn!("cache-peer connection ended: {e}");
93 }
94 });
95 }
96 Ok(())
97}
98
99#[cfg(not(metrale_rdma_verbs))]
100fn handle_conn(
101 _stream: TcpStream,
102 _rdma: &RdmaConfig,
103 _ledger: &std::sync::Arc<crate::blade_cap::CommitLedger>,
104) -> Result<()> {
105 bail!("cache-peer needs a build with rdma-core (metrale_rdma_verbs)");
106}
107
108#[cfg(metrale_rdma_verbs)]
109fn handle_conn(
110 mut stream: TcpStream,
111 rdma: &RdmaConfig,
112 ledger: &std::sync::Arc<crate::blade_cap::CommitLedger>,
113) -> Result<()> {
114 use super::registry::{self, Mmap, SharedPaging};
115 use metrale_gpu_sys::verbs::Verbs;
116 use metrale_gpu_sys::wire::{CacheServerParams, STATUS_OK, VerbsClientParams};
117 use std::io::{Read, Write};
118 stream.set_nodelay(true).ok();
119
120 let mut b8 = [0u8; 8];
126 stream.read_exact(&mut b8).context("read paging magic")?;
127 let first = u64::from_le_bytes(b8);
128 let (kind, arena_bytes, blob) = crate::snapshot_swap::parse_paging_header(first, &mut stream)?;
129 let total = arena_bytes as usize;
130 let blob = blob as usize;
131 if total == 0 || total > (1usize << 42) {
134 bail!("implausible blade arena size: {total}");
135 }
136 let paging: Option<(u8, usize)> = if blob == 0 {
137 None
138 } else {
139 if !total.is_multiple_of(blob) {
140 bail!("paging: arena_bytes {total} not a multiple of blob_bytes {blob}");
141 }
142 if rdma.swap_dir.is_none() {
144 bail!("paging client but peer started without --swap-dir; refusing");
145 }
146 Some((kind.0, blob))
147 };
148 let mut b1 = [0u8; 1];
149 stream.read_exact(&mut b1).context("read n_rails")?;
150 let n_rails = b1[0] as usize;
151 if n_rails == 0 || n_rails > rdma.rails.len() {
152 bail!(
153 "client asked for {n_rails} rails; peer has {}",
154 rdma.rails.len()
155 );
156 }
157
158 let pid = std::process::id();
162 let shared: Option<std::sync::Arc<SharedPaging>> = match paging {
163 Some((kind, blob)) => Some(registry::get_or_init_shared_paging(
164 rdma, kind, total, blob, ledger,
165 )?),
166 None => None,
167 };
168 let local: Option<(crate::blade_cap::Reservation, Mmap)> = if shared.is_none() {
171 let reservation = ledger.try_reserve(total as u64).context("kv blade cap")?;
172 let arena = Mmap::anon(total).context("mmap kv blade arena")?;
173 Some((reservation, arena))
174 } else {
175 None
176 };
177 let (arena_base, arena_len): (*mut libc::c_void, usize) = match (&shared, &local) {
178 (Some(sh), _) => (sh.arena.addr, sh.arena.len),
179 (None, Some((_, arena))) => (arena.addr, arena.len),
180 _ => unreachable!("exactly one of shared/local is set"),
181 };
182 let mut rails: Vec<Verbs> = Vec::with_capacity(n_rails);
185 let mut rkeys: Vec<u32> = Vec::with_capacity(n_rails);
186 for (i, (dev, gid)) in rdma.rails.iter().take(n_rails).enumerate() {
187 let psn = (0x5a5a5a ^ pid ^ ((i as u32) << 20)) & 0xff_ffff;
188 let mut v = Verbs::create(dev, *gid, psn)?;
189 let keys = unsafe { v.reg_mr_rw(arena_base as *mut _, arena_len)? };
192 rkeys.push(keys.rkey);
193 rails.push(v);
194 }
195
196 stream.write_all(&[n_rails as u8]).context("send n_rails")?;
197 for (v, rkey) in rails.iter().zip(&rkeys) {
198 CacheServerParams {
199 qpn: v.qpn(),
200 psn: v.psn(),
201 gid: v.gid(),
202 base_addr: arena_base as u64,
203 rkey: *rkey,
204 }
205 .write_to(&mut stream)
206 .context("send kv server params")?;
207 }
208
209 stream.read_exact(&mut b1).context("read client n_rails")?;
210 if b1[0] as usize != n_rails {
211 bail!("client rail count mismatch");
212 }
213 for v in rails.iter_mut() {
214 let cp = VerbsClientParams::read_from(&mut stream).context("read kv client params")?;
215 v.connect(cp.qpn, cp.psn, &cp.gid)?;
216 }
217 stream
218 .write_all(&[STATUS_OK])
219 .context("send kv ready ack")?;
220 let mode = if paging.is_some() {
221 "paging"
222 } else {
223 "raw one-sided"
224 };
225 tracing::info!(
226 "cache-peer client connected: kind {}, {n_rails} rail(s), {:.1} GiB RW blade ({mode})",
227 kind.0,
228 total as f64 / (1024.0 * 1024.0 * 1024.0),
229 );
230
231 if let Some(sh) = shared {
232 tracing::info!("cache-peer PAGING client joined shared arena ({n_rails} rail(s))");
236 let r = crate::snapshot_swap::run_paging_loop_shared(&mut stream, &sh.residency);
237 drop(rails);
238 return r;
239 }
240
241 let mut sink = [0u8; 8];
244 loop {
245 match stream.read(&mut sink) {
246 Ok(0) => break,
247 Ok(_) => {}
248 Err(_) => break,
249 }
250 }
251 drop(rails);
253 drop(local);
254 Ok(())
255}