From b5c6b04b360b4a215751f966a176561340d4f8e0 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 4 May 2021 10:33:35 +0200 Subject: [PATCH] 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 --- virtio-devices/src/vhost_user/mod.rs | 4 +++ virtio-devices/src/vhost_user/net.rs | 26 ++++++++++++-- vmm/src/api/openapi/cloud-hypervisor.yaml | 3 ++ vmm/src/config.rs | 42 +++++++++++++++++++++-- vmm/src/device_manager.rs | 12 +++++-- 5 files changed, 80 insertions(+), 7 deletions(-) diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index 2851c3579..343a19a39 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -24,10 +24,14 @@ pub use self::vu_common_ctrl::VhostUserConfig; #[derive(Debug)] pub enum Error { + /// Failed accepting connection. + AcceptConnection(io::Error), /// Invalid available address. AvailAddress, /// Queue number is not correct BadQueueNum, + /// Failed binding vhost-user socket. + BindSocket(io::Error), /// Creating kill eventfd failed. CreateKillEventFd(io::Error), /// Cloning kill eventfd failed. diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index d76d15543..77c9b37f2 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -16,6 +16,7 @@ use net_util::MacAddr; use seccomp::{SeccompAction, SeccompFilter}; use std::ops::Deref; use std::os::unix::io::AsRawFd; +use std::os::unix::net::UnixListener; use std::result; use std::sync::{Arc, Barrier}; use std::thread; @@ -46,6 +47,7 @@ pub struct Net { seccomp_action: SeccompAction, guest_memory: Option>, acked_protocol_features: u64, + socket_path: Option, } impl Net { @@ -56,9 +58,23 @@ impl Net { mac_addr: MacAddr, vu_cfg: VhostUserConfig, seccomp_action: SeccompAction, + server: bool, ) -> Result { - let mut vhost_user_net = Master::connect(&vu_cfg.socket, vu_cfg.num_queues as u64) - .map_err(Error::VhostUserCreateMaster)?; + let mut socket_path: Option = None; + + 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. let mut avail_features = 1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM @@ -166,6 +182,7 @@ impl Net { seccomp_action, guest_memory: None, acked_protocol_features, + socket_path, }) } } @@ -368,6 +385,11 @@ impl VirtioDevice for Net { fn shutdown(&mut self) { 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( diff --git a/vmm/src/api/openapi/cloud-hypervisor.yaml b/vmm/src/api/openapi/cloud-hypervisor.yaml index 224054664..49ddf3fbf 100644 --- a/vmm/src/api/openapi/cloud-hypervisor.yaml +++ b/vmm/src/api/openapi/cloud-hypervisor.yaml @@ -712,6 +712,9 @@ components: default: false vhost_socket: type: string + vhost_mode: + type: string + default: "client" id: type: string fd: diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 5d2d84eb0..bdfb7ac40 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -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 { + 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)] pub struct NetConfig { #[serde(default = "default_netconfig_tap")] @@ -900,6 +929,8 @@ pub struct NetConfig { pub vhost_user: bool, pub vhost_socket: Option, #[serde(default)] + pub vhost_mode: VhostMode, + #[serde(default)] pub id: Option, #[serde(default)] pub fds: Option>, @@ -944,6 +975,7 @@ impl Default for NetConfig { queue_size: default_netconfig_queue_size(), vhost_user: false, vhost_socket: None, + vhost_mode: VhostMode::Client, id: None, fds: None, rate_limiter_config: None, @@ -954,8 +986,8 @@ impl Default for NetConfig { impl NetConfig { pub const SYNTAX: &'static str = "Network parameters \ \"tap=,ip=,mask=,mac=,fd=,iommu=on|off,\ - num_queues=,queue_size=,\ - vhost_user=,socket=,id=,\ + num_queues=,queue_size=,id=,\ + vhost_user=,socket=,vhost_mode=client|server,\ bw_size=,bw_one_time_burst=,bw_refill_time=,\ ops_size=,ops_one_time_burst=,ops_refill_time=\""; @@ -973,6 +1005,7 @@ impl NetConfig { .add("num_queues") .add("vhost_user") .add("socket") + .add("vhost_mode") .add("id") .add("fd") .add("bw_size") @@ -1016,6 +1049,10 @@ impl NetConfig { .unwrap_or(Toggle(false)) .0; 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 fds = parser .convert::("fd") @@ -1084,6 +1121,7 @@ impl NetConfig { queue_size, vhost_user, vhost_socket, + vhost_mode, id, fds, rate_limiter_config, diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 48f66c3f6..6aa90b0ac 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -9,9 +9,10 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause // -use crate::config::ConsoleOutputMode; -use crate::config::DeviceConfig; -use crate::config::{DiskConfig, FsConfig, NetConfig, PmemConfig, VmConfig, VsockConfig}; +use crate::config::{ + ConsoleOutputMode, DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, VhostMode, + VmConfig, VsockConfig, +}; use crate::device_tree::{DeviceNode, DeviceTree}; #[cfg(feature = "kvm")] use crate::interrupt::kvm::KvmMsiInterruptManager as MsiInterruptManager; @@ -2035,12 +2036,17 @@ impl DeviceManager { num_queues: net_cfg.num_queues, 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( match virtio_devices::vhost_user::Net::new( id.clone(), net_cfg.mac, vu_cfg, self.seccomp_action.clone(), + server, ) { Ok(vun_device) => vun_device, Err(e) => {