mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Having the virtual IOMMU created with --iommu is one thing, but we also need a way to decide if a virtio-console device should be attached to this virtual IOMMU or not. That's why we introduce an extra option "iommu" with the value "on" or "off". By default, the device is not attached, which means "iommu=off". Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
899 lines
26 KiB
Rust
899 lines
26 KiB
Rust
// Copyright © 2019 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
extern crate vm_virtio;
|
|
|
|
use net_util::MacAddr;
|
|
use std::convert::From;
|
|
use std::net::AddrParseError;
|
|
use std::net::Ipv4Addr;
|
|
use std::path::PathBuf;
|
|
use std::result;
|
|
|
|
pub const DEFAULT_VCPUS: u8 = 1;
|
|
pub const DEFAULT_MEMORY_MB: u64 = 512;
|
|
pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
|
|
|
|
/// Errors associated with VM configuration parameters.
|
|
#[derive(Debug)]
|
|
pub enum Error<'a> {
|
|
/// Failed parsing cpus parameters.
|
|
ParseCpusParams(std::num::ParseIntError),
|
|
/// Failed parsing memory file parameter.
|
|
ParseMemoryFileParam,
|
|
/// Failed parsing kernel parameters.
|
|
ParseKernelParams,
|
|
/// Failed parsing kernel command line parameters.
|
|
ParseCmdlineParams,
|
|
/// Failed parsing disks parameters.
|
|
ParseDisksParams,
|
|
/// Failed parsing random number generator parameters.
|
|
ParseRngParams,
|
|
/// Failed parsing network ip parameter.
|
|
ParseNetIpParam(AddrParseError),
|
|
/// Failed parsing network mask parameter.
|
|
ParseNetMaskParam(AddrParseError),
|
|
/// Failed parsing network mac parameter.
|
|
ParseNetMacParam(&'a str),
|
|
/// Failed parsing fs tag parameter.
|
|
ParseFsTagParam,
|
|
/// Failed parsing fs socket path parameter.
|
|
ParseFsSockParam,
|
|
/// Failed parsing fs number of queues parameter.
|
|
ParseFsNumQueuesParam(std::num::ParseIntError),
|
|
/// Failed parsing fs queue size parameter.
|
|
ParseFsQueueSizeParam(std::num::ParseIntError),
|
|
/// Failed parsing fs dax parameter.
|
|
ParseFsDax,
|
|
/// Cannot have dax=off along with cache_size parameter.
|
|
InvalidCacheSizeWithDaxOff,
|
|
/// Failed parsing persitent memory file parameter.
|
|
ParsePmemFileParam,
|
|
/// Failed parsing size parameter.
|
|
ParseSizeParam(std::num::ParseIntError),
|
|
/// Failed parsing console parameter.
|
|
ParseConsoleParam,
|
|
/// Both console and serial are tty.
|
|
ParseTTYParam,
|
|
/// Failed parsing vhost-user-net mac parameter.
|
|
ParseVuNetMacParam(&'a str),
|
|
/// Failed parsing vhost-user sock parameter.
|
|
ParseVuSockParam,
|
|
/// Failed parsing vhost-user queue number parameter.
|
|
ParseVuNumQueuesParam(std::num::ParseIntError),
|
|
/// Failed parsing vhost-user queue size parameter.
|
|
ParseVuQueueSizeParam(std::num::ParseIntError),
|
|
/// Failed parsing vhost-user-net server parameter.
|
|
ParseVuNetServerParam(std::num::ParseIntError),
|
|
/// Failed parsing vhost-user-blk wce parameter.
|
|
ParseVuBlkWceParam(std::str::ParseBoolError),
|
|
/// Failed parsing vsock context ID parameter.
|
|
ParseVsockCidParam(std::num::ParseIntError),
|
|
/// Failed parsing vsock socket path parameter.
|
|
ParseVsockSockParam,
|
|
/// Missing kernel configuration
|
|
ValidateMissingKernelConfig,
|
|
/// Failed parsing iommu parameter for the device.
|
|
ParseDeviceIommu,
|
|
}
|
|
pub type Result<'a, T> = result::Result<T, Error<'a>>;
|
|
|
|
pub struct VmParams<'a> {
|
|
pub cpus: &'a str,
|
|
pub memory: &'a str,
|
|
pub kernel: Option<&'a str>,
|
|
pub cmdline: Option<&'a str>,
|
|
pub disks: Option<Vec<&'a str>>,
|
|
pub net: Option<Vec<&'a str>>,
|
|
pub rng: &'a str,
|
|
pub fs: Option<Vec<&'a str>>,
|
|
pub pmem: Option<Vec<&'a str>>,
|
|
pub serial: &'a str,
|
|
pub console: &'a str,
|
|
pub devices: Option<Vec<&'a str>>,
|
|
pub vhost_user_net: Option<Vec<&'a str>>,
|
|
pub vhost_user_blk: Option<Vec<&'a str>>,
|
|
pub vsock: Option<Vec<&'a str>>,
|
|
}
|
|
|
|
fn parse_size(size: &str) -> Result<u64> {
|
|
let s = size.trim();
|
|
|
|
let shift = if s.ends_with('K') {
|
|
10
|
|
} else if s.ends_with('M') {
|
|
20
|
|
} else if s.ends_with('G') {
|
|
30
|
|
} else {
|
|
0
|
|
};
|
|
|
|
let s = s.trim_end_matches(|c| c == 'K' || c == 'M' || c == 'G');
|
|
let res = s.parse::<u64>().map_err(Error::ParseSizeParam)?;
|
|
Ok(res << shift)
|
|
}
|
|
|
|
fn parse_iommu(iommu: &str) -> Result<bool> {
|
|
if !iommu.is_empty() {
|
|
let res = match iommu {
|
|
"on" => true,
|
|
"off" => false,
|
|
_ => return Err(Error::ParseDeviceIommu),
|
|
};
|
|
|
|
Ok(res)
|
|
} else {
|
|
Ok(false)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct CpusConfig(pub u8);
|
|
|
|
impl CpusConfig {
|
|
pub fn parse(cpus: &str) -> Result<Self> {
|
|
Ok(CpusConfig(
|
|
cpus.parse::<u8>().map_err(Error::ParseCpusParams)?,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl From<&CpusConfig> for u8 {
|
|
fn from(val: &CpusConfig) -> Self {
|
|
val.0
|
|
}
|
|
}
|
|
|
|
impl Default for CpusConfig {
|
|
fn default() -> Self {
|
|
CpusConfig(DEFAULT_VCPUS)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct MemoryConfig {
|
|
pub size: u64,
|
|
pub file: Option<PathBuf>,
|
|
}
|
|
|
|
impl MemoryConfig {
|
|
pub fn parse(memory: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = memory.split(',').collect();
|
|
|
|
let mut size_str: &str = "";
|
|
let mut file_str: &str = "";
|
|
let mut backed = false;
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("size=") {
|
|
size_str = ¶m[5..];
|
|
} else if param.starts_with("file=") {
|
|
backed = true;
|
|
file_str = ¶m[5..];
|
|
}
|
|
}
|
|
|
|
let file = if backed {
|
|
if file_str.is_empty() {
|
|
return Err(Error::ParseMemoryFileParam);
|
|
}
|
|
|
|
Some(PathBuf::from(file_str))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(MemoryConfig {
|
|
size: parse_size(size_str)?,
|
|
file,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Default for MemoryConfig {
|
|
fn default() -> Self {
|
|
MemoryConfig {
|
|
size: DEFAULT_MEMORY_MB << 20,
|
|
file: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct KernelConfig {
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
#[derive(Clone, Deserialize, Serialize)]
|
|
pub struct CmdlineConfig {
|
|
pub args: String,
|
|
}
|
|
|
|
impl CmdlineConfig {
|
|
pub fn parse(cmdline: Option<&str>) -> Result<Self> {
|
|
let args = cmdline
|
|
.map(std::string::ToString::to_string)
|
|
.unwrap_or_else(String::new);
|
|
|
|
Ok(CmdlineConfig { args })
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct DiskConfig {
|
|
pub path: PathBuf,
|
|
#[serde(default)]
|
|
pub iommu: bool,
|
|
}
|
|
|
|
impl DiskConfig {
|
|
pub fn parse(disk: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = disk.split(',').collect();
|
|
|
|
let mut path_str: &str = "";
|
|
let mut iommu_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("path=") {
|
|
path_str = ¶m[5..];
|
|
} else if param.starts_with("iommu=") {
|
|
iommu_str = ¶m[6..];
|
|
}
|
|
}
|
|
|
|
Ok(DiskConfig {
|
|
path: PathBuf::from(path_str),
|
|
iommu: parse_iommu(iommu_str)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct NetConfig {
|
|
pub tap: Option<String>,
|
|
pub ip: Ipv4Addr,
|
|
pub mask: Ipv4Addr,
|
|
pub mac: MacAddr,
|
|
#[serde(default)]
|
|
pub iommu: bool,
|
|
}
|
|
|
|
impl NetConfig {
|
|
pub fn parse(net: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = net.split(',').collect();
|
|
|
|
let mut tap_str: &str = "";
|
|
let mut ip_str: &str = "";
|
|
let mut mask_str: &str = "";
|
|
let mut mac_str: &str = "";
|
|
let mut iommu_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("tap=") {
|
|
tap_str = ¶m[4..];
|
|
} else if param.starts_with("ip=") {
|
|
ip_str = ¶m[3..];
|
|
} else if param.starts_with("mask=") {
|
|
mask_str = ¶m[5..];
|
|
} else if param.starts_with("mac=") {
|
|
mac_str = ¶m[4..];
|
|
} else if param.starts_with("iommu=") {
|
|
iommu_str = ¶m[6..];
|
|
}
|
|
}
|
|
|
|
let mut tap: Option<String> = None;
|
|
let mut ip: Ipv4Addr = Ipv4Addr::new(192, 168, 249, 1);
|
|
let mut mask: Ipv4Addr = Ipv4Addr::new(255, 255, 255, 0);;
|
|
let mut mac: MacAddr = MacAddr::local_random();
|
|
let iommu = parse_iommu(iommu_str)?;
|
|
|
|
if !tap_str.is_empty() {
|
|
tap = Some(tap_str.to_string());
|
|
}
|
|
if !ip_str.is_empty() {
|
|
ip = ip_str.parse().map_err(Error::ParseNetIpParam)?;
|
|
}
|
|
if !mask_str.is_empty() {
|
|
mask = mask_str.parse().map_err(Error::ParseNetMaskParam)?;
|
|
}
|
|
if !mac_str.is_empty() {
|
|
mac = MacAddr::parse_str(mac_str).map_err(Error::ParseNetMacParam)?;
|
|
}
|
|
|
|
Ok(NetConfig {
|
|
tap,
|
|
ip,
|
|
mask,
|
|
mac,
|
|
iommu,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct RngConfig {
|
|
pub src: PathBuf,
|
|
#[serde(default)]
|
|
pub iommu: bool,
|
|
}
|
|
|
|
impl RngConfig {
|
|
pub fn parse(rng: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = rng.split(',').collect();
|
|
|
|
let mut src_str: &str = "";
|
|
let mut iommu_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("src=") {
|
|
src_str = ¶m[4..];
|
|
} else if param.starts_with("iommu=") {
|
|
iommu_str = ¶m[6..];
|
|
}
|
|
}
|
|
|
|
Ok(RngConfig {
|
|
src: PathBuf::from(src_str),
|
|
iommu: parse_iommu(iommu_str)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Default for RngConfig {
|
|
fn default() -> Self {
|
|
RngConfig {
|
|
src: PathBuf::from(DEFAULT_RNG_SOURCE),
|
|
iommu: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct FsConfig {
|
|
pub tag: String,
|
|
pub sock: PathBuf,
|
|
pub num_queues: usize,
|
|
pub queue_size: u16,
|
|
pub cache_size: Option<u64>,
|
|
}
|
|
|
|
impl FsConfig {
|
|
pub fn parse(fs: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = fs.split(',').collect();
|
|
|
|
let mut tag: &str = "";
|
|
let mut sock: &str = "";
|
|
let mut num_queues_str: &str = "";
|
|
let mut queue_size_str: &str = "";
|
|
let mut dax_str: &str = "";
|
|
let mut cache_size_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("tag=") {
|
|
tag = ¶m[4..];
|
|
} else if param.starts_with("sock=") {
|
|
sock = ¶m[5..];
|
|
} else if param.starts_with("num_queues=") {
|
|
num_queues_str = ¶m[11..];
|
|
} else if param.starts_with("queue_size=") {
|
|
queue_size_str = ¶m[11..];
|
|
} else if param.starts_with("dax=") {
|
|
dax_str = ¶m[4..];
|
|
} else if param.starts_with("cache_size=") {
|
|
cache_size_str = ¶m[11..];
|
|
}
|
|
}
|
|
|
|
let mut num_queues: usize = 1;
|
|
let mut queue_size: u16 = 1024;
|
|
let mut dax: bool = true;
|
|
// Default cache size set to 8Gib.
|
|
let mut cache_size: Option<u64> = Some(0x0002_0000_0000);
|
|
|
|
if tag.is_empty() {
|
|
return Err(Error::ParseFsTagParam);
|
|
}
|
|
if sock.is_empty() {
|
|
return Err(Error::ParseFsSockParam);
|
|
}
|
|
if !num_queues_str.is_empty() {
|
|
num_queues = num_queues_str
|
|
.parse()
|
|
.map_err(Error::ParseFsNumQueuesParam)?;
|
|
}
|
|
if !queue_size_str.is_empty() {
|
|
queue_size = queue_size_str
|
|
.parse()
|
|
.map_err(Error::ParseFsQueueSizeParam)?;
|
|
}
|
|
if !dax_str.is_empty() {
|
|
match dax_str {
|
|
"on" => dax = true,
|
|
"off" => dax = false,
|
|
_ => return Err(Error::ParseFsDax),
|
|
}
|
|
}
|
|
|
|
// Take appropriate decision about cache_size based on DAX being
|
|
// enabled or disabled.
|
|
if !dax {
|
|
if !cache_size_str.is_empty() {
|
|
return Err(Error::InvalidCacheSizeWithDaxOff);
|
|
}
|
|
cache_size = None;
|
|
} else if !cache_size_str.is_empty() {
|
|
cache_size = Some(parse_size(cache_size_str)?);
|
|
}
|
|
|
|
Ok(FsConfig {
|
|
tag: tag.to_string(),
|
|
sock: PathBuf::from(sock),
|
|
num_queues,
|
|
queue_size,
|
|
cache_size,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct PmemConfig {
|
|
pub file: PathBuf,
|
|
pub size: u64,
|
|
#[serde(default)]
|
|
pub iommu: bool,
|
|
}
|
|
|
|
impl PmemConfig {
|
|
pub fn parse(pmem: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = pmem.split(',').collect();
|
|
|
|
let mut file_str: &str = "";
|
|
let mut size_str: &str = "";
|
|
let mut iommu_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("file=") {
|
|
file_str = ¶m[5..];
|
|
} else if param.starts_with("size=") {
|
|
size_str = ¶m[5..];
|
|
} else if param.starts_with("iommu=") {
|
|
iommu_str = ¶m[6..];
|
|
}
|
|
}
|
|
|
|
if file_str.is_empty() {
|
|
return Err(Error::ParsePmemFileParam);
|
|
}
|
|
|
|
Ok(PmemConfig {
|
|
file: PathBuf::from(file_str),
|
|
size: parse_size(size_str)?,
|
|
iommu: parse_iommu(iommu_str)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Deserialize, Serialize)]
|
|
pub enum ConsoleOutputMode {
|
|
Off,
|
|
Tty,
|
|
File,
|
|
Null,
|
|
}
|
|
|
|
impl ConsoleOutputMode {
|
|
pub fn input_enabled(&self) -> bool {
|
|
match self {
|
|
ConsoleOutputMode::Tty => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Deserialize, Serialize)]
|
|
pub struct ConsoleConfig {
|
|
pub file: Option<PathBuf>,
|
|
pub mode: ConsoleOutputMode,
|
|
#[serde(default)]
|
|
pub iommu: bool,
|
|
}
|
|
|
|
impl ConsoleConfig {
|
|
pub fn parse(console: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = console.split(',').collect();
|
|
|
|
let mut valid = false;
|
|
let mut file: Option<PathBuf> = None;
|
|
let mut mode: ConsoleOutputMode = ConsoleOutputMode::Off;
|
|
let mut iommu_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("iommu=") {
|
|
iommu_str = ¶m[6..];
|
|
} else {
|
|
if *param == "off" {
|
|
mode = ConsoleOutputMode::Off;
|
|
file = None;
|
|
} else if *param == "tty" {
|
|
mode = ConsoleOutputMode::Tty;
|
|
file = None;
|
|
} else if param.starts_with("file=") {
|
|
mode = ConsoleOutputMode::File;
|
|
file = Some(PathBuf::from(¶m[5..]));
|
|
} else if param.starts_with("null") {
|
|
mode = ConsoleOutputMode::Null;
|
|
file = None;
|
|
} else {
|
|
return Err(Error::ParseConsoleParam);
|
|
}
|
|
valid = true;
|
|
}
|
|
}
|
|
|
|
if !valid {
|
|
return Err(Error::ParseConsoleParam);
|
|
}
|
|
|
|
Ok(Self {
|
|
mode,
|
|
file,
|
|
iommu: parse_iommu(iommu_str)?,
|
|
})
|
|
}
|
|
|
|
pub fn default_serial() -> Self {
|
|
ConsoleConfig {
|
|
file: None,
|
|
mode: ConsoleOutputMode::Null,
|
|
iommu: false,
|
|
}
|
|
}
|
|
|
|
pub fn default_console() -> Self {
|
|
ConsoleConfig {
|
|
file: None,
|
|
mode: ConsoleOutputMode::Tty,
|
|
iommu: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct DeviceConfig {
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
impl DeviceConfig {
|
|
pub fn parse(device: &str) -> Result<Self> {
|
|
Ok(DeviceConfig {
|
|
path: PathBuf::from(device),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct VuConfig {
|
|
pub sock: String,
|
|
pub num_queues: usize,
|
|
pub queue_size: u16,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct VhostUserNetConfig {
|
|
pub mac: MacAddr,
|
|
pub vu_cfg: VuConfig,
|
|
}
|
|
|
|
impl VhostUserNetConfig {
|
|
pub fn parse(vhost_user_net: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = vhost_user_net.split(',').collect();
|
|
|
|
let mut mac_str: &str = "";
|
|
let mut sock: &str = "";
|
|
let mut num_queues_str: &str = "";
|
|
let mut queue_size_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("mac=") {
|
|
mac_str = ¶m[4..];
|
|
} else if param.starts_with("sock=") {
|
|
sock = ¶m[5..];
|
|
} else if param.starts_with("num_queues=") {
|
|
num_queues_str = ¶m[11..];
|
|
} else if param.starts_with("queue_size=") {
|
|
queue_size_str = ¶m[11..];
|
|
}
|
|
}
|
|
|
|
let mut mac: MacAddr = MacAddr::local_random();
|
|
let mut num_queues: usize = 2;
|
|
let mut queue_size: u16 = 256;
|
|
|
|
if !mac_str.is_empty() {
|
|
mac = MacAddr::parse_str(mac_str).map_err(Error::ParseVuNetMacParam)?;
|
|
}
|
|
if sock.is_empty() {
|
|
return Err(Error::ParseVuSockParam);
|
|
}
|
|
if !num_queues_str.is_empty() {
|
|
num_queues = num_queues_str
|
|
.parse()
|
|
.map_err(Error::ParseVuNumQueuesParam)?;
|
|
}
|
|
if !queue_size_str.is_empty() {
|
|
queue_size = queue_size_str
|
|
.parse()
|
|
.map_err(Error::ParseVuQueueSizeParam)?;
|
|
}
|
|
|
|
let vu_cfg = VuConfig {
|
|
sock: sock.to_string(),
|
|
num_queues,
|
|
queue_size,
|
|
};
|
|
|
|
Ok(VhostUserNetConfig { mac, vu_cfg })
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct VsockConfig {
|
|
pub cid: u64,
|
|
pub sock: PathBuf,
|
|
}
|
|
|
|
impl VsockConfig {
|
|
pub fn parse(vsock: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = vsock.split(',').collect();
|
|
|
|
let mut cid_str: &str = "";
|
|
let mut sock_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("cid=") {
|
|
cid_str = ¶m[4..];
|
|
} else if param.starts_with("sock=") {
|
|
sock_str = ¶m[5..];
|
|
}
|
|
}
|
|
|
|
if sock_str.is_empty() {
|
|
return Err(Error::ParseVsockSockParam);
|
|
}
|
|
|
|
Ok(VsockConfig {
|
|
cid: cid_str.parse::<u64>().map_err(Error::ParseVsockCidParam)?,
|
|
sock: PathBuf::from(sock_str),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
pub struct VhostUserBlkConfig {
|
|
pub wce: bool,
|
|
pub vu_cfg: VuConfig,
|
|
}
|
|
|
|
impl VhostUserBlkConfig {
|
|
pub fn parse(vhost_user_blk: &str) -> Result<Self> {
|
|
// Split the parameters based on the comma delimiter
|
|
let params_list: Vec<&str> = vhost_user_blk.split(',').collect();
|
|
|
|
let mut sock: &str = "";
|
|
let mut num_queues_str: &str = "";
|
|
let mut queue_size_str: &str = "";
|
|
let mut wce_str: &str = "";
|
|
|
|
for param in params_list.iter() {
|
|
if param.starts_with("sock=") {
|
|
sock = ¶m[5..];
|
|
} else if param.starts_with("num_queues=") {
|
|
num_queues_str = ¶m[11..];
|
|
} else if param.starts_with("queue_size=") {
|
|
queue_size_str = ¶m[11..];
|
|
} else if param.starts_with("wce=") {
|
|
wce_str = ¶m[4..];
|
|
}
|
|
}
|
|
|
|
let mut num_queues: usize = 1;
|
|
let mut queue_size: u16 = 128;
|
|
let mut wce: bool = true;
|
|
|
|
if !num_queues_str.is_empty() {
|
|
num_queues = num_queues_str
|
|
.parse()
|
|
.map_err(Error::ParseVuNumQueuesParam)?;
|
|
}
|
|
if !queue_size_str.is_empty() {
|
|
queue_size = queue_size_str
|
|
.parse()
|
|
.map_err(Error::ParseVuQueueSizeParam)?;
|
|
}
|
|
if !wce_str.is_empty() {
|
|
wce = wce_str.parse().map_err(Error::ParseVuBlkWceParam)?;
|
|
}
|
|
|
|
let vu_cfg = VuConfig {
|
|
sock: sock.to_string(),
|
|
num_queues,
|
|
queue_size,
|
|
};
|
|
|
|
Ok(VhostUserBlkConfig { wce, vu_cfg })
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Deserialize, Serialize)]
|
|
pub struct VmConfig {
|
|
#[serde(default)]
|
|
pub cpus: CpusConfig,
|
|
#[serde(default)]
|
|
pub memory: MemoryConfig,
|
|
pub kernel: Option<KernelConfig>,
|
|
pub cmdline: CmdlineConfig,
|
|
pub disks: Option<Vec<DiskConfig>>,
|
|
pub net: Option<Vec<NetConfig>>,
|
|
#[serde(default)]
|
|
pub rng: RngConfig,
|
|
pub fs: Option<Vec<FsConfig>>,
|
|
pub pmem: Option<Vec<PmemConfig>>,
|
|
#[serde(default = "ConsoleConfig::default_serial")]
|
|
pub serial: ConsoleConfig,
|
|
#[serde(default = "ConsoleConfig::default_console")]
|
|
pub console: ConsoleConfig,
|
|
pub devices: Option<Vec<DeviceConfig>>,
|
|
pub vhost_user_net: Option<Vec<VhostUserNetConfig>>,
|
|
pub vhost_user_blk: Option<Vec<VhostUserBlkConfig>>,
|
|
pub vsock: Option<Vec<VsockConfig>>,
|
|
#[serde(default)]
|
|
pub iommu: bool,
|
|
}
|
|
|
|
impl VmConfig {
|
|
pub fn valid(&self) -> bool {
|
|
self.kernel.is_some()
|
|
}
|
|
|
|
pub fn parse(vm_params: VmParams) -> Result<Self> {
|
|
let mut iommu = false;
|
|
|
|
let mut disks: Option<Vec<DiskConfig>> = None;
|
|
if let Some(disk_list) = &vm_params.disks {
|
|
let mut disk_config_list = Vec::new();
|
|
for item in disk_list.iter() {
|
|
let disk_config = DiskConfig::parse(item)?;
|
|
if disk_config.iommu {
|
|
iommu = true;
|
|
}
|
|
disk_config_list.push(disk_config);
|
|
}
|
|
disks = Some(disk_config_list);
|
|
}
|
|
|
|
let mut net: Option<Vec<NetConfig>> = None;
|
|
if let Some(net_list) = &vm_params.net {
|
|
let mut net_config_list = Vec::new();
|
|
for item in net_list.iter() {
|
|
let net_config = NetConfig::parse(item)?;
|
|
if net_config.iommu {
|
|
iommu = true;
|
|
}
|
|
net_config_list.push(net_config);
|
|
}
|
|
net = Some(net_config_list);
|
|
}
|
|
|
|
let rng = RngConfig::parse(vm_params.rng)?;
|
|
if rng.iommu {
|
|
iommu = true;
|
|
}
|
|
|
|
let mut fs: Option<Vec<FsConfig>> = None;
|
|
if let Some(fs_list) = &vm_params.fs {
|
|
let mut fs_config_list = Vec::new();
|
|
for item in fs_list.iter() {
|
|
fs_config_list.push(FsConfig::parse(item)?);
|
|
}
|
|
fs = Some(fs_config_list);
|
|
}
|
|
|
|
let mut pmem: Option<Vec<PmemConfig>> = None;
|
|
if let Some(pmem_list) = &vm_params.pmem {
|
|
let mut pmem_config_list = Vec::new();
|
|
for item in pmem_list.iter() {
|
|
let pmem_config = PmemConfig::parse(item)?;
|
|
if pmem_config.iommu {
|
|
iommu = true;
|
|
}
|
|
pmem_config_list.push(pmem_config);
|
|
}
|
|
pmem = Some(pmem_config_list);
|
|
}
|
|
|
|
let console = ConsoleConfig::parse(vm_params.console)?;
|
|
if console.iommu {
|
|
iommu = true;
|
|
}
|
|
let serial = ConsoleConfig::parse(vm_params.serial)?;
|
|
if console.mode == ConsoleOutputMode::Tty && serial.mode == ConsoleOutputMode::Tty {
|
|
return Err(Error::ParseTTYParam);
|
|
}
|
|
|
|
let mut devices: Option<Vec<DeviceConfig>> = None;
|
|
if let Some(device_list) = &vm_params.devices {
|
|
let mut device_config_list = Vec::new();
|
|
for item in device_list.iter() {
|
|
device_config_list.push(DeviceConfig::parse(item)?);
|
|
}
|
|
devices = Some(device_config_list);
|
|
}
|
|
|
|
let mut vhost_user_net: Option<Vec<VhostUserNetConfig>> = None;
|
|
if let Some(vhost_user_net_list) = &vm_params.vhost_user_net {
|
|
let mut vhost_user_net_config_list = Vec::new();
|
|
for item in vhost_user_net_list.iter() {
|
|
vhost_user_net_config_list.push(VhostUserNetConfig::parse(item)?);
|
|
}
|
|
vhost_user_net = Some(vhost_user_net_config_list);
|
|
}
|
|
|
|
let mut vsock: Option<Vec<VsockConfig>> = None;
|
|
if let Some(vsock_list) = &vm_params.vsock {
|
|
let mut vsock_config_list = Vec::new();
|
|
for item in vsock_list.iter() {
|
|
vsock_config_list.push(VsockConfig::parse(item)?);
|
|
}
|
|
vsock = Some(vsock_config_list);
|
|
}
|
|
|
|
let mut vhost_user_blk: Option<Vec<VhostUserBlkConfig>> = None;
|
|
if let Some(vhost_user_blk_list) = &vm_params.vhost_user_blk {
|
|
let mut vhost_user_blk_config_list = Vec::new();
|
|
for item in vhost_user_blk_list.iter() {
|
|
vhost_user_blk_config_list.push(VhostUserBlkConfig::parse(item)?);
|
|
}
|
|
vhost_user_blk = Some(vhost_user_blk_config_list);
|
|
}
|
|
|
|
let mut kernel: Option<KernelConfig> = None;
|
|
if let Some(k) = vm_params.kernel {
|
|
kernel = Some(KernelConfig {
|
|
path: PathBuf::from(k),
|
|
});
|
|
}
|
|
|
|
Ok(VmConfig {
|
|
cpus: CpusConfig::parse(vm_params.cpus)?,
|
|
memory: MemoryConfig::parse(vm_params.memory)?,
|
|
kernel,
|
|
cmdline: CmdlineConfig::parse(vm_params.cmdline)?,
|
|
disks,
|
|
net,
|
|
rng,
|
|
fs,
|
|
pmem,
|
|
serial,
|
|
console,
|
|
devices,
|
|
vhost_user_net,
|
|
vhost_user_blk,
|
|
vsock,
|
|
iommu,
|
|
})
|
|
}
|
|
}
|