1#![expect(missing_docs)]
37#![forbid(unsafe_code)]
38
39pub mod loopback;
40pub mod null;
41pub mod resolve;
42pub mod tests;
43
44use async_trait::async_trait;
45use futures::FutureExt;
46use futures::StreamExt;
47use futures::TryFutureExt;
48use futures::lock::Mutex;
49use futures_concurrency::future::Race;
50use guestmem::GuestMemory;
51use guestmem::GuestMemoryError;
52use inspect::InspectMut;
53use inspect_counters::Counter;
54use mesh::rpc::Rpc;
55use mesh::rpc::RpcSend;
56use null::NullEndpoint;
57use pal_async::driver::Driver;
58use std::future::pending;
59use std::sync::Arc;
60use std::task::Context;
61use std::task::Poll;
62use thiserror::Error;
63
64pub use net_backend_core::*;
65
66pub struct QueueConfig {
72 pub driver: Box<dyn Driver>,
73 pub enabled_rx_offloads: RxOffloadSupport,
75}
76
77#[async_trait]
88pub trait Endpoint: Send + Sync + InspectMut {
89 fn endpoint_type(&self) -> &'static str;
91
92 async fn get_queues(
94 &mut self,
95 config: Vec<QueueConfig>,
96 rss: Option<&RssConfig<'_>>,
97 queues: &mut Vec<Box<dyn Queue>>,
98 ) -> anyhow::Result<()>;
99
100 async fn stop(&mut self);
104
105 fn is_ordered(&self) -> bool;
109
110 fn tx_offload_support(&self) -> TxOffloadSupport {
112 TxOffloadSupport::default()
113 }
114
115 fn multiqueue_support(&self) -> MultiQueueSupport {
117 MultiQueueSupport {
118 max_queues: 1,
119 indirection_table_size: 0,
120 }
121 }
122
123 fn tx_fast_completions(&self) -> bool {
127 false
128 }
129
130 async fn set_data_path_to_guest_vf(&self, _use_vf: bool) -> anyhow::Result<()> {
133 Err(anyhow::Error::msg("Unsupported in current endpoint"))
134 }
135
136 async fn get_data_path_to_guest_vf(&self) -> anyhow::Result<bool> {
137 Err(anyhow::Error::msg("Unsupported in current endpoint"))
138 }
139
140 async fn wait_for_endpoint_action(&mut self) -> EndpointAction {
142 pending().await
143 }
144
145 fn link_speed(&self) -> u64 {
147 10 * 1000 * 1000 * 1000
150 }
151}
152
153#[derive(Error, Debug)]
154pub enum TxError {
155 #[error("error requiring queue restart. {0}")]
156 TryRestart(#[source] anyhow::Error),
157 #[error("unrecoverable error. {0}")]
158 Fatal(#[source] anyhow::Error),
159}
160pub trait BackendQueueStats {
161 fn rx_errors(&self) -> Counter;
162 fn tx_errors(&self) -> Counter;
163 fn rx_packets(&self) -> Counter;
164 fn tx_packets(&self) -> Counter;
165 fn tx_vlan_packets(&self) -> Counter {
166 Counter::new()
167 }
168 fn rx_vlan_packets(&self) -> Counter {
169 Counter::new()
170 }
171}
172
173#[async_trait]
190pub trait Queue: Send + InspectMut {
191 async fn update_target_vp(&mut self, target_vp: u32) {
193 let _ = target_vp;
194 }
195
196 fn poll_ready(&mut self, cx: &mut Context<'_>, pool: &mut dyn BufferAccess) -> Poll<()>;
198
199 fn rx_avail(&mut self, pool: &mut dyn BufferAccess, done: &[RxId]);
201
202 fn rx_poll(
204 &mut self,
205 pool: &mut dyn BufferAccess,
206 packets: &mut [RxId],
207 ) -> anyhow::Result<usize>;
208
209 fn tx_avail(
213 &mut self,
214 pool: &mut dyn BufferAccess,
215 segments: &[TxSegment],
216 ) -> anyhow::Result<(bool, usize)>;
217
218 fn tx_poll(&mut self, pool: &mut dyn BufferAccess, done: &mut [TxId])
220 -> Result<usize, TxError>;
221
222 fn queue_stats(&self) -> Option<&dyn BackendQueueStats> {
224 None }
226}
227
228pub trait BufferAccess {
239 fn guest_memory(&self) -> &GuestMemory;
241
242 fn write_data(&mut self, id: RxId, data: &[u8]);
244
245 fn push_guest_addresses(&self, id: RxId, buf: &mut Vec<RxBufferSegment>);
250
251 fn capacity(&self, id: RxId) -> u32;
253
254 fn write_header(&mut self, id: RxId, metadata: &RxMetadata);
256
257 fn write_packet(&mut self, id: RxId, metadata: &RxMetadata, data: &[u8]) {
259 self.write_data(id, data);
260 self.write_header(id, metadata);
261 }
262
263 fn write_packet_segments(&mut self, id: RxId, metadata: &RxMetadata, segments: &[&[u8]]) {
276 if let [segment] = segments {
277 self.write_packet(id, metadata, segment);
278 return;
279 }
280 let total = segments.iter().map(|s| s.len()).sum();
281 let mut data = Vec::with_capacity(total);
282 for segment in segments {
283 data.extend_from_slice(segment);
284 }
285 self.write_packet(id, metadata, &data);
286 }
287}
288
289pub fn linearize(
292 pool: &dyn BufferAccess,
293 segments: &mut &[TxSegment],
294) -> Result<Vec<u8>, GuestMemoryError> {
295 let (head, this, rest) = next_packet(segments);
296 let mut v = vec![0; head.len as usize];
297 let mut offset = 0;
298 let mem = pool.guest_memory();
299 for segment in this {
300 let dest = &mut v[offset..offset + segment.len as usize];
301 mem.read_at(segment.gpa, dest)?;
302 offset += segment.len as usize;
303 }
304 assert_eq!(v.len(), offset);
305 *segments = rest;
306 Ok(v)
307}
308
309enum DisconnectableEndpointUpdate {
310 EndpointConnected(Box<dyn Endpoint>),
311 EndpointDisconnected(Rpc<(), Option<Box<dyn Endpoint>>>),
312}
313
314pub struct DisconnectableEndpointControl {
315 send_update: mesh::Sender<DisconnectableEndpointUpdate>,
316 is_ordered: Option<bool>,
317}
318
319impl DisconnectableEndpointControl {
320 pub fn connect(&mut self, endpoint: Box<dyn Endpoint>) -> anyhow::Result<()> {
321 let new_is_ordered = endpoint.is_ordered();
322 if let Some(is_ordered) = self.is_ordered {
323 anyhow::ensure!(
324 !is_ordered || new_is_ordered,
325 "network endpoint cannot be reattached as unordered after being ordered"
326 );
327 } else {
328 self.is_ordered = Some(new_is_ordered);
329 }
330 self.send_update
331 .send(DisconnectableEndpointUpdate::EndpointConnected(endpoint));
332 Ok(())
333 }
334
335 pub async fn disconnect(&mut self) -> anyhow::Result<Option<Box<dyn Endpoint>>> {
336 self.send_update
337 .call(DisconnectableEndpointUpdate::EndpointDisconnected, ())
338 .map_err(anyhow::Error::from)
339 .await
340 }
341}
342
343pub struct DisconnectableEndpointCachedState {
344 is_ordered: bool,
345 tx_offload_support: TxOffloadSupport,
346 multiqueue_support: MultiQueueSupport,
347 tx_fast_completions: bool,
348 link_speed: u64,
349}
350
351pub struct DisconnectableEndpoint {
352 endpoint: Option<Box<dyn Endpoint>>,
353 null_endpoint: Box<dyn Endpoint>,
354 cached_state: Option<DisconnectableEndpointCachedState>,
355 receive_update: Arc<Mutex<mesh::Receiver<DisconnectableEndpointUpdate>>>,
356 notify_disconnect_complete: Option<(
357 Rpc<(), Option<Box<dyn Endpoint>>>,
358 Option<Box<dyn Endpoint>>,
359 )>,
360}
361
362impl InspectMut for DisconnectableEndpoint {
363 fn inspect_mut(&mut self, req: inspect::Request<'_>) {
364 self.current_mut().inspect_mut(req)
365 }
366}
367
368impl DisconnectableEndpoint {
369 pub fn new() -> (Self, DisconnectableEndpointControl) {
370 let (endpoint_tx, endpoint_rx) = mesh::channel();
371 let control = DisconnectableEndpointControl {
372 send_update: endpoint_tx,
373 is_ordered: None,
374 };
375 (
376 Self {
377 endpoint: None,
378 null_endpoint: Box::new(NullEndpoint::new()),
379 cached_state: None,
380 receive_update: Arc::new(Mutex::new(endpoint_rx)),
381 notify_disconnect_complete: None,
382 },
383 control,
384 )
385 }
386
387 fn current(&self) -> &dyn Endpoint {
388 self.endpoint
389 .as_ref()
390 .unwrap_or(&self.null_endpoint)
391 .as_ref()
392 }
393
394 fn current_mut(&mut self) -> &mut dyn Endpoint {
395 self.endpoint
396 .as_mut()
397 .unwrap_or(&mut self.null_endpoint)
398 .as_mut()
399 }
400}
401
402#[async_trait]
403impl Endpoint for DisconnectableEndpoint {
404 fn endpoint_type(&self) -> &'static str {
405 self.current().endpoint_type()
406 }
407
408 async fn get_queues(
409 &mut self,
410 config: Vec<QueueConfig>,
411 rss: Option<&RssConfig<'_>>,
412 queues: &mut Vec<Box<dyn Queue>>,
413 ) -> anyhow::Result<()> {
414 self.current_mut().get_queues(config, rss, queues).await
415 }
416
417 async fn stop(&mut self) {
418 self.current_mut().stop().await
419 }
420
421 fn is_ordered(&self) -> bool {
422 self.cached_state
423 .as_ref()
424 .expect("Endpoint needs connected at least once before use")
425 .is_ordered
426 }
427
428 fn tx_offload_support(&self) -> TxOffloadSupport {
429 self.cached_state
430 .as_ref()
431 .expect("Endpoint needs connected at least once before use")
432 .tx_offload_support
433 }
434
435 fn multiqueue_support(&self) -> MultiQueueSupport {
436 self.cached_state
437 .as_ref()
438 .expect("Endpoint needs connected at least once before use")
439 .multiqueue_support
440 }
441
442 fn tx_fast_completions(&self) -> bool {
443 self.cached_state
444 .as_ref()
445 .expect("Endpoint needs connected at least once before use")
446 .tx_fast_completions
447 }
448
449 async fn set_data_path_to_guest_vf(&self, use_vf: bool) -> anyhow::Result<()> {
450 self.current().set_data_path_to_guest_vf(use_vf).await
451 }
452
453 async fn get_data_path_to_guest_vf(&self) -> anyhow::Result<bool> {
454 self.current().get_data_path_to_guest_vf().await
455 }
456
457 async fn wait_for_endpoint_action(&mut self) -> EndpointAction {
458 if let Some((rpc, old_endpoint)) = self.notify_disconnect_complete.take() {
461 rpc.handle(async |_| old_endpoint).await;
462 }
463
464 enum Message {
465 DisconnectableEndpointUpdate(DisconnectableEndpointUpdate),
466 UpdateFromEndpoint(EndpointAction),
467 }
468 let receiver = self.receive_update.clone();
469 let mut receive_update = receiver.lock().await;
470 let update = async {
471 match receive_update.next().await {
472 Some(m) => Message::DisconnectableEndpointUpdate(m),
473 None => {
474 pending::<()>().await;
475 unreachable!()
476 }
477 }
478 };
479 let ep_update = self
480 .current_mut()
481 .wait_for_endpoint_action()
482 .map(Message::UpdateFromEndpoint);
483 let m = (update, ep_update).race().await;
484 match m {
485 Message::DisconnectableEndpointUpdate(
486 DisconnectableEndpointUpdate::EndpointConnected(endpoint),
487 ) => {
488 let old_endpoint = self.endpoint.take();
489 assert!(old_endpoint.is_none());
490 self.endpoint = Some(endpoint);
491 let new_is_ordered = self.current().is_ordered();
492 let is_ordered = if let Some(prev) = &self.cached_state {
493 assert!(
494 !prev.is_ordered || new_is_ordered,
495 "network endpoint reattached as unordered after being ordered"
496 );
497 prev.is_ordered
498 } else {
499 new_is_ordered
500 };
501 self.cached_state = Some(DisconnectableEndpointCachedState {
502 is_ordered,
503 tx_offload_support: self.current().tx_offload_support(),
504 multiqueue_support: self.current().multiqueue_support(),
505 tx_fast_completions: self.current().tx_fast_completions(),
506 link_speed: self.current().link_speed(),
507 });
508 EndpointAction::RestartRequired
509 }
510 Message::DisconnectableEndpointUpdate(
511 DisconnectableEndpointUpdate::EndpointDisconnected(rpc),
512 ) => {
513 let old_endpoint = self.endpoint.take();
514 self.notify_disconnect_complete = Some((rpc, old_endpoint));
519 EndpointAction::RestartRequired
520 }
521 Message::UpdateFromEndpoint(update) => update,
522 }
523 }
524
525 fn link_speed(&self) -> u64 {
526 self.cached_state
527 .as_ref()
528 .expect("Endpoint needs connected at least once before use")
529 .link_speed
530 }
531}
532
533#[cfg(test)]
534mod disconnectable_endpoint_tests {
535 use super::*;
536 use test_with_tracing::test;
537
538 #[derive(InspectMut)]
539 struct TestEndpoint {
540 is_ordered: bool,
541 }
542
543 #[async_trait]
544 impl Endpoint for TestEndpoint {
545 fn endpoint_type(&self) -> &'static str {
546 "test"
547 }
548
549 async fn get_queues(
550 &mut self,
551 _config: Vec<QueueConfig>,
552 _rss: Option<&RssConfig<'_>>,
553 _queues: &mut Vec<Box<dyn Queue>>,
554 ) -> anyhow::Result<()> {
555 unreachable!()
556 }
557
558 async fn stop(&mut self) {
559 unreachable!()
560 }
561
562 fn is_ordered(&self) -> bool {
563 self.is_ordered
564 }
565 }
566
567 #[test]
568 fn connect_pins_endpoint_ordering() {
569 let (_endpoint, mut control) = DisconnectableEndpoint::new();
570 control
571 .connect(Box::new(TestEndpoint { is_ordered: true }))
572 .unwrap();
573
574 let err = control
575 .connect(Box::new(TestEndpoint { is_ordered: false }))
576 .unwrap_err();
577 assert_eq!(
578 err.to_string(),
579 "network endpoint cannot be reattached as unordered after being ordered"
580 );
581
582 let (_endpoint, mut control) = DisconnectableEndpoint::new();
583 control
584 .connect(Box::new(TestEndpoint { is_ordered: false }))
585 .unwrap();
586 control
587 .connect(Box::new(TestEndpoint { is_ordered: true }))
588 .unwrap();
589 }
590}