mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-06 02:25:23 +00:00
devices: virtio: Add vhost-user-net master device
Add vhost-user virtio-net master device. Ctrl queue will be supported in a separate CL. BUG=b:179755448 TEST=curl/ping worked with cloud-hypervisor's backend Change-Id: Ibda3d93457be9841748b649e492d0fd11969fd4f Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2717904 Tested-by: Keiichi Watanabe <keiichiw@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Commit-Queue: Keiichi Watanabe <keiichiw@chromium.org> Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
This commit is contained in:
parent
f3a37f4953
commit
6068658584
6 changed files with 227 additions and 3 deletions
|
@ -134,7 +134,7 @@ fn virtio_features_to_tap_offload(features: u64) -> c_uint {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct VirtioNetConfig {
|
pub(crate) struct VirtioNetConfig {
|
||||||
mac: [u8; 6],
|
mac: [u8; 6],
|
||||||
status: Le16,
|
status: Le16,
|
||||||
max_vq_pairs: Le16,
|
max_vq_pairs: Le16,
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
|
|
||||||
mod block;
|
mod block;
|
||||||
mod handler;
|
mod handler;
|
||||||
|
mod net;
|
||||||
mod worker;
|
mod worker;
|
||||||
|
|
||||||
pub use self::block::*;
|
pub use self::block::*;
|
||||||
|
pub use self::net::*;
|
||||||
|
|
||||||
use remain::sorted;
|
use remain::sorted;
|
||||||
use thiserror::Error as ThisError;
|
use thiserror::Error as ThisError;
|
||||||
|
|
185
devices/src/virtio/vhost/user/net.rs
Normal file
185
devices/src/virtio/vhost/user/net.rs
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
// Copyright 2021 The Chromium OS Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::thread;
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
use base::{error, Event, RawDescriptor};
|
||||||
|
use cros_async::Executor;
|
||||||
|
use virtio_sys::virtio_net;
|
||||||
|
use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||||
|
use vm_memory::GuestMemory;
|
||||||
|
use vmm_vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
|
||||||
|
use vmm_vhost::vhost_user::Master;
|
||||||
|
|
||||||
|
use crate::virtio::vhost::user::handler::VhostUserHandler;
|
||||||
|
use crate::virtio::vhost::user::worker::Worker;
|
||||||
|
use crate::virtio::vhost::user::Error;
|
||||||
|
use crate::virtio::{Interrupt, Queue, VirtioDevice, VirtioNetConfig, TYPE_NET};
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
const QUEUE_SIZE: u16 = 256;
|
||||||
|
|
||||||
|
pub struct Net {
|
||||||
|
kill_evt: Option<Event>,
|
||||||
|
worker_thread: Option<thread::JoinHandle<Worker>>,
|
||||||
|
handler: RefCell<VhostUserHandler>,
|
||||||
|
queue_sizes: Vec<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Net {
|
||||||
|
pub fn new<P: AsRef<Path>>(base_features: u64, socket_path: P) -> Result<Net> {
|
||||||
|
let socket = UnixStream::connect(&socket_path).map_err(Error::SocketConnect)?;
|
||||||
|
let vhost_user_net = Master::from_stream(socket, 16 /* # of queues */);
|
||||||
|
|
||||||
|
// TODO(b/182430355): Support VIRTIO_NET_F_CTRL_VQ and VIRTIO_NET_F_CTRL_GUEST_OFFLOADS.
|
||||||
|
let allow_features = 1 << crate::virtio::VIRTIO_F_VERSION_1
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_CSUM
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_GUEST_UFO
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_HOST_TSO4
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_HOST_UFO
|
||||||
|
| 1 << virtio_net::VIRTIO_NET_F_MQ
|
||||||
|
| 1 << VIRTIO_RING_F_EVENT_IDX
|
||||||
|
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
|
||||||
|
let init_features = base_features | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
|
||||||
|
let allow_protocol_features =
|
||||||
|
VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIG;
|
||||||
|
|
||||||
|
let mut handler = VhostUserHandler::new(
|
||||||
|
vhost_user_net,
|
||||||
|
allow_features,
|
||||||
|
init_features,
|
||||||
|
allow_protocol_features,
|
||||||
|
)?;
|
||||||
|
let queue_sizes = handler.queue_sizes(QUEUE_SIZE, 2 /* 1 rx + 1 tx */)?;
|
||||||
|
|
||||||
|
Ok(Net {
|
||||||
|
kill_evt: None,
|
||||||
|
worker_thread: None,
|
||||||
|
handler: RefCell::new(handler),
|
||||||
|
queue_sizes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Net {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
|
// Ignore the result because there is nothing we can do about it.
|
||||||
|
let _ = kill_evt.write(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(worker_thread) = self.worker_thread.take() {
|
||||||
|
let _ = worker_thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtioDevice for Net {
|
||||||
|
fn keep_rds(&self) -> Vec<RawDescriptor> {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn features(&self) -> u64 {
|
||||||
|
self.handler.borrow().avail_features
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ack_features(&mut self, features: u64) {
|
||||||
|
if let Err(e) = self.handler.borrow_mut().ack_features(features) {
|
||||||
|
error!("failed to enable features 0x{:x}: {}", features, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_type(&self) -> u32 {
|
||||||
|
TYPE_NET
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue_max_sizes(&self) -> &[u16] {
|
||||||
|
self.queue_sizes.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_config(&self, offset: u64, data: &mut [u8]) {
|
||||||
|
if let Err(e) = self
|
||||||
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.read_config::<VirtioNetConfig>(offset, data)
|
||||||
|
{
|
||||||
|
error!("failed to read config: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate(
|
||||||
|
&mut self,
|
||||||
|
mem: GuestMemory,
|
||||||
|
interrupt: Interrupt,
|
||||||
|
queues: Vec<Queue>,
|
||||||
|
queue_evts: Vec<Event>,
|
||||||
|
) {
|
||||||
|
// TODO(b/182430355): Remove this check once ctrlq is supported.
|
||||||
|
if queues.len() % 2 != 0 {
|
||||||
|
error!(
|
||||||
|
"The number of queues must be an even number but {}",
|
||||||
|
queues.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = self
|
||||||
|
.handler
|
||||||
|
.borrow_mut()
|
||||||
|
.activate(&mem, &interrupt, &queues, &queue_evts)
|
||||||
|
{
|
||||||
|
error!("failed to activate queues: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (self_kill_evt, kill_evt) = match Event::new().and_then(|e| Ok((e.try_clone()?, e))) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed creating kill Event pair: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.kill_evt = Some(self_kill_evt);
|
||||||
|
|
||||||
|
let worker_result = thread::Builder::new()
|
||||||
|
.name("vhost_user_virtio_net".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
let ex = Executor::new().expect("failed to create an executor");
|
||||||
|
let mut worker = Worker {
|
||||||
|
queues,
|
||||||
|
mem,
|
||||||
|
kill_evt,
|
||||||
|
};
|
||||||
|
if let Err(e) = worker.run(&ex, interrupt) {
|
||||||
|
error!("failed to start a worker: {}", e);
|
||||||
|
}
|
||||||
|
worker
|
||||||
|
});
|
||||||
|
|
||||||
|
match worker_result {
|
||||||
|
Err(e) => {
|
||||||
|
error!("failed to spawn virtio_net worker: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(join_handle) => {
|
||||||
|
self.worker_thread = Some(join_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> bool {
|
||||||
|
if let Err(e) = self.handler.borrow_mut().reset(self.queue_sizes.len()) {
|
||||||
|
error!("Failed to reset net device: {}", e);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -241,6 +241,7 @@ pub struct Config {
|
||||||
pub gdb: Option<u32>,
|
pub gdb: Option<u32>,
|
||||||
pub balloon_bias: i64,
|
pub balloon_bias: i64,
|
||||||
pub vhost_user_blk: Vec<VhostUserOption>,
|
pub vhost_user_blk: Vec<VhostUserOption>,
|
||||||
|
pub vhost_user_net: Vec<VhostUserOption>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
@ -306,6 +307,7 @@ impl Default for Config {
|
||||||
gdb: None,
|
gdb: None,
|
||||||
balloon_bias: 0,
|
balloon_bias: 0,
|
||||||
vhost_user_blk: Vec::new(),
|
vhost_user_blk: Vec::new(),
|
||||||
|
vhost_user_net: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
35
src/linux.rs
35
src/linux.rs
|
@ -33,7 +33,9 @@ use libc::{self, c_int, gid_t, uid_t};
|
||||||
use acpi_tables::sdt::SDT;
|
use acpi_tables::sdt::SDT;
|
||||||
|
|
||||||
use base::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
|
use base::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
|
||||||
use devices::virtio::vhost::user::{Block as VhostUserBlock, Error as VhostUserBlockError};
|
use devices::virtio::vhost::user::{
|
||||||
|
Block as VhostUserBlock, Error as VhostUserError, Net as VhostUserNet,
|
||||||
|
};
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
use devices::virtio::EventDevice;
|
use devices::virtio::EventDevice;
|
||||||
use devices::virtio::{self, Console, VirtioDevice};
|
use devices::virtio::{self, Console, VirtioDevice};
|
||||||
|
@ -192,7 +194,9 @@ pub enum Error {
|
||||||
Timer(base::Error),
|
Timer(base::Error),
|
||||||
ValidateRawDescriptor(base::Error),
|
ValidateRawDescriptor(base::Error),
|
||||||
VhostNetDeviceNew(virtio::vhost::Error),
|
VhostNetDeviceNew(virtio::vhost::Error),
|
||||||
VhostUserBlockDeviceNew(VhostUserBlockError),
|
VhostUserBlockDeviceNew(VhostUserError),
|
||||||
|
VhostUserNetDeviceNew(VhostUserError),
|
||||||
|
VhostUserNetWithNetArgs,
|
||||||
VhostVsockDeviceNew(virtio::vhost::Error),
|
VhostVsockDeviceNew(virtio::vhost::Error),
|
||||||
VirtioPciDev(base::Error),
|
VirtioPciDev(base::Error),
|
||||||
WaitContextAdd(base::Error),
|
WaitContextAdd(base::Error),
|
||||||
|
@ -316,6 +320,15 @@ impl Display for Error {
|
||||||
VhostUserBlockDeviceNew(e) => {
|
VhostUserBlockDeviceNew(e) => {
|
||||||
write!(f, "failed to set up vhost-user block device: {}", e)
|
write!(f, "failed to set up vhost-user block device: {}", e)
|
||||||
}
|
}
|
||||||
|
VhostUserNetDeviceNew(e) => {
|
||||||
|
write!(f, "failed to set up vhost-user net device: {}", e)
|
||||||
|
}
|
||||||
|
VhostUserNetWithNetArgs => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"vhost-user-net cannot be used with any of --host_ip, --netmask or --mac"
|
||||||
|
)
|
||||||
|
}
|
||||||
VhostVsockDeviceNew(e) => write!(f, "failed to set up virtual socket device: {}", e),
|
VhostVsockDeviceNew(e) => write!(f, "failed to set up virtual socket device: {}", e),
|
||||||
VirtioPciDev(e) => write!(f, "failed to create virtio pci dev: {}", e),
|
VirtioPciDev(e) => write!(f, "failed to create virtio pci dev: {}", e),
|
||||||
WaitContextAdd(e) => write!(f, "failed to add descriptor to wait context: {}", e),
|
WaitContextAdd(e) => write!(f, "failed to add descriptor to wait context: {}", e),
|
||||||
|
@ -820,6 +833,17 @@ fn create_net_device(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_vhost_user_net_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult {
|
||||||
|
let dev = VhostUserNet::new(virtio::base_features(cfg.protected_vm), &opt.socket)
|
||||||
|
.map_err(Error::VhostUserNetDeviceNew)?;
|
||||||
|
|
||||||
|
Ok(VirtioDeviceStub {
|
||||||
|
dev: Box::new(dev),
|
||||||
|
// no sandbox here because virtqueue handling is exported to a different process.
|
||||||
|
jail: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
fn create_gpu_device(
|
fn create_gpu_device(
|
||||||
cfg: &Config,
|
cfg: &Config,
|
||||||
|
@ -1425,9 +1449,16 @@ fn create_virtio_devices(
|
||||||
if let (Some(host_ip), Some(netmask), Some(mac_address)) =
|
if let (Some(host_ip), Some(netmask), Some(mac_address)) =
|
||||||
(cfg.host_ip, cfg.netmask, cfg.mac_address)
|
(cfg.host_ip, cfg.netmask, cfg.mac_address)
|
||||||
{
|
{
|
||||||
|
if !cfg.vhost_user_net.is_empty() {
|
||||||
|
return Err(Error::VhostUserNetWithNetArgs);
|
||||||
|
}
|
||||||
devs.push(create_net_device(cfg, host_ip, netmask, mac_address, mem)?);
|
devs.push(create_net_device(cfg, host_ip, netmask, mac_address, mem)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for net in &cfg.vhost_user_net {
|
||||||
|
devs.push(create_vhost_user_net_device(cfg, net)?);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
|
#[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
|
||||||
let mut resource_bridges = Vec::<virtio::resource_bridge::ResourceResponseSocket>::new();
|
let mut resource_bridges = Vec::<virtio::resource_bridge::ResourceResponseSocket>::new();
|
||||||
|
|
||||||
|
|
|
@ -1588,6 +1588,9 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
||||||
"vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
|
"vhost-user-blk" => cfg.vhost_user_blk.push(VhostUserOption {
|
||||||
socket: PathBuf::from(value.unwrap()),
|
socket: PathBuf::from(value.unwrap()),
|
||||||
}),
|
}),
|
||||||
|
"vhost-user-net" => cfg.vhost_user_net.push(VhostUserOption {
|
||||||
|
socket: PathBuf::from(value.unwrap()),
|
||||||
|
}),
|
||||||
"help" => return Err(argument::Error::PrintHelp),
|
"help" => return Err(argument::Error::PrintHelp),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1798,6 +1801,7 @@ writeback=BOOL - Indicates whether the VM can use writeback caching (default: fa
|
||||||
Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
|
Argument::value("gdb", "PORT", "(EXPERIMENTAL) gdb on the given port"),
|
||||||
Argument::value("balloon_bias_mib", "N", "Amount to bias balance of memory between host and guest as the balloon inflates, in MiB."),
|
Argument::value("balloon_bias_mib", "N", "Amount to bias balance of memory between host and guest as the balloon inflates, in MiB."),
|
||||||
Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
|
Argument::value("vhost-user-blk", "SOCKET_PATH", "Path to a socket for vhost-user block"),
|
||||||
|
Argument::value("vhost-user-net", "SOCKET_PATH", "Path to a socket for vhost-user net"),
|
||||||
Argument::short_flag('h', "help", "Print help message.")];
|
Argument::short_flag('h', "help", "Print help message.")];
|
||||||
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
|
|
Loading…
Reference in a new issue