vsock: Implement yaml config to support multiple vms setup

This commit aims to allow the vhost-user-vsock to
support local yaml configuration.

It introduces a new parameter '--config <CONFIG>' to allow user
to input a yaml configuration during startup and
uses config-rs to parse it.

Note that the configuration is currently made
conflicted to the original input parameters.

It introduces a new error -- ConfigParse inside the
crates/vsock/src/vhu_vsock.rs to support runtime error handling
and the new test_vsock_config_from_file() test.

It includes a new README.md with a parameter specification
and a config example in the Usage section.

It also introduces serde_deserialize(yaml) for VsockParam to let
config-rs directly pack the field specified in the array into the
VsockParam as suggested in config-rs. The serde crate is added to
crates/vsock/Cargo.toml correspondingly.

This commit also changes the original #[clap] into #[arg]
as suggested in clap-v4.

Signed-off-by: Yiyang Wu <toolmanp@outlook.com>
This commit is contained in:
Yiyang Wu 2023-05-04 22:15:23 +08:00 committed by Viresh Kumar
parent 6edcd04c1d
commit 500f617b24
5 changed files with 426 additions and 23 deletions

324
Cargo.lock generated
View File

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
version = "1.0.1"
@ -66,12 +77,29 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
[[package]]
name = "async-trait"
version = "0.1.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bindgen"
version = "0.63.0"
@ -100,6 +128,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "byteorder"
version = "1.4.3"
@ -186,6 +223,44 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "config"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7"
dependencies = [
"async-trait",
"json5",
"lazy_static",
"nom",
"pathdiff",
"ron",
"rust-ini",
"serde",
"serde_json",
"toml",
"yaml-rust",
]
[[package]]
name = "cpufeatures"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "dashmap"
version = "5.4.0"
@ -199,6 +274,22 @@ dependencies = [
"parking_lot_core",
]
[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dlv-list"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
[[package]]
name = "either"
version = "1.8.1"
@ -359,6 +450,16 @@ dependencies = [
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.9"
@ -381,6 +482,9 @@ name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "heck"
@ -409,6 +513,16 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -447,6 +561,23 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "json5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
dependencies = [
"pest",
"pest_derive",
"serde",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -496,6 +627,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.3.4"
@ -559,6 +696,16 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "ordered-multimap"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
dependencies = [
"dlv-list",
"hashbrown",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -582,12 +729,62 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "pathdiff"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "pest"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]]
name = "pest_meta"
version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
@ -689,6 +886,27 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
[[package]]
name = "ron"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
dependencies = [
"base64",
"bitflags",
"serde",
]
[[package]]
name = "rust-ini"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
dependencies = [
"cfg-if",
"ordered-multimap",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -709,12 +927,62 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.160"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]]
name = "serde_json"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "serial_test"
version = "1.0.0"
@ -740,6 +1008,17 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "1.1.0"
@ -831,18 +1110,51 @@ dependencies = [
"syn 2.0.13",
]
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ucd-trie"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unsafe-libyaml"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vhost"
version = "0.6.0"
@ -931,10 +1243,13 @@ version = "0.1.0"
dependencies = [
"byteorder",
"clap",
"config",
"env_logger",
"epoll",
"futures",
"log",
"serde",
"serde_yaml",
"serial_test",
"thiserror",
"vhost",
@ -1181,3 +1496,12 @@ name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -24,6 +24,9 @@ virtio-queue = "0.7"
virtio-vsock = "0.2.1"
vm-memory = "0.10"
vmm-sys-util = "0.11"
config = "0.13"
serde = "1"
serde_yaml = "0.9"
[dev-dependencies]
virtio-queue = { version = "0.7", features = ["test-utils"] }

View File

@ -44,6 +44,17 @@ vhost-user-vsock --guest-cid=<CID assigned to the guest> \
--socket=<path to the Unix socket to be created to communicate with the VMM via the vhost-user protocol>
--uds-path=<path to the Unix socket to communicate with the guest via the virtio-vsock device>
[--tx-buffer-size=<size of the buffer used for the TX virtqueue (guest->host packets)>]
--config=<path to the local yaml configuration file>
```
Configuration Example
```yaml
vms:
- guest_cid: 3
socket: /tmp/vhost3.socket
uds_path: /tmp/vm3.sock
tx_buffer_size: 65536
```
Run VMM (e.g. QEMU):

View File

@ -10,47 +10,79 @@ mod vsock_conn;
use std::{convert::TryFrom, sync::Arc};
use clap::Parser;
use crate::vhu_vsock::{Error, Result, VhostUserVsockBackend, VsockConfig};
use clap::{Args, Parser};
use log::{info, warn};
use serde::Deserialize;
use vhost::{vhost_user, vhost_user::Listener};
use vhost_user_backend::VhostUserDaemon;
use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap};
use crate::vhu_vsock::{Error, Result, VhostUserVsockBackend, VsockConfig};
#[derive(Parser, Debug)]
#[clap(version, about, long_about = None)]
struct VsockArgs {
#[derive(Args, Debug, Deserialize)]
struct VsockParam {
/// Context identifier of the guest which uniquely identifies the device for its lifetime.
#[clap(long, default_value_t = 3)]
#[arg(long, default_value_t = 3, conflicts_with = "config")]
guest_cid: u64,
/// Unix socket to which a hypervisor connects to and sets up the control path with the device.
#[clap(long)]
#[arg(long, conflicts_with = "config")]
socket: String,
/// Unix socket to which a host-side application connects to.
#[clap(long)]
#[arg(long, conflicts_with = "config")]
uds_path: String,
/// The size of the buffer used for the TX virtqueue
#[clap(long, default_value_t = 64 * 1024)]
#[clap(long, default_value_t = 64 * 1024, conflicts_with = "config")]
tx_buffer_size: u32,
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct VsockArgs {
#[command(flatten)]
param: Option<VsockParam>,
/// Load from a given configuration file
#[arg(long)]
config: Option<String>,
}
impl VsockArgs {
pub fn parse_config(&self) -> Option<VsockConfig> {
if let Some(c) = &self.config {
let b = config::Config::builder()
.add_source(config::File::new(c.as_str(), config::FileFormat::Yaml))
.build();
if let Ok(s) = b {
let mut v = s.get::<Vec<VsockParam>>("vms").unwrap();
if v.len() == 1 {
return v.pop().map(|vm| {
VsockConfig::new(vm.guest_cid, vm.socket, vm.uds_path, vm.tx_buffer_size)
});
}
}
}
None
}
}
impl TryFrom<VsockArgs> for VsockConfig {
type Error = Error;
fn try_from(cmd_args: VsockArgs) -> Result<Self> {
let socket = cmd_args.socket.trim().to_string();
let uds_path = cmd_args.uds_path.trim().to_string();
Ok(VsockConfig::new(
cmd_args.guest_cid,
socket,
uds_path,
cmd_args.tx_buffer_size,
))
// we try to use the configuration first, if failed, then fall back to the manual settings.
match cmd_args.parse_config() {
Some(c) => Ok(c),
_ => cmd_args.param.map_or(Err(Error::ConfigParse), |p| {
Ok(Self::new(
p.guest_cid,
p.socket.trim().to_string(),
p.uds_path.trim().to_string(),
p.tx_buffer_size,
))
}),
}
}
}
@ -108,14 +140,25 @@ fn main() {
mod tests {
use super::*;
use serial_test::serial;
use std::fs::File;
use std::io::Write;
impl VsockArgs {
fn from_args(guest_cid: u64, socket: &str, uds_path: &str, tx_buffer_size: u32) -> Self {
VsockArgs {
guest_cid,
socket: socket.to_string(),
uds_path: uds_path.to_string(),
tx_buffer_size,
param: Some(VsockParam {
guest_cid,
socket: socket.to_string(),
uds_path: uds_path.to_string(),
tx_buffer_size,
}),
config: None,
}
}
fn from_file(config: &str) -> Self {
VsockArgs {
param: None,
config: Some(config.to_string()),
}
}
}
@ -135,6 +178,26 @@ mod tests {
assert_eq!(config.get_tx_buffer_size(), 64 * 1024);
}
#[test]
#[serial]
fn test_vsock_config_setup_from_file() {
let mut yaml = File::create("./config.yaml").unwrap();
yaml.write_all(
b"vms:
- guest_cid: 4
socket: /tmp/vhost4.socket
uds_path: /tmp/vm4.vsock
tx_buffer_size: 65536",
)
.unwrap();
let args = VsockArgs::from_file("./config.yaml");
let config = VsockConfig::try_from(args).unwrap();
assert_eq!(config.get_guest_cid(), 4);
assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket");
assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock");
std::fs::remove_file("./config.yaml").unwrap();
}
#[test]
#[serial]
fn test_vsock_server() {

View File

@ -122,6 +122,8 @@ pub(crate) enum Error {
EmptyBackendRxQ,
#[error("Failed to create an EventFd")]
EventFdCreate(std::io::Error),
#[error("Failed to parse a configuration file")]
ConfigParse,
}
impl std::convert::From<Error> for std::io::Error {