Skip to content

Commit 47dd716

Browse files
committed
devices/net: Rewrite virtio-net in terms of new batch_queue utilities
Signed-off-by: Matej Hrica <mhrica@redhat.com>
1 parent a85e44d commit 47dd716

10 files changed

Lines changed: 951 additions & 658 deletions

File tree

src/devices/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ edition = "2021"
88
tee = []
99
amd-sev = ["blk", "tee"]
1010
tdx = ["blk", "tee"]
11-
net = []
11+
net = ["batch_queue"]
1212
batch_queue = []
1313
blk = []
1414
efi = ["blk", "net"]
@@ -25,7 +25,7 @@ crossbeam-channel = ">=0.5.15"
2525
libc = ">=0.2.39"
2626
libloading = "0.8"
2727
log = "0.4.0"
28-
nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs"] }
28+
nix = { version = "0.30.1", features = ["ioctl", "net", "poll", "socket", "fs", "uio"] }
2929
pw = { package = "pipewire", version = "0.8.0", optional = true }
3030
rand = "0.9.2"
3131
thiserror = { version = "2.0", optional = true }

src/devices/src/virtio/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use std::io::Error as IOError;
1212

1313
#[cfg(not(feature = "tee"))]
1414
pub mod balloon;
15+
#[cfg(feature = "batch_queue")]
16+
pub mod batch_queue;
1517
#[allow(dead_code)]
1618
#[allow(non_camel_case_types)]
1719
pub mod bindings;
@@ -36,6 +38,8 @@ mod queue;
3638
pub mod rng;
3739
#[cfg(feature = "snd")]
3840
pub mod snd;
41+
#[cfg(all(feature = "batch_queue", test))]
42+
pub(crate) mod test_utils;
3943
pub mod vsock;
4044

4145
#[cfg(not(feature = "tee"))]

src/devices/src/virtio/net/backend.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,39 @@ pub enum ConnectError {
1818
#[allow(dead_code)]
1919
#[derive(Debug)]
2020
pub enum ReadError {
21-
/// Nothing was written
22-
NothingRead,
23-
/// Another internal error occurred
21+
/// Backend process not running (EPIPE)
22+
ProcessNotRunning,
23+
/// Internal I/O error
2424
Internal(nix::Error),
2525
}
2626

2727
#[allow(dead_code)]
2828
#[derive(Debug)]
2929
pub enum WriteError {
30-
/// Nothing was written, you can drop the frame or try to resend it later
31-
NothingWritten,
32-
/// Part of the buffer was written, the write has to be finished using try_finish_write
33-
PartialWrite,
34-
/// Passt doesnt seem to be running (received EPIPE)
30+
/// Backend process not running (EPIPE)
3531
ProcessNotRunning,
36-
/// Another internal error occurred
32+
/// Internal I/O error
3733
Internal(nix::Error),
3834
}
3935

36+
/// Network backend trait.
37+
///
38+
/// Backends own both the socket and the queue consumers. The send/recv methods
39+
/// operate on internal queues. EAGAIN is not an error - it just means nothing
40+
/// happened this call.
4041
pub trait NetBackend {
41-
fn read_frame(&mut self, buf: &mut [u8]) -> Result<usize, ReadError>;
42-
fn write_frame(&mut self, hdr_len: usize, buf: &mut [u8]) -> Result<(), WriteError>;
43-
fn has_unfinished_write(&self) -> bool;
44-
fn try_finish_write(&mut self, hdr_len: usize, buf: &[u8]) -> Result<(), WriteError>;
42+
/// Send pending frames from the TX queue to the network.
43+
///
44+
/// Pulls frames from internal TxQueueConsumer and sends using batched I/O.
45+
/// EAGAIN returns Ok(()) - pending frames kept for retry.
46+
fn send(&mut self) -> Result<(), WriteError>;
47+
48+
/// Receive frames from the network into the RX queue.
49+
///
50+
/// Reads from socket into internal RxQueueProvider.
51+
/// EAGAIN returns Ok(()).
52+
fn recv(&mut self) -> Result<(), ReadError>;
53+
54+
/// Returns the raw socket fd for epoll registration.
4555
fn raw_socket_fd(&self) -> RawFd;
4656
}

src/devices/src/virtio/net/device.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,18 @@ use std::cmp;
2020
use std::io::Write;
2121
use std::os::fd::RawFd;
2222
use std::path::PathBuf;
23-
use virtio_bindings::virtio_net::VIRTIO_NET_F_MAC;
24-
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
25-
use vm_memory::{ByteValued, GuestMemoryError, GuestMemoryMmap};
23+
use virtio_bindings::{virtio_net::VIRTIO_NET_F_MAC, virtio_ring::VIRTIO_RING_F_EVENT_IDX};
24+
use vm_memory::{ByteValued, GuestMemoryMmap};
2625

2726
const VIRTIO_F_VERSION_1: u32 = 32;
2827

29-
#[derive(Debug)]
30-
pub enum FrontendError {
31-
DescriptorChainTooSmall,
32-
EmptyQueue,
33-
GuestMemory(GuestMemoryError),
34-
QueueError(QueueError),
35-
ReadOnlyDescriptor,
36-
}
28+
// FrontendError removed - no longer used with vectored I/O
3729

3830
#[derive(Debug)]
3931
pub enum RxError {
4032
Backend(ReadError),
4133
DeviceError(DeviceError),
34+
QueueError(QueueError),
4235
}
4336

4437
#[derive(Debug)]

src/devices/src/virtio/net/mod.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub static QUEUE_CONFIG: [QueueConfig; NUM_QUEUES] = [QueueConfig::new(QUEUE_SIZ
1818

1919
mod backend;
2020
pub mod device;
21+
#[cfg(target_os = "macos")]
22+
mod socket_x;
2123
#[cfg(target_os = "linux")]
2224
mod tap;
2325
mod unixgram;
@@ -28,13 +30,10 @@ fn vnet_hdr_len() -> usize {
2830
mem::size_of::<virtio_net_hdr_v1>()
2931
}
3032

31-
// This initializes to all 0 the virtio_net_hdr part of a buf and return the length of the header
32-
// https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2050006
33-
fn write_virtio_net_hdr(buf: &mut [u8]) -> usize {
34-
let len = vnet_hdr_len();
35-
buf[0..len].fill(0);
36-
len
37-
}
33+
/// Default zeroed virtio_net_hdr_v1 (12 bytes) - used as prefix when receiving from backends
34+
/// that don't include vnet headers (e.g., passt/unixstream)
35+
/// https://docs.oasis-open.org/virtio/virtio/v1.1/csprd01/virtio-v1.1-csprd01.html#x1-2050006
36+
static DEFAULT_VNET_HDR: [u8; 12] = [0u8; 12];
3837

3938
pub use self::device::Net;
4039
#[derive(Debug)]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// macOS-specific batch message syscalls (sendmsg_x/recvmsg_x)
2+
//
3+
// These are private Apple APIs that allow sending/receiving multiple messages
4+
// in a single syscall, similar to Linux's sendmmsg/recvmmsg.
5+
//
6+
// Reference: https://github.com/nirs/vmnet-helper/blob/main/socket_x.h
7+
8+
#![allow(dead_code)]
9+
#![allow(non_camel_case_types)]
10+
11+
#[cfg(target_os = "macos")]
12+
pub mod macos {
13+
use libc::{c_int, c_uint, c_void, iovec, socklen_t};
14+
15+
/// Extended message header for batch operations.
16+
/// Similar to msghdr but includes msg_datalen for output.
17+
#[repr(C)]
18+
pub struct msghdr_x {
19+
pub msg_name: *mut c_void,
20+
pub msg_namelen: socklen_t,
21+
pub msg_iov: *mut iovec,
22+
pub msg_iovlen: c_int,
23+
pub msg_control: *mut c_void,
24+
pub msg_controllen: socklen_t,
25+
pub msg_flags: c_int,
26+
pub msg_datalen: usize, // out: bytes transferred for this message
27+
}
28+
29+
impl Default for msghdr_x {
30+
fn default() -> Self {
31+
Self {
32+
msg_name: std::ptr::null_mut(),
33+
msg_namelen: 0,
34+
msg_iov: std::ptr::null_mut(),
35+
msg_iovlen: 0,
36+
msg_control: std::ptr::null_mut(),
37+
msg_controllen: 0,
38+
msg_flags: 0,
39+
msg_datalen: 0,
40+
}
41+
}
42+
}
43+
44+
extern "C" {
45+
/// Send multiple datagrams in a single syscall.
46+
///
47+
/// # Arguments
48+
/// * `s` - Socket file descriptor
49+
/// * `msgp` - Pointer to array of msghdr_x structures
50+
/// * `cnt` - Number of messages to send
51+
/// * `flags` - Only MSG_DONTWAIT is supported
52+
///
53+
/// # Constraints
54+
/// For each msghdr_x: msg_name, msg_namelen, msg_control, msg_controllen,
55+
/// msg_flags, and msg_datalen must all be zero on input.
56+
///
57+
/// # Returns
58+
/// Number of datagrams sent, or -1 on error.
59+
/// Each msghdr_x.msg_datalen is set to bytes sent for that message.
60+
pub fn sendmsg_x(s: c_int, msgp: *const msghdr_x, cnt: c_uint, flags: c_int) -> isize;
61+
62+
/// Receive multiple datagrams in a single syscall.
63+
///
64+
/// # Arguments
65+
/// * `s` - Socket file descriptor
66+
/// * `msgp` - Pointer to array of msghdr_x structures
67+
/// * `cnt` - Maximum number of messages to receive
68+
/// * `flags` - Only MSG_DONTWAIT is supported
69+
///
70+
/// # Constraints
71+
/// For each msghdr_x: msg_flags must be zero on input.
72+
///
73+
/// # Returns
74+
/// Number of datagrams received (may be less than cnt), or -1 on error.
75+
/// Each msghdr_x.msg_datalen is set to bytes received for that message.
76+
pub fn recvmsg_x(s: c_int, msgp: *mut msghdr_x, cnt: c_uint, flags: c_int) -> isize;
77+
}
78+
}
79+
80+
#[cfg(target_os = "macos")]
81+
pub use macos::*;

0 commit comments

Comments
 (0)