virtio-devices, vmm: vhost: net: Add client mode support

Adding the support for an OVS vhost-user backend to connect as the
vhost-user client. This means we introduce with this patch a new
option to our `--net` parameter. This option is called 'server' in order
to ask the VMM to run as the server for the vhost-user socket.

Fixes #1745

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2021-05-04 10:33:35 +02:00
parent 656b9f97f9
commit b5c6b04b36
5 changed files with 80 additions and 7 deletions

View File

@ -24,10 +24,14 @@ pub use self::vu_common_ctrl::VhostUserConfig;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
/// Failed accepting connection.
AcceptConnection(io::Error),
/// Invalid available address. /// Invalid available address.
AvailAddress, AvailAddress,
/// Queue number is not correct /// Queue number is not correct
BadQueueNum, BadQueueNum,
/// Failed binding vhost-user socket.
BindSocket(io::Error),
/// Creating kill eventfd failed. /// Creating kill eventfd failed.
CreateKillEventFd(io::Error), CreateKillEventFd(io::Error),
/// Cloning kill eventfd failed. /// Cloning kill eventfd failed.

View File

@ -16,6 +16,7 @@ use net_util::MacAddr;
use seccomp::{SeccompAction, SeccompFilter}; use seccomp::{SeccompAction, SeccompFilter};
use std::ops::Deref; use std::ops::Deref;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::os::unix::net::UnixListener;
use std::result; use std::result;
use std::sync::{Arc, Barrier}; use std::sync::{Arc, Barrier};
use std::thread; use std::thread;
@ -46,6 +47,7 @@ pub struct Net {
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>, guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
acked_protocol_features: u64, acked_protocol_features: u64,
socket_path: Option<String>,
} }
impl Net { impl Net {
@ -56,9 +58,23 @@ impl Net {
mac_addr: MacAddr, mac_addr: MacAddr,
vu_cfg: VhostUserConfig, vu_cfg: VhostUserConfig,
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
server: bool,
) -> Result<Net> { ) -> Result<Net> {
let mut vhost_user_net = Master::connect(&vu_cfg.socket, vu_cfg.num_queues as u64) let mut socket_path: Option<String> = None;
.map_err(Error::VhostUserCreateMaster)?;
let mut vhost_user_net = if server {
info!("Binding vhost-user-net listener...");
let listener = UnixListener::bind(&vu_cfg.socket).map_err(Error::BindSocket)?;
info!("Waiting for incoming vhost-user-net connection...");
let (stream, _) = listener.accept().map_err(Error::AcceptConnection)?;
socket_path = Some(vu_cfg.socket.clone());
Master::from_stream(stream, vu_cfg.num_queues as u64)
} else {
Master::connect(&vu_cfg.socket, vu_cfg.num_queues as u64)
.map_err(Error::VhostUserCreateMaster)?
};
// Filling device and vring features VMM supports. // Filling device and vring features VMM supports.
let mut avail_features = 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM let mut avail_features = 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM
@ -166,6 +182,7 @@ impl Net {
seccomp_action, seccomp_action,
guest_memory: None, guest_memory: None,
acked_protocol_features, acked_protocol_features,
socket_path,
}) })
} }
} }
@ -368,6 +385,11 @@ impl VirtioDevice for Net {
fn shutdown(&mut self) { fn shutdown(&mut self) {
let _ = unsafe { libc::close(self.vhost_user_net.as_raw_fd()) }; let _ = unsafe { libc::close(self.vhost_user_net.as_raw_fd()) };
// Remove socket path if needed
if let Some(socket_path) = &self.socket_path {
let _ = std::fs::remove_file(socket_path);
}
} }
fn add_memory_region( fn add_memory_region(

View File

@ -712,6 +712,9 @@ components:
default: false default: false
vhost_socket: vhost_socket:
type: string type: string
vhost_mode:
type: string
default: "client"
id: id:
type: string type: string
fd: fd:

View File

@ -878,6 +878,35 @@ impl DiskConfig {
} }
} }
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum VhostMode {
Client,
Server,
}
impl Default for VhostMode {
fn default() -> Self {
VhostMode::Client
}
}
#[derive(Debug)]
pub enum ParseVhostModeError {
InvalidValue(String),
}
impl FromStr for VhostMode {
type Err = ParseVhostModeError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"client" => Ok(VhostMode::Client),
"server" => Ok(VhostMode::Server),
_ => Err(ParseVhostModeError::InvalidValue(s.to_owned())),
}
}
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct NetConfig { pub struct NetConfig {
#[serde(default = "default_netconfig_tap")] #[serde(default = "default_netconfig_tap")]
@ -900,6 +929,8 @@ pub struct NetConfig {
pub vhost_user: bool, pub vhost_user: bool,
pub vhost_socket: Option<String>, pub vhost_socket: Option<String>,
#[serde(default)] #[serde(default)]
pub vhost_mode: VhostMode,
#[serde(default)]
pub id: Option<String>, pub id: Option<String>,
#[serde(default)] #[serde(default)]
pub fds: Option<Vec<i32>>, pub fds: Option<Vec<i32>>,
@ -944,6 +975,7 @@ impl Default for NetConfig {
queue_size: default_netconfig_queue_size(), queue_size: default_netconfig_queue_size(),
vhost_user: false, vhost_user: false,
vhost_socket: None, vhost_socket: None,
vhost_mode: VhostMode::Client,
id: None, id: None,
fds: None, fds: None,
rate_limiter_config: None, rate_limiter_config: None,
@ -954,8 +986,8 @@ impl Default for NetConfig {
impl NetConfig { impl NetConfig {
pub const SYNTAX: &'static str = "Network parameters \ pub const SYNTAX: &'static str = "Network parameters \
\"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1:fd2...>,iommu=on|off,\ \"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1:fd2...>,iommu=on|off,\
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\ num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,\
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,id=<device_id>,\ vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,\
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\ bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>\""; ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>\"";
@ -973,6 +1005,7 @@ impl NetConfig {
.add("num_queues") .add("num_queues")
.add("vhost_user") .add("vhost_user")
.add("socket") .add("socket")
.add("vhost_mode")
.add("id") .add("id")
.add("fd") .add("fd")
.add("bw_size") .add("bw_size")
@ -1016,6 +1049,10 @@ impl NetConfig {
.unwrap_or(Toggle(false)) .unwrap_or(Toggle(false))
.0; .0;
let vhost_socket = parser.get("socket"); let vhost_socket = parser.get("socket");
let vhost_mode = parser
.convert("vhost_mode")
.map_err(Error::ParseNetwork)?
.unwrap_or_default();
let id = parser.get("id"); let id = parser.get("id");
let fds = parser let fds = parser
.convert::<IntegerList>("fd") .convert::<IntegerList>("fd")
@ -1084,6 +1121,7 @@ impl NetConfig {
queue_size, queue_size,
vhost_user, vhost_user,
vhost_socket, vhost_socket,
vhost_mode,
id, id,
fds, fds,
rate_limiter_config, rate_limiter_config,

View File

@ -9,9 +9,10 @@
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
// //
use crate::config::ConsoleOutputMode; use crate::config::{
use crate::config::DeviceConfig; ConsoleOutputMode, DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, VhostMode,
use crate::config::{DiskConfig, FsConfig, NetConfig, PmemConfig, VmConfig, VsockConfig}; VmConfig, VsockConfig,
};
use crate::device_tree::{DeviceNode, DeviceTree}; use crate::device_tree::{DeviceNode, DeviceTree};
#[cfg(feature = "kvm")] #[cfg(feature = "kvm")]
use crate::interrupt::kvm::KvmMsiInterruptManager as MsiInterruptManager; use crate::interrupt::kvm::KvmMsiInterruptManager as MsiInterruptManager;
@ -2035,12 +2036,17 @@ impl DeviceManager {
num_queues: net_cfg.num_queues, num_queues: net_cfg.num_queues,
queue_size: net_cfg.queue_size, queue_size: net_cfg.queue_size,
}; };
let server = match net_cfg.vhost_mode {
VhostMode::Client => false,
VhostMode::Server => true,
};
let vhost_user_net_device = Arc::new(Mutex::new( let vhost_user_net_device = Arc::new(Mutex::new(
match virtio_devices::vhost_user::Net::new( match virtio_devices::vhost_user::Net::new(
id.clone(), id.clone(),
net_cfg.mac, net_cfg.mac,
vu_cfg, vu_cfg,
self.seccomp_action.clone(), self.seccomp_action.clone(),
server,
) { ) {
Ok(vun_device) => vun_device, Ok(vun_device) => vun_device,
Err(e) => { Err(e) => {