Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

virtio/net: implement gvproxy backend #169

Merged
merged 2 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/libkrun.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ int32_t krun_add_virtiofs(uint32_t ctx_id,
*/
int32_t krun_set_passt_fd(uint32_t ctx_id, int fd);

/*
* Configures the networking to use gvproxy in vfkit mode.
* Call to this function disables TSI backend to use gvproxy instead.
*
* Arguments:
* "ctx_id" - the configuration context ID.
* "c_path" - a null-terminated string representing the path for
* gvproxy's listen-vfkit unixdgram socket.
*
* Notes:
* If you never call this function, networking uses the TSI backend.
* This function should be called before krun_set_port_map.
*
* Returns:
* Zero on success or a negative error number on failure.
*/
int32_t krun_set_gvproxy_path(uint32_t ctx_id, char* c_path);

/*
* Sets the MAC address for the virtio-net device when using the passt backend.
*
Expand Down
37 changes: 37 additions & 0 deletions src/devices/src/virtio/net/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use std::os::fd::RawFd;

#[derive(Debug)]
pub enum ConnectError {
InvalidAddress(nix::Error),
CreateSocket(nix::Error),
Binding(nix::Error),
SendingMagic(nix::Error),
}

#[derive(Debug)]
pub enum ReadError {
/// Nothing was written
NothingRead,
/// Another internal error occurred
Internal(nix::Error),
}

#[derive(Debug)]
pub enum WriteError {
/// Nothing was written, you can drop the frame or try to resend it later
NothingWritten,
/// Part of the buffer was written, the write has to be finished using try_finish_write
PartialWrite,
/// Passt doesnt seem to be running (received EPIPE)
ProcessNotRunning,
/// Another internal error occurred
Internal(nix::Error),
}

pub trait NetBackend {
fn read_frame(&mut self, buf: &mut [u8]) -> Result<usize, ReadError>;
fn write_frame(&mut self, hdr_len: usize, buf: &mut [u8]) -> Result<(), WriteError>;
fn has_unfinished_write(&self) -> bool;
fn try_finish_write(&mut self, hdr_len: usize, buf: &[u8]) -> Result<(), WriteError>;
fn raw_socket_fd(&self) -> RawFd;
}
88 changes: 52 additions & 36 deletions src/devices/src/virtio/net/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
use crate::legacy::Gic;
use crate::virtio::net::gvproxy::Gvproxy;
use crate::virtio::net::passt::Passt;
use crate::virtio::net::{passt, MAX_BUFFER_SIZE, QUEUE_SIZE, QUEUE_SIZES, RX_INDEX, TX_INDEX};
use crate::virtio::net::{Error, Result};
use crate::virtio::net::{MAX_BUFFER_SIZE, QUEUE_SIZE, QUEUE_SIZES, RX_INDEX, TX_INDEX};
use crate::virtio::{
ActivateResult, DeviceState, Queue, VirtioDevice, TYPE_NET, VIRTIO_MMIO_INT_VRING,
};
use crate::Error as DeviceError;

use super::backend::{NetBackend, ReadError, WriteError};

use std::io::Write;
use std::os::fd::RawFd;
use std::path::PathBuf;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
Expand All @@ -37,13 +42,13 @@ enum FrontendError {

#[derive(Debug)]
enum RxError {
Passt(passt::ReadError),
Backend(ReadError),
DeviceError(DeviceError),
}

#[derive(Debug)]
enum TxError {
Passt(passt::WriteError),
Backend(WriteError),
DeviceError(DeviceError),
}

Expand All @@ -58,6 +63,11 @@ struct VirtioNetConfig {
// Safe because it only has data and has no implicit padding.
unsafe impl ByteValued for VirtioNetConfig {}

pub enum VirtioNetBackend {
Passt(RawFd),
Gvproxy(PathBuf),
}

pub(crate) fn vnet_hdr_len() -> usize {
mem::size_of::<virtio_net_hdr_v1>()
}
Expand All @@ -72,7 +82,7 @@ fn write_virtio_net_hdr(buf: &mut [u8]) -> usize {

pub struct Net {
id: String,
passt: Passt,
backend: Box<dyn NetBackend + Send>,

avail_features: u64,
acked_features: u64,
Expand Down Expand Up @@ -101,9 +111,15 @@ pub struct Net {
}

impl Net {
/// Create a new virtio network device using passt
pub fn new(id: String, passt_fd: RawFd, mac: [u8; 6]) -> Result<Self> {
let passt = Passt::new(passt_fd);
/// Create a new virtio network device using the backend
pub fn new(id: String, backend: VirtioNetBackend, mac: [u8; 6]) -> Result<Self> {
let backend = match backend {
VirtioNetBackend::Passt(fd) => Box::new(Passt::new(fd)) as Box<dyn NetBackend + Send>,
VirtioNetBackend::Gvproxy(path) => {
Box::new(Gvproxy::new(path).unwrap()) as Box<dyn NetBackend + Send>
}
};

let avail_features = 1 << VIRTIO_NET_F_GUEST_CSUM
| 1 << VIRTIO_NET_F_CSUM
| 1 << VIRTIO_NET_F_GUEST_TSO4
Expand All @@ -128,7 +144,7 @@ impl Net {

Ok(Net {
id,
passt,
backend,

avail_features,
acked_features: 0u64,
Expand Down Expand Up @@ -188,34 +204,34 @@ impl Net {
}
}

pub(crate) fn process_passt_socket_readable(&mut self) {
pub(crate) fn process_backend_socket_readable(&mut self) {
if let Err(e) = self.process_rx() {
log::error!("Failed to process rx: {e:?} (triggered by passt socket readable)");
log::error!("Failed to process rx: {e:?} (triggered by backend socket readable)");
};
}

pub(crate) fn process_passt_socket_writeable(&mut self) {
pub(crate) fn process_backend_socket_writeable(&mut self) {
match self
.passt
.backend
.try_finish_write(vnet_hdr_len(), &self.tx_frame_buf[..self.tx_frame_len])
{
Ok(()) => {
if let Err(e) = self.process_tx() {
log::error!("Failed to continue processing tx after passt socket was writable again: {e:?}");
log::error!("Failed to continue processing tx after backend socket was writable again: {e:?}");
}
}
Err(passt::WriteError::PartialWrite | passt::WriteError::NothingWritten) => {}
Err(e @ passt::WriteError::Internal(_)) => {
Err(WriteError::PartialWrite | WriteError::NothingWritten) => {}
Err(e @ WriteError::Internal(_)) => {
log::error!("Failed to finish write: {e:?}");
}
Err(e @ passt::WriteError::ProcessNotRunning) => {
Err(e @ WriteError::ProcessNotRunning) => {
log::debug!("Failed to finish write: {e:?}");
}
}
}

pub(crate) fn raw_passt_socket_fd(&self) -> RawFd {
self.passt.raw_socket_fd()
pub(crate) fn raw_backend_socket_fd(&self) -> RawFd {
self.backend.raw_socket_fd()
}

fn process_rx(&mut self) -> result::Result<(), RxError> {
Expand All @@ -233,7 +249,7 @@ impl Net {

// Read as many frames as possible.
let result = loop {
match self.read_into_rx_frame_buf_from_passt() {
match self.read_into_rx_frame_buf_from_backend() {
Ok(()) => {
if self.write_frame_to_guest() {
signal_queue = true;
Expand All @@ -242,8 +258,8 @@ impl Net {
break Ok(());
}
}
Err(passt::ReadError::NothingRead) => break Ok(()),
Err(e @ passt::ReadError::Internal(_)) => break Err(RxError::Passt(e)),
Err(ReadError::NothingRead) => break Ok(()),
Err(e @ ReadError::Internal(_)) => break Err(RxError::Backend(e)),
}
};

Expand All @@ -265,9 +281,9 @@ impl Net {

let tx_queue = &mut self.queues[TX_INDEX];

if self.passt.has_unfinished_write()
if self.backend.has_unfinished_write()
&& self
.passt
.backend
.try_finish_write(vnet_hdr_len(), &self.tx_frame_buf[..self.tx_frame_len])
.is_err()
{
Expand Down Expand Up @@ -314,38 +330,38 @@ impl Net {

self.tx_frame_len = read_count;
match self
.passt
.backend
.write_frame(vnet_hdr_len(), &mut self.tx_frame_buf[..read_count])
{
Ok(()) => {
self.tx_frame_len = 0;
tx_queue.add_used(mem, head_index, 0);
raise_irq = true;
}
Err(passt::WriteError::NothingWritten) => {
Err(WriteError::NothingWritten) => {
tx_queue.undo_pop();
break;
}
Err(passt::WriteError::PartialWrite) => {
Err(WriteError::PartialWrite) => {
log::trace!("process_tx: partial write");
/*
This situation should be pretty rare, assuming reasonably sized socket buffers.
We have written only a part of a frame to the passt socket (the socket is full).
We have written only a part of a frame to the backend socket (the socket is full).

The frame we have read from the guest remains in tx_frame_buf, and will be sent
later.

Note that we cannot wait for passt to process our sending frames, because passt
could be blocked on sending a remainder of a frame to us - us waiting for passt
would cause a deadlock.
Note that we cannot wait for the backend to process our sending frames, because
the backend could be blocked on sending a remainder of a frame to us - us waiting
for backend would cause a deadlock.
*/
tx_queue.add_used(mem, head_index, 0);
raise_irq = true;
break;
}
Err(
e @ passt::WriteError::Internal(_) | e @ passt::WriteError::ProcessNotRunning,
) => return Err(TxError::Passt(e)),
Err(e @ WriteError::Internal(_) | e @ WriteError::ProcessNotRunning) => {
return Err(TxError::Backend(e))
}
}
}

Expand Down Expand Up @@ -443,11 +459,11 @@ impl Net {
false
}

/// Fills self.rx_frame_buf with an ethernet frame from passt and prepends virtio_net_hdr to it
fn read_into_rx_frame_buf_from_passt(&mut self) -> result::Result<(), passt::ReadError> {
/// Fills self.rx_frame_buf with an ethernet frame from backend and prepends virtio_net_hdr to it
fn read_into_rx_frame_buf_from_backend(&mut self) -> result::Result<(), ReadError> {
let mut len = 0;
len += write_virtio_net_hdr(&mut self.rx_frame_buf);
len += self.passt.read_frame(&mut self.rx_frame_buf[len..])?;
len += self.backend.read_frame(&mut self.rx_frame_buf[len..])?;
self.rx_frame_buf_len = len;
Ok(())
}
Expand Down
16 changes: 9 additions & 7 deletions src/devices/src/virtio/net/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl Subscriber for Net {
if self.is_activated() {
let virtq_rx_ev_fd = self.queue_evts[RX_INDEX].as_raw_fd();
let virtq_tx_ev_fd = self.queue_evts[TX_INDEX].as_raw_fd();
let passt_socket = self.raw_passt_socket_fd();
let backend_socket = self.raw_backend_socket_fd();
let activate_fd = self.activate_evt.as_raw_fd();

match event_set {
Expand All @@ -64,19 +64,21 @@ impl Subscriber for Net {
EventSet::IN if source == virtq_tx_ev_fd => {
self.process_tx_queue_event();
}
_ if source == passt_socket => {
_ if source == backend_socket => {
if event_set.contains(EventSet::HANG_UP)
|| event_set.contains(EventSet::READ_HANG_UP)
{
log::error!("Got {event_set:?} on passt fd, virtio-net will stop working");
eprintln!("LIBKRUN VIRTIO-NET FATAL: Passt process seems to have quit or crashed! Networking is now disabled!");
log::error!(
"Got {event_set:?} on backend fd, virtio-net will stop working"
);
eprintln!("LIBKRUN VIRTIO-NET FATAL: Backend process seems to have quit or crashed! Networking is now disabled!");
} else {
if event_set.contains(EventSet::IN) {
self.process_passt_socket_readable()
self.process_backend_socket_readable()
}

if event_set.contains(EventSet::OUT) {
self.process_passt_socket_writeable()
self.process_backend_socket_writeable()
}
}
}
Expand Down Expand Up @@ -106,7 +108,7 @@ impl Subscriber for Net {
| EventSet::OUT
| EventSet::EDGE_TRIGGERED
| EventSet::READ_HANG_UP,
self.raw_passt_socket_fd() as u64,
self.raw_backend_socket_fd() as u64,
),
]
} else {
Expand Down
Loading
Loading