2019-05-23 19:48:05 +00:00
|
|
|
// Copyright © 2019 Intel Corporation
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
//
|
|
|
|
|
2019-12-12 17:12:42 +00:00
|
|
|
use clap::ArgMatches;
|
2019-05-23 19:48:05 +00:00
|
|
|
use net_util::MacAddr;
|
2020-08-31 12:20:20 +00:00
|
|
|
use option_parser::{
|
2020-09-04 07:36:10 +00:00
|
|
|
ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, TupleTwoIntegers,
|
2020-08-31 12:20:20 +00:00
|
|
|
};
|
2021-08-07 10:15:13 +00:00
|
|
|
use std::collections::HashMap;
|
2019-05-23 19:48:05 +00:00
|
|
|
use std::convert::From;
|
2020-04-03 09:28:04 +00:00
|
|
|
use std::fmt;
|
2019-05-23 19:48:05 +00:00
|
|
|
use std::net::Ipv4Addr;
|
2019-09-23 17:46:35 +00:00
|
|
|
use std::path::PathBuf;
|
2019-05-23 19:48:05 +00:00
|
|
|
use std::result;
|
2020-03-27 11:15:55 +00:00
|
|
|
use std::str::FromStr;
|
2019-05-23 19:48:05 +00:00
|
|
|
|
2021-02-26 20:06:10 +00:00
|
|
|
use virtio_devices::{RateLimiterConfig, TokenBucketConfig};
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
pub const DEFAULT_VCPUS: u8 = 1;
|
|
|
|
pub const DEFAULT_MEMORY_MB: u64 = 512;
|
2021-10-01 09:54:27 +00:00
|
|
|
|
|
|
|
// When booting with PVH boot the maximum physical addressable size
|
|
|
|
// is a 46 bit address space even when the host supports with 5-level
|
|
|
|
// paging.
|
|
|
|
pub const DEFAULT_MAX_PHYS_BITS: u8 = 46;
|
|
|
|
|
2019-05-23 19:48:05 +00:00
|
|
|
pub const DEFAULT_RNG_SOURCE: &str = "/dev/urandom";
|
2019-12-10 16:09:28 +00:00
|
|
|
pub const DEFAULT_NUM_QUEUES_VUNET: usize = 2;
|
|
|
|
pub const DEFAULT_QUEUE_SIZE_VUNET: u16 = 256;
|
2019-12-10 16:15:43 +00:00
|
|
|
pub const DEFAULT_NUM_QUEUES_VUBLK: usize = 1;
|
|
|
|
pub const DEFAULT_QUEUE_SIZE_VUBLK: u16 = 128;
|
2019-05-23 19:48:05 +00:00
|
|
|
|
2021-10-11 14:51:30 +00:00
|
|
|
pub const DEFAULT_NUM_PCI_SEGMENTS: u16 = 1;
|
|
|
|
const MAX_NUM_PCI_SEGMENTS: u16 = 16;
|
|
|
|
|
2019-05-23 19:48:05 +00:00
|
|
|
/// Errors associated with VM configuration parameters.
|
|
|
|
#[derive(Debug)]
|
2020-01-24 07:30:50 +00:00
|
|
|
pub enum Error {
|
2020-04-02 10:00:45 +00:00
|
|
|
/// Filesystem tag is missing
|
|
|
|
ParseFsTagMissing,
|
|
|
|
/// Filesystem socket is missing
|
|
|
|
ParseFsSockMissing,
|
2019-08-05 19:45:58 +00:00
|
|
|
/// Cannot have dax=off along with cache_size parameter.
|
|
|
|
InvalidCacheSizeWithDaxOff,
|
2020-09-22 11:31:42 +00:00
|
|
|
/// Missing persistent memory file parameter.
|
2020-04-02 15:00:52 +00:00
|
|
|
ParsePmemFileMissing,
|
2020-04-02 17:39:19 +00:00
|
|
|
/// Missing vsock socket path parameter.
|
|
|
|
ParseVsockSockMissing,
|
|
|
|
/// Missing vsock cid parameter.
|
|
|
|
ParseVsockCidMissing,
|
2020-04-07 12:50:19 +00:00
|
|
|
/// Missing restore source_url parameter.
|
|
|
|
ParseRestoreSourceUrlMissing,
|
2020-03-27 11:51:09 +00:00
|
|
|
/// Error parsing CPU options
|
|
|
|
ParseCpus(OptionParserError),
|
2020-03-30 17:50:46 +00:00
|
|
|
/// Error parsing memory options
|
|
|
|
ParseMemory(OptionParserError),
|
2020-08-18 13:19:49 +00:00
|
|
|
/// Error parsing memory zone options
|
|
|
|
ParseMemoryZone(OptionParserError),
|
2020-09-03 18:06:38 +00:00
|
|
|
/// Missing 'id' from memory zone
|
|
|
|
ParseMemoryZoneIdMissing,
|
2020-03-31 10:45:43 +00:00
|
|
|
/// Error parsing disk options
|
|
|
|
ParseDisk(OptionParserError),
|
2020-03-31 15:54:42 +00:00
|
|
|
/// Error parsing network options
|
|
|
|
ParseNetwork(OptionParserError),
|
2020-04-02 09:17:10 +00:00
|
|
|
/// Error parsing RNG options
|
2021-03-25 17:01:21 +00:00
|
|
|
ParseRng(OptionParserError),
|
2020-10-14 08:48:50 +00:00
|
|
|
/// Error parsing balloon options
|
|
|
|
ParseBalloon(OptionParserError),
|
2020-04-02 10:00:45 +00:00
|
|
|
/// Error parsing filesystem parameters
|
|
|
|
ParseFileSystem(OptionParserError),
|
2020-09-22 11:31:42 +00:00
|
|
|
/// Error parsing persistent memory parameters
|
2020-04-02 15:00:52 +00:00
|
|
|
ParsePersistentMemory(OptionParserError),
|
2020-04-02 15:52:30 +00:00
|
|
|
/// Failed parsing console
|
|
|
|
ParseConsole(OptionParserError),
|
|
|
|
/// No mode given for console
|
|
|
|
ParseConsoleInvalidModeGiven,
|
2020-04-02 17:25:52 +00:00
|
|
|
/// Failed parsing device parameters
|
|
|
|
ParseDevice(OptionParserError),
|
|
|
|
/// Missing path from device,
|
|
|
|
ParseDevicePathMissing,
|
2020-04-02 17:39:19 +00:00
|
|
|
/// Failed to parse vsock parameters
|
|
|
|
ParseVsock(OptionParserError),
|
2020-04-07 12:50:19 +00:00
|
|
|
/// Failed to parse restore parameters
|
|
|
|
ParseRestore(OptionParserError),
|
2020-07-08 07:18:45 +00:00
|
|
|
/// Failed to parse SGX EPC parameters
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
ParseSgxEpc(OptionParserError),
|
2021-07-09 08:48:32 +00:00
|
|
|
/// Missing 'id' from SGX EPC section
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
ParseSgxEpcIdMissing,
|
2020-08-28 16:20:12 +00:00
|
|
|
/// Failed to parse NUMA parameters
|
|
|
|
ParseNuma(OptionParserError),
|
2020-04-06 15:20:27 +00:00
|
|
|
/// Failed to validate configuration
|
|
|
|
Validation(ValidationError),
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
/// Failed to parse TDX config
|
|
|
|
ParseTdx(OptionParserError),
|
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
// No TDX firmware
|
|
|
|
FirmwarePathMissing,
|
2021-06-04 14:40:13 +00:00
|
|
|
/// Failed to parse userspace device
|
|
|
|
ParseUserDevice(OptionParserError),
|
|
|
|
/// Missing socket for userspace device
|
|
|
|
ParseUserDeviceSocketMissing,
|
2021-10-11 14:51:30 +00:00
|
|
|
/// Failed parsing platform parameters
|
|
|
|
ParsePlatform(OptionParserError),
|
2020-04-06 15:20:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ValidationError {
|
|
|
|
/// Both console and serial are tty.
|
|
|
|
DoubleTtyMode,
|
2020-04-06 16:00:38 +00:00
|
|
|
/// No kernel specified
|
|
|
|
KernelMissing,
|
2020-04-07 14:28:20 +00:00
|
|
|
/// Missing file value for console
|
|
|
|
ConsoleFileMissing,
|
2020-04-07 14:53:41 +00:00
|
|
|
/// Max is less than boot
|
|
|
|
CpusMaxLowerThanBoot,
|
2020-04-07 14:53:41 +00:00
|
|
|
/// Both socket and path specified
|
|
|
|
DiskSocketAndPath,
|
2020-04-24 13:46:48 +00:00
|
|
|
/// Using vhost user requires shared memory
|
|
|
|
VhostUserRequiresSharedMemory,
|
2020-11-06 09:44:17 +00:00
|
|
|
/// No socket provided for vhost_use
|
|
|
|
VhostUserMissingSocket,
|
2020-05-05 08:59:49 +00:00
|
|
|
/// Trying to use IOMMU without PCI
|
|
|
|
IommuUnsupported,
|
|
|
|
/// Trying to use VFIO without PCI
|
|
|
|
VfioUnsupported,
|
2020-06-16 09:56:36 +00:00
|
|
|
/// CPU topology count doesn't match max
|
|
|
|
CpuTopologyCount,
|
|
|
|
/// One part of the CPU topology was zero
|
|
|
|
CpuTopologyZeroPart,
|
2020-07-29 20:20:50 +00:00
|
|
|
/// Virtio needs a min of 2 queues
|
|
|
|
VnetQueueLowerThan2,
|
2021-01-27 04:52:50 +00:00
|
|
|
/// The input queue number for virtio_net must match the number of input fds
|
|
|
|
VnetQueueFdMismatch,
|
2021-04-13 10:15:03 +00:00
|
|
|
/// Using reserved fd
|
|
|
|
VnetReservedFd,
|
2021-08-07 10:15:13 +00:00
|
|
|
/// Hugepages not turned on
|
2021-02-04 16:21:53 +00:00
|
|
|
HugePageSizeWithoutHugePages,
|
2021-08-07 10:15:13 +00:00
|
|
|
/// Huge page size is not power of 2
|
2021-02-04 16:21:53 +00:00
|
|
|
InvalidHugePageSize(u64),
|
2021-08-07 10:15:13 +00:00
|
|
|
/// CPU Hotplug not permitted with TDX
|
2021-03-03 15:38:02 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
2021-03-25 17:01:21 +00:00
|
|
|
TdxNoCpuHotplug,
|
2021-08-07 10:15:13 +00:00
|
|
|
/// Specifying kernel not permitted with TDX
|
2021-05-10 13:12:12 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
TdxKernelSpecified,
|
2021-08-07 10:15:13 +00:00
|
|
|
/// Insuffient vCPUs for queues
|
2021-04-29 11:10:44 +00:00
|
|
|
TooManyQueues,
|
2021-08-07 10:15:13 +00:00
|
|
|
/// Need shared memory for vfio-user
|
2021-08-04 15:14:24 +00:00
|
|
|
UserDevicesRequireSharedMemory,
|
2021-08-07 10:15:13 +00:00
|
|
|
/// Memory zone is reused across NUMA nodes
|
|
|
|
MemoryZoneReused(String, u32, u32),
|
2021-10-11 14:51:30 +00:00
|
|
|
/// Invalid number of PCI segments
|
|
|
|
InvalidNumPciSegments(u16),
|
2020-04-06 15:20:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ValidationResult<T> = std::result::Result<T, ValidationError>;
|
|
|
|
|
|
|
|
impl fmt::Display for ValidationError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
use self::ValidationError::*;
|
|
|
|
match self {
|
|
|
|
DoubleTtyMode => write!(f, "Console mode tty specified for both serial and console"),
|
2020-04-06 16:00:38 +00:00
|
|
|
KernelMissing => write!(f, "No kernel specified"),
|
2020-04-07 14:28:20 +00:00
|
|
|
ConsoleFileMissing => write!(f, "Path missing when using file console mode"),
|
2020-04-07 14:53:41 +00:00
|
|
|
CpusMaxLowerThanBoot => write!(f, "Max CPUs greater than boot CPUs"),
|
2020-04-07 14:53:41 +00:00
|
|
|
DiskSocketAndPath => write!(f, "Disk path and vhost socket both provided"),
|
2020-04-24 13:46:48 +00:00
|
|
|
VhostUserRequiresSharedMemory => {
|
|
|
|
write!(f, "Using vhost-user requires using shared memory")
|
|
|
|
}
|
2020-11-06 09:44:17 +00:00
|
|
|
VhostUserMissingSocket => write!(f, "No socket provided when using vhost-user"),
|
2020-05-05 08:59:49 +00:00
|
|
|
IommuUnsupported => write!(f, "Using an IOMMU without PCI support is unsupported"),
|
|
|
|
VfioUnsupported => write!(f, "Using VFIO without PCI support is unsupported"),
|
2020-06-16 09:56:36 +00:00
|
|
|
CpuTopologyZeroPart => write!(f, "No part of the CPU topology can be zero"),
|
|
|
|
CpuTopologyCount => write!(
|
|
|
|
f,
|
|
|
|
"Product of CPU topology parts does not match maximum vCPUs"
|
|
|
|
),
|
2020-07-29 20:20:50 +00:00
|
|
|
VnetQueueLowerThan2 => write!(f, "Number of queues to virtio_net less than 2"),
|
2021-01-27 04:52:50 +00:00
|
|
|
VnetQueueFdMismatch => write!(
|
|
|
|
f,
|
|
|
|
"Number of queues to virtio_net does not match the number of input FDs"
|
|
|
|
),
|
2021-04-13 10:15:03 +00:00
|
|
|
VnetReservedFd => write!(f, "Reserved fd number (<= 2)"),
|
2021-02-04 16:21:53 +00:00
|
|
|
HugePageSizeWithoutHugePages => {
|
|
|
|
write!(f, "Huge page size specified but huge pages not enabled")
|
|
|
|
}
|
|
|
|
InvalidHugePageSize(s) => {
|
|
|
|
write!(f, "Huge page size is not power of 2: {}", s)
|
|
|
|
}
|
2021-03-03 15:38:02 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
2021-03-25 17:01:21 +00:00
|
|
|
TdxNoCpuHotplug => {
|
2021-03-03 15:38:02 +00:00
|
|
|
write!(f, "CPU hotplug not possible with TDX")
|
|
|
|
}
|
2021-05-10 13:12:12 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
TdxKernelSpecified => {
|
|
|
|
write!(f, "Direct kernel boot not possible with TDX")
|
|
|
|
}
|
2021-04-29 11:10:44 +00:00
|
|
|
TooManyQueues => {
|
|
|
|
write!(f, "Number of vCPUs is insufficient for number of queues")
|
|
|
|
}
|
2021-08-04 15:14:24 +00:00
|
|
|
UserDevicesRequireSharedMemory => {
|
|
|
|
write!(f, "Using user devices requires using shared memory")
|
|
|
|
}
|
2021-08-07 10:15:13 +00:00
|
|
|
MemoryZoneReused(s, u1, u2) => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Memory zone: {} belongs to multiple NUMA nodes {} and {}",
|
|
|
|
s, u1, u2
|
|
|
|
)
|
|
|
|
}
|
2021-10-11 14:51:30 +00:00
|
|
|
InvalidNumPciSegments(n) => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Number of PCI segments ({}) not in range of 1 to {}",
|
|
|
|
n, MAX_NUM_PCI_SEGMENTS
|
|
|
|
)
|
|
|
|
}
|
2020-04-06 15:20:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
2020-04-03 09:28:04 +00:00
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
use self::Error::*;
|
|
|
|
match self {
|
|
|
|
ParseConsole(o) => write!(f, "Error parsing --console: {}", o),
|
|
|
|
ParseConsoleInvalidModeGiven => {
|
|
|
|
write!(f, "Error parsing --console: invalid console mode given")
|
|
|
|
}
|
|
|
|
ParseCpus(o) => write!(f, "Error parsing --cpus: {}", o),
|
2020-04-07 14:53:41 +00:00
|
|
|
|
2020-04-03 09:28:04 +00:00
|
|
|
ParseDevice(o) => write!(f, "Error parsing --device: {}", o),
|
|
|
|
ParseDevicePathMissing => write!(f, "Error parsing --device: path missing"),
|
|
|
|
ParseFileSystem(o) => write!(f, "Error parsing --fs: {}", o),
|
2020-06-04 19:19:24 +00:00
|
|
|
ParseFsSockMissing => write!(f, "Error parsing --fs: socket missing"),
|
2020-04-03 09:28:04 +00:00
|
|
|
ParseFsTagMissing => write!(f, "Error parsing --fs: tag missing"),
|
|
|
|
InvalidCacheSizeWithDaxOff => {
|
|
|
|
write!(f, "Error parsing --fs: cache_size used with dax=on")
|
|
|
|
}
|
|
|
|
ParsePersistentMemory(o) => write!(f, "Error parsing --pmem: {}", o),
|
|
|
|
ParsePmemFileMissing => write!(f, "Error parsing --pmem: file missing"),
|
|
|
|
ParseVsock(o) => write!(f, "Error parsing --vsock: {}", o),
|
|
|
|
ParseVsockCidMissing => write!(f, "Error parsing --vsock: cid missing"),
|
2020-06-04 19:19:24 +00:00
|
|
|
ParseVsockSockMissing => write!(f, "Error parsing --vsock: socket missing"),
|
2020-04-03 09:28:04 +00:00
|
|
|
ParseMemory(o) => write!(f, "Error parsing --memory: {}", o),
|
2020-08-18 13:19:49 +00:00
|
|
|
ParseMemoryZone(o) => write!(f, "Error parsing --memory-zone: {}", o),
|
2020-09-03 18:06:38 +00:00
|
|
|
ParseMemoryZoneIdMissing => write!(f, "Error parsing --memory-zone: id missing"),
|
2020-04-03 09:28:04 +00:00
|
|
|
ParseNetwork(o) => write!(f, "Error parsing --net: {}", o),
|
|
|
|
ParseDisk(o) => write!(f, "Error parsing --disk: {}", o),
|
2021-03-25 17:01:21 +00:00
|
|
|
ParseRng(o) => write!(f, "Error parsing --rng: {}", o),
|
2020-10-14 08:48:50 +00:00
|
|
|
ParseBalloon(o) => write!(f, "Error parsing --balloon: {}", o),
|
2020-04-07 12:50:19 +00:00
|
|
|
ParseRestore(o) => write!(f, "Error parsing --restore: {}", o),
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
ParseSgxEpc(o) => write!(f, "Error parsing --sgx-epc: {}", o),
|
2021-07-09 09:22:35 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
2021-07-09 08:48:32 +00:00
|
|
|
ParseSgxEpcIdMissing => write!(f, "Error parsing --sgx-epc: id missing"),
|
2020-08-28 16:20:12 +00:00
|
|
|
ParseNuma(o) => write!(f, "Error parsing --numa: {}", o),
|
2020-04-07 12:50:19 +00:00
|
|
|
ParseRestoreSourceUrlMissing => {
|
|
|
|
write!(f, "Error parsing --restore: source_url missing")
|
|
|
|
}
|
2021-06-04 14:40:13 +00:00
|
|
|
ParseUserDeviceSocketMissing => {
|
|
|
|
write!(f, "Error parsing --user-device: socket missing")
|
|
|
|
}
|
|
|
|
ParseUserDevice(o) => write!(f, "Error parsing --user-device: {}", o),
|
2020-04-06 15:20:27 +00:00
|
|
|
Validation(v) => write!(f, "Error validating configuration: {}", v),
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
ParseTdx(o) => write!(f, "Error parsing --tdx: {}", o),
|
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
FirmwarePathMissing => write!(f, "TDX firmware missing"),
|
2021-10-11 14:51:30 +00:00
|
|
|
ParsePlatform(o) => write!(f, "Error parsing --platform: {}", o),
|
2020-04-03 09:28:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-24 07:30:50 +00:00
|
|
|
pub type Result<T> = result::Result<T, Error>;
|
2019-05-23 19:48:05 +00:00
|
|
|
|
|
|
|
pub struct VmParams<'a> {
|
|
|
|
pub cpus: &'a str,
|
|
|
|
pub memory: &'a str,
|
2020-08-18 13:19:49 +00:00
|
|
|
pub memory_zones: Option<Vec<&'a str>>,
|
2019-09-19 07:52:55 +00:00
|
|
|
pub kernel: Option<&'a str>,
|
2020-03-15 17:51:17 +00:00
|
|
|
pub initramfs: Option<&'a str>,
|
2019-05-23 19:48:05 +00:00
|
|
|
pub cmdline: Option<&'a str>,
|
2019-07-08 22:48:39 +00:00
|
|
|
pub disks: Option<Vec<&'a str>>,
|
2019-07-08 22:31:13 +00:00
|
|
|
pub net: Option<Vec<&'a str>>,
|
2019-05-22 20:06:49 +00:00
|
|
|
pub rng: &'a str,
|
2020-10-14 08:48:50 +00:00
|
|
|
pub balloon: Option<&'a str>,
|
2019-06-27 16:14:11 +00:00
|
|
|
pub fs: Option<Vec<&'a str>>,
|
2019-06-28 08:45:58 +00:00
|
|
|
pub pmem: Option<Vec<&'a str>>,
|
2019-07-10 14:41:46 +00:00
|
|
|
pub serial: &'a str,
|
2019-07-22 19:29:02 +00:00
|
|
|
pub console: &'a str,
|
2019-07-15 09:42:40 +00:00
|
|
|
pub devices: Option<Vec<&'a str>>,
|
2021-06-04 14:40:13 +00:00
|
|
|
pub user_devices: Option<Vec<&'a str>>,
|
2020-04-28 17:10:32 +00:00
|
|
|
pub vsock: Option<&'a str>,
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
pub sgx_epc: Option<Vec<&'a str>>,
|
2020-08-28 16:20:12 +00:00
|
|
|
pub numa: Option<Vec<&'a str>>,
|
2020-09-25 09:39:45 +00:00
|
|
|
pub watchdog: bool,
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
pub tdx: Option<&'a str>,
|
2021-10-11 14:51:30 +00:00
|
|
|
pub platform: Option<&'a str>,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-12-12 17:12:42 +00:00
|
|
|
impl<'a> VmParams<'a> {
|
|
|
|
pub fn from_arg_matches(args: &'a ArgMatches) -> Self {
|
|
|
|
// These .unwrap()s cannot fail as there is a default value defined
|
|
|
|
let cpus = args.value_of("cpus").unwrap();
|
|
|
|
let memory = args.value_of("memory").unwrap();
|
2020-08-18 13:19:49 +00:00
|
|
|
let memory_zones: Option<Vec<&str>> = args.values_of("memory-zone").map(|x| x.collect());
|
2019-12-12 17:12:42 +00:00
|
|
|
let rng = args.value_of("rng").unwrap();
|
|
|
|
let serial = args.value_of("serial").unwrap();
|
|
|
|
|
|
|
|
let kernel = args.value_of("kernel");
|
2020-03-15 17:51:17 +00:00
|
|
|
let initramfs = args.value_of("initramfs");
|
2019-12-12 17:12:42 +00:00
|
|
|
let cmdline = args.value_of("cmdline");
|
|
|
|
|
|
|
|
let disks: Option<Vec<&str>> = args.values_of("disk").map(|x| x.collect());
|
|
|
|
let net: Option<Vec<&str>> = args.values_of("net").map(|x| x.collect());
|
|
|
|
let console = args.value_of("console").unwrap();
|
2020-10-14 08:48:50 +00:00
|
|
|
let balloon = args.value_of("balloon");
|
2019-12-12 17:12:42 +00:00
|
|
|
let fs: Option<Vec<&str>> = args.values_of("fs").map(|x| x.collect());
|
|
|
|
let pmem: Option<Vec<&str>> = args.values_of("pmem").map(|x| x.collect());
|
|
|
|
let devices: Option<Vec<&str>> = args.values_of("device").map(|x| x.collect());
|
2021-06-04 14:40:13 +00:00
|
|
|
let user_devices: Option<Vec<&str>> = args.values_of("user-device").map(|x| x.collect());
|
2020-04-28 17:10:32 +00:00
|
|
|
let vsock: Option<&str> = args.value_of("vsock");
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
let sgx_epc: Option<Vec<&str>> = args.values_of("sgx-epc").map(|x| x.collect());
|
2020-08-28 16:20:12 +00:00
|
|
|
let numa: Option<Vec<&str>> = args.values_of("numa").map(|x| x.collect());
|
2020-09-25 09:39:45 +00:00
|
|
|
let watchdog = args.is_present("watchdog");
|
2021-10-11 14:51:30 +00:00
|
|
|
let platform = args.value_of("platform");
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
let tdx = args.value_of("tdx");
|
2019-12-12 17:12:42 +00:00
|
|
|
VmParams {
|
|
|
|
cpus,
|
|
|
|
memory,
|
2020-08-18 13:19:49 +00:00
|
|
|
memory_zones,
|
2019-12-12 17:12:42 +00:00
|
|
|
kernel,
|
2020-03-15 17:51:17 +00:00
|
|
|
initramfs,
|
2019-12-12 17:12:42 +00:00
|
|
|
cmdline,
|
|
|
|
disks,
|
|
|
|
net,
|
|
|
|
rng,
|
2020-10-14 08:48:50 +00:00
|
|
|
balloon,
|
2019-12-12 17:12:42 +00:00
|
|
|
fs,
|
|
|
|
pmem,
|
|
|
|
serial,
|
|
|
|
console,
|
|
|
|
devices,
|
2021-06-04 14:40:13 +00:00
|
|
|
user_devices,
|
2019-12-12 17:12:42 +00:00
|
|
|
vsock,
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
sgx_epc,
|
2020-08-28 16:20:12 +00:00
|
|
|
numa,
|
2020-09-25 09:39:45 +00:00
|
|
|
watchdog,
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
tdx,
|
2021-10-11 14:51:30 +00:00
|
|
|
platform,
|
2019-12-12 17:12:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-27 12:34:05 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub enum HotplugMethod {
|
|
|
|
Acpi,
|
|
|
|
VirtioMem,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for HotplugMethod {
|
|
|
|
fn default() -> Self {
|
|
|
|
HotplugMethod::Acpi
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum ParseHotplugMethodError {
|
|
|
|
InvalidValue(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for HotplugMethod {
|
|
|
|
type Err = ParseHotplugMethodError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
match s.to_lowercase().as_str() {
|
|
|
|
"acpi" => Ok(HotplugMethod::Acpi),
|
|
|
|
"virtio-mem" => Ok(HotplugMethod::VirtioMem),
|
|
|
|
_ => Err(ParseHotplugMethodError::InvalidValue(s.to_owned())),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-16 09:56:36 +00:00
|
|
|
pub enum CpuTopologyParseError {
|
|
|
|
InvalidValue(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub struct CpuTopology {
|
|
|
|
pub threads_per_core: u8,
|
|
|
|
pub cores_per_die: u8,
|
|
|
|
pub dies_per_package: u8,
|
|
|
|
pub packages: u8,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FromStr for CpuTopology {
|
|
|
|
type Err = CpuTopologyParseError;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
let parts: Vec<&str> = s.split(':').collect();
|
|
|
|
|
|
|
|
if parts.len() != 4 {
|
|
|
|
return Err(Self::Err::InvalidValue(s.to_owned()));
|
|
|
|
}
|
|
|
|
|
|
|
|
let t = CpuTopology {
|
|
|
|
threads_per_core: parts[0]
|
|
|
|
.parse()
|
|
|
|
.map_err(|_| Self::Err::InvalidValue(s.to_owned()))?,
|
|
|
|
cores_per_die: parts[1]
|
|
|
|
.parse()
|
|
|
|
.map_err(|_| Self::Err::InvalidValue(s.to_owned()))?,
|
|
|
|
dies_per_package: parts[2]
|
|
|
|
.parse()
|
|
|
|
.map_err(|_| Self::Err::InvalidValue(s.to_owned()))?,
|
|
|
|
packages: parts[3]
|
|
|
|
.parse()
|
|
|
|
.map_err(|_| Self::Err::InvalidValue(s.to_owned()))?,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-01 09:54:27 +00:00
|
|
|
fn default_cpuconfig_max_phys_bits() -> u8 {
|
|
|
|
DEFAULT_MAX_PHYS_BITS
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-10-16 05:38:42 +00:00
|
|
|
pub struct CpusConfig {
|
2019-11-25 14:18:03 +00:00
|
|
|
pub boot_vcpus: u8,
|
|
|
|
pub max_vcpus: u8,
|
2020-06-16 09:56:36 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub topology: Option<CpuTopology>,
|
2020-09-15 15:13:48 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub kvm_hyperv: bool,
|
2021-10-01 09:54:27 +00:00
|
|
|
#[serde(default = "default_cpuconfig_max_phys_bits")]
|
|
|
|
pub max_phys_bits: u8,
|
2019-10-16 05:38:42 +00:00
|
|
|
}
|
2019-05-23 19:48:05 +00:00
|
|
|
|
|
|
|
impl CpusConfig {
|
|
|
|
pub fn parse(cpus: &str) -> Result<Self> {
|
2020-03-27 11:51:09 +00:00
|
|
|
let mut parser = OptionParser::new();
|
2020-09-15 15:13:48 +00:00
|
|
|
parser
|
|
|
|
.add("boot")
|
|
|
|
.add("max")
|
|
|
|
.add("topology")
|
2020-10-13 07:17:42 +00:00
|
|
|
.add("kvm_hyperv")
|
|
|
|
.add("max_phys_bits");
|
2020-03-27 11:51:09 +00:00
|
|
|
parser.parse(cpus).map_err(Error::ParseCpus)?;
|
|
|
|
|
|
|
|
let boot_vcpus: u8 = parser
|
|
|
|
.convert("boot")
|
|
|
|
.map_err(Error::ParseCpus)?
|
|
|
|
.unwrap_or(DEFAULT_VCPUS);
|
|
|
|
let max_vcpus: u8 = parser
|
|
|
|
.convert("max")
|
|
|
|
.map_err(Error::ParseCpus)?
|
|
|
|
.unwrap_or(boot_vcpus);
|
2020-06-16 09:56:36 +00:00
|
|
|
let topology = parser.convert("topology").map_err(Error::ParseCpus)?;
|
2020-09-15 15:13:48 +00:00
|
|
|
let kvm_hyperv = parser
|
|
|
|
.convert::<Toggle>("kvm_hyperv")
|
|
|
|
.map_err(Error::ParseCpus)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-10-13 07:17:42 +00:00
|
|
|
let max_phys_bits = parser
|
|
|
|
.convert::<u8>("max_phys_bits")
|
2021-10-01 09:54:27 +00:00
|
|
|
.map_err(Error::ParseCpus)?
|
|
|
|
.unwrap_or(DEFAULT_MAX_PHYS_BITS);
|
2020-02-21 16:08:25 +00:00
|
|
|
|
|
|
|
Ok(CpusConfig {
|
|
|
|
boot_vcpus,
|
|
|
|
max_vcpus,
|
2020-06-16 09:56:36 +00:00
|
|
|
topology,
|
2020-09-15 15:13:48 +00:00
|
|
|
kvm_hyperv,
|
2020-10-13 07:17:42 +00:00
|
|
|
max_phys_bits,
|
2020-02-21 16:08:25 +00:00
|
|
|
})
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
impl Default for CpusConfig {
|
|
|
|
fn default() -> Self {
|
2019-10-16 05:38:42 +00:00
|
|
|
CpusConfig {
|
2019-11-25 14:18:03 +00:00
|
|
|
boot_vcpus: DEFAULT_VCPUS,
|
|
|
|
max_vcpus: DEFAULT_VCPUS,
|
2020-06-16 09:56:36 +00:00
|
|
|
topology: None,
|
2020-09-15 15:13:48 +00:00
|
|
|
kvm_hyperv: false,
|
2021-10-01 09:54:27 +00:00
|
|
|
max_phys_bits: DEFAULT_MAX_PHYS_BITS,
|
2019-10-16 05:38:42 +00:00
|
|
|
}
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 14:51:30 +00:00
|
|
|
fn default_platformconfig_num_pci_segments() -> u16 {
|
|
|
|
DEFAULT_NUM_PCI_SEGMENTS
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub struct PlatformConfig {
|
|
|
|
#[serde(default = "default_platformconfig_num_pci_segments")]
|
|
|
|
pub num_pci_segments: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PlatformConfig {
|
|
|
|
pub fn parse(platform: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser.add("num_pci_segments");
|
|
|
|
parser.parse(platform).map_err(Error::ParseCpus)?;
|
|
|
|
|
|
|
|
let num_pci_segments: u16 = parser
|
|
|
|
.convert("num_pci_segments")
|
|
|
|
.map_err(Error::ParsePlatform)?
|
|
|
|
.unwrap_or(DEFAULT_NUM_PCI_SEGMENTS);
|
|
|
|
Ok(PlatformConfig { num_pci_segments })
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn validate(&self) -> ValidationResult<()> {
|
|
|
|
if self.num_pci_segments == 0 || self.num_pci_segments > MAX_NUM_PCI_SEGMENTS {
|
|
|
|
return Err(ValidationError::InvalidNumPciSegments(
|
|
|
|
self.num_pci_segments,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for PlatformConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
PlatformConfig {
|
|
|
|
num_pci_segments: DEFAULT_NUM_PCI_SEGMENTS,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-18 13:19:49 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub struct MemoryZoneConfig {
|
2020-09-03 18:06:38 +00:00
|
|
|
pub id: String,
|
2020-08-18 13:19:49 +00:00
|
|
|
pub size: u64,
|
|
|
|
#[serde(default)]
|
|
|
|
pub file: Option<PathBuf>,
|
|
|
|
#[serde(default)]
|
|
|
|
pub shared: bool,
|
|
|
|
#[serde(default)]
|
|
|
|
pub hugepages: bool,
|
2020-08-25 15:36:19 +00:00
|
|
|
#[serde(default)]
|
2021-02-04 16:21:53 +00:00
|
|
|
pub hugepage_size: Option<u64>,
|
|
|
|
#[serde(default)]
|
2020-09-01 10:51:25 +00:00
|
|
|
pub host_numa_node: Option<u32>,
|
2020-09-10 10:20:22 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub hotplug_size: Option<u64>,
|
2020-09-14 18:23:45 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub hotplugged_size: Option<u64>,
|
2021-09-29 04:54:22 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub prefault: bool,
|
2020-08-18 13:19:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 16:51:36 +00:00
|
|
|
pub struct MemoryConfig {
|
2019-05-24 19:21:23 +00:00
|
|
|
pub size: u64,
|
2019-12-10 13:43:26 +00:00
|
|
|
#[serde(default)]
|
2019-11-19 23:40:57 +00:00
|
|
|
pub mergeable: bool,
|
2020-01-07 11:25:55 +00:00
|
|
|
#[serde(default)]
|
2020-03-04 02:16:07 +00:00
|
|
|
pub hotplug_method: HotplugMethod,
|
|
|
|
#[serde(default)]
|
2020-01-07 11:25:55 +00:00
|
|
|
pub hotplug_size: Option<u64>,
|
2020-04-22 21:20:17 +00:00
|
|
|
#[serde(default)]
|
2020-09-14 18:23:45 +00:00
|
|
|
pub hotplugged_size: Option<u64>,
|
|
|
|
#[serde(default)]
|
2020-04-22 21:20:17 +00:00
|
|
|
pub shared: bool,
|
|
|
|
#[serde(default)]
|
|
|
|
pub hugepages: bool,
|
2020-03-20 03:43:37 +00:00
|
|
|
#[serde(default)]
|
2021-02-04 16:21:53 +00:00
|
|
|
pub hugepage_size: Option<u64>,
|
|
|
|
#[serde(default)]
|
2021-09-29 04:54:22 +00:00
|
|
|
pub prefault: bool,
|
|
|
|
#[serde(default)]
|
2020-08-18 13:19:49 +00:00
|
|
|
pub zones: Option<Vec<MemoryZoneConfig>>,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 16:51:36 +00:00
|
|
|
impl MemoryConfig {
|
2020-08-18 13:19:49 +00:00
|
|
|
pub fn parse(memory: &str, memory_zones: Option<Vec<&str>>) -> Result<Self> {
|
2020-03-30 17:50:46 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("size")
|
|
|
|
.add("file")
|
|
|
|
.add("mergeable")
|
|
|
|
.add("hotplug_method")
|
2020-04-22 21:20:17 +00:00
|
|
|
.add("hotplug_size")
|
2020-09-14 18:23:45 +00:00
|
|
|
.add("hotplugged_size")
|
2020-04-22 21:20:17 +00:00
|
|
|
.add("shared")
|
2021-02-04 16:21:53 +00:00
|
|
|
.add("hugepages")
|
2021-09-29 04:54:22 +00:00
|
|
|
.add("hugepage_size")
|
|
|
|
.add("prefault");
|
2020-03-30 17:50:46 +00:00
|
|
|
parser.parse(memory).map_err(Error::ParseMemory)?;
|
|
|
|
|
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(ByteSized(DEFAULT_MEMORY_MB << 20))
|
|
|
|
.0;
|
|
|
|
let mergeable = parser
|
|
|
|
.convert::<Toggle>("mergeable")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let hotplug_method = parser
|
|
|
|
.convert("hotplug_method")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let hotplug_size = parser
|
|
|
|
.convert::<ByteSized>("hotplug_size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.map(|v| v.0);
|
2020-09-14 18:23:45 +00:00
|
|
|
let hotplugged_size = parser
|
|
|
|
.convert::<ByteSized>("hotplugged_size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.map(|v| v.0);
|
2020-04-22 21:20:17 +00:00
|
|
|
let shared = parser
|
|
|
|
.convert::<Toggle>("shared")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let hugepages = parser
|
|
|
|
.convert::<Toggle>("hugepages")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2021-02-04 16:21:53 +00:00
|
|
|
let hugepage_size = parser
|
|
|
|
.convert::<ByteSized>("hugepage_size")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.map(|v| v.0);
|
2021-09-29 04:54:22 +00:00
|
|
|
let prefault = parser
|
|
|
|
.convert::<Toggle>("prefault")
|
|
|
|
.map_err(Error::ParseMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-03-04 02:16:07 +00:00
|
|
|
|
2020-08-18 13:19:49 +00:00
|
|
|
let zones: Option<Vec<MemoryZoneConfig>> = if let Some(memory_zones) = &memory_zones {
|
|
|
|
let mut zones = Vec::new();
|
|
|
|
for memory_zone in memory_zones.iter() {
|
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
2020-09-03 18:06:38 +00:00
|
|
|
.add("id")
|
2020-08-18 13:19:49 +00:00
|
|
|
.add("size")
|
|
|
|
.add("file")
|
|
|
|
.add("shared")
|
2020-08-25 15:36:19 +00:00
|
|
|
.add("hugepages")
|
2021-02-04 16:21:53 +00:00
|
|
|
.add("hugepage_size")
|
2020-09-10 10:20:22 +00:00
|
|
|
.add("host_numa_node")
|
2020-09-14 18:23:45 +00:00
|
|
|
.add("hotplug_size")
|
2021-09-29 04:54:22 +00:00
|
|
|
.add("hotplugged_size")
|
|
|
|
.add("prefault");
|
2020-08-18 13:19:49 +00:00
|
|
|
parser.parse(memory_zone).map_err(Error::ParseMemoryZone)?;
|
|
|
|
|
2020-09-03 18:06:38 +00:00
|
|
|
let id = parser.get("id").ok_or(Error::ParseMemoryZoneIdMissing)?;
|
2020-08-18 13:19:49 +00:00
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.unwrap_or(ByteSized(DEFAULT_MEMORY_MB << 20))
|
|
|
|
.0;
|
|
|
|
let file = parser.get("file").map(PathBuf::from);
|
|
|
|
let shared = parser
|
|
|
|
.convert::<Toggle>("shared")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let hugepages = parser
|
|
|
|
.convert::<Toggle>("hugepages")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2021-02-04 16:21:53 +00:00
|
|
|
let hugepage_size = parser
|
|
|
|
.convert::<ByteSized>("hugepage_size")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.map(|v| v.0);
|
|
|
|
|
2020-08-25 15:36:19 +00:00
|
|
|
let host_numa_node = parser
|
2020-09-01 10:51:25 +00:00
|
|
|
.convert::<u32>("host_numa_node")
|
2020-08-25 15:36:19 +00:00
|
|
|
.map_err(Error::ParseMemoryZone)?;
|
2020-09-10 10:20:22 +00:00
|
|
|
let hotplug_size = parser
|
|
|
|
.convert::<ByteSized>("hotplug_size")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.map(|v| v.0);
|
2020-09-14 18:23:45 +00:00
|
|
|
let hotplugged_size = parser
|
|
|
|
.convert::<ByteSized>("hotplugged_size")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.map(|v| v.0);
|
2021-09-29 04:54:22 +00:00
|
|
|
let prefault = parser
|
|
|
|
.convert::<Toggle>("prefault")
|
|
|
|
.map_err(Error::ParseMemoryZone)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-08-18 13:19:49 +00:00
|
|
|
|
|
|
|
zones.push(MemoryZoneConfig {
|
2020-09-03 18:06:38 +00:00
|
|
|
id,
|
2020-08-18 13:19:49 +00:00
|
|
|
size,
|
|
|
|
file,
|
|
|
|
shared,
|
|
|
|
hugepages,
|
2021-02-04 16:21:53 +00:00
|
|
|
hugepage_size,
|
2020-08-25 15:36:19 +00:00
|
|
|
host_numa_node,
|
2020-09-10 10:20:22 +00:00
|
|
|
hotplug_size,
|
2020-09-14 18:23:45 +00:00
|
|
|
hotplugged_size,
|
2021-09-29 04:54:22 +00:00
|
|
|
prefault,
|
2020-08-18 13:19:49 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
Some(zones)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2019-05-24 19:21:23 +00:00
|
|
|
Ok(MemoryConfig {
|
2020-03-30 17:50:46 +00:00
|
|
|
size,
|
|
|
|
mergeable,
|
2020-03-04 02:16:07 +00:00
|
|
|
hotplug_method,
|
2020-03-30 17:50:46 +00:00
|
|
|
hotplug_size,
|
2020-09-14 18:23:45 +00:00
|
|
|
hotplugged_size,
|
2020-04-22 21:20:17 +00:00
|
|
|
shared,
|
|
|
|
hugepages,
|
2021-02-04 16:21:53 +00:00
|
|
|
hugepage_size,
|
2021-09-29 04:54:22 +00:00
|
|
|
prefault,
|
2020-08-18 13:19:49 +00:00
|
|
|
zones,
|
2019-05-24 19:21:23 +00:00
|
|
|
})
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
2020-08-25 06:44:21 +00:00
|
|
|
|
|
|
|
pub fn total_size(&self) -> u64 {
|
|
|
|
let mut size = self.size;
|
|
|
|
if let Some(hotplugged_size) = self.hotplugged_size {
|
|
|
|
size += hotplugged_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(zones) = &self.zones {
|
|
|
|
for zone in zones.iter() {
|
|
|
|
size += zone.size;
|
|
|
|
if let Some(hotplugged_size) = zone.hotplugged_size {
|
|
|
|
size += hotplugged_size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size
|
|
|
|
}
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
impl Default for MemoryConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
MemoryConfig {
|
|
|
|
size: DEFAULT_MEMORY_MB << 20,
|
2019-11-19 23:40:57 +00:00
|
|
|
mergeable: false,
|
2020-03-04 02:16:07 +00:00
|
|
|
hotplug_method: HotplugMethod::Acpi,
|
2020-01-07 11:25:55 +00:00
|
|
|
hotplug_size: None,
|
2020-09-14 18:23:45 +00:00
|
|
|
hotplugged_size: None,
|
2020-04-22 21:20:17 +00:00
|
|
|
shared: false,
|
|
|
|
hugepages: false,
|
2021-02-04 16:21:53 +00:00
|
|
|
hugepage_size: None,
|
2021-09-29 04:54:22 +00:00
|
|
|
prefault: false,
|
2020-08-18 13:19:49 +00:00
|
|
|
zones: None,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 16:54:00 +00:00
|
|
|
pub struct KernelConfig {
|
|
|
|
pub path: PathBuf,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2020-03-15 17:51:17 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub struct InitramfsConfig {
|
|
|
|
pub path: PathBuf,
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub struct CmdlineConfig {
|
2019-09-27 08:39:56 +00:00
|
|
|
pub args: String,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CmdlineConfig {
|
|
|
|
pub fn parse(cmdline: Option<&str>) -> Result<Self> {
|
2019-09-27 08:39:56 +00:00
|
|
|
let args = cmdline
|
2019-05-23 19:48:05 +00:00
|
|
|
.map(std::string::ToString::to_string)
|
|
|
|
.unwrap_or_else(String::new);
|
|
|
|
|
2019-09-27 16:06:53 +00:00
|
|
|
Ok(CmdlineConfig { args })
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:12:19 +00:00
|
|
|
pub struct DiskConfig {
|
2020-03-13 10:25:17 +00:00
|
|
|
pub path: Option<PathBuf>,
|
2019-10-02 21:01:36 +00:00
|
|
|
#[serde(default)]
|
2020-01-21 11:22:09 +00:00
|
|
|
pub readonly: bool,
|
|
|
|
#[serde(default)]
|
2020-01-21 11:36:27 +00:00
|
|
|
pub direct: bool,
|
|
|
|
#[serde(default)]
|
2019-10-02 21:01:36 +00:00
|
|
|
pub iommu: bool,
|
2020-01-24 15:31:13 +00:00
|
|
|
#[serde(default = "default_diskconfig_num_queues")]
|
|
|
|
pub num_queues: usize,
|
|
|
|
#[serde(default = "default_diskconfig_queue_size")]
|
|
|
|
pub queue_size: u16,
|
2020-01-28 11:43:15 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub vhost_user: bool,
|
|
|
|
pub vhost_socket: Option<String>,
|
2020-02-20 15:49:16 +00:00
|
|
|
#[serde(default = "default_diskconfig_poll_queue")]
|
|
|
|
pub poll_queue: bool,
|
2020-04-08 12:58:51 +00:00
|
|
|
#[serde(default)]
|
2021-02-26 20:06:10 +00:00
|
|
|
pub rate_limiter_config: Option<RateLimiterConfig>,
|
|
|
|
#[serde(default)]
|
2020-04-08 12:58:51 +00:00
|
|
|
pub id: Option<String>,
|
2020-11-17 17:11:53 +00:00
|
|
|
// For testing use only. Not exposed in API.
|
|
|
|
#[serde(default)]
|
|
|
|
pub disable_io_uring: bool,
|
2021-10-11 16:34:29 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2020-01-24 15:31:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn default_diskconfig_num_queues() -> usize {
|
|
|
|
DEFAULT_NUM_QUEUES_VUBLK
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_diskconfig_queue_size() -> u16 {
|
|
|
|
DEFAULT_QUEUE_SIZE_VUBLK
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2020-02-20 15:49:16 +00:00
|
|
|
fn default_diskconfig_poll_queue() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2020-03-31 10:46:11 +00:00
|
|
|
impl Default for DiskConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
path: None,
|
|
|
|
readonly: false,
|
|
|
|
direct: false,
|
|
|
|
iommu: false,
|
|
|
|
num_queues: default_diskconfig_num_queues(),
|
|
|
|
queue_size: default_diskconfig_queue_size(),
|
|
|
|
vhost_user: false,
|
|
|
|
vhost_socket: None,
|
|
|
|
poll_queue: default_diskconfig_poll_queue(),
|
2020-04-08 12:58:51 +00:00
|
|
|
id: None,
|
2020-11-17 17:11:53 +00:00
|
|
|
disable_io_uring: false,
|
2021-02-26 20:06:10 +00:00
|
|
|
rate_limiter_config: None,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment: 0,
|
2020-03-31 10:46:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:12:19 +00:00
|
|
|
impl DiskConfig {
|
2020-03-12 16:31:26 +00:00
|
|
|
pub const SYNTAX: &'static str = "Disk parameters \
|
2021-02-22 20:06:49 +00:00
|
|
|
\"path=<disk_image_path>,readonly=on|off,direct=on|off,iommu=on|off,\
|
|
|
|
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,\
|
2021-02-26 20:06:10 +00:00
|
|
|
vhost_user=on|off,socket=<vhost_user_socket_path>,poll_queue=on|off,\
|
|
|
|
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>,\
|
2021-10-11 16:34:29 +00:00
|
|
|
id=<device_id>,pci_segment=<segment_id>\"";
|
2020-03-12 16:31:26 +00:00
|
|
|
|
2019-09-23 17:12:19 +00:00
|
|
|
pub fn parse(disk: &str) -> Result<Self> {
|
2020-03-31 10:45:43 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("path")
|
|
|
|
.add("readonly")
|
|
|
|
.add("direct")
|
|
|
|
.add("iommu")
|
|
|
|
.add("queue_size")
|
|
|
|
.add("num_queues")
|
|
|
|
.add("vhost_user")
|
|
|
|
.add("socket")
|
2020-04-08 12:58:51 +00:00
|
|
|
.add("poll_queue")
|
2021-02-26 20:06:10 +00:00
|
|
|
.add("bw_size")
|
|
|
|
.add("bw_one_time_burst")
|
|
|
|
.add("bw_refill_time")
|
|
|
|
.add("ops_size")
|
|
|
|
.add("ops_one_time_burst")
|
|
|
|
.add("ops_refill_time")
|
2020-11-17 17:11:53 +00:00
|
|
|
.add("id")
|
2021-10-11 16:34:29 +00:00
|
|
|
.add("_disable_io_uring")
|
|
|
|
.add("pci_segment");
|
2020-03-31 10:45:43 +00:00
|
|
|
parser.parse(disk).map_err(Error::ParseDisk)?;
|
|
|
|
|
|
|
|
let path = parser.get("path").map(PathBuf::from);
|
|
|
|
let readonly = parser
|
|
|
|
.convert::<Toggle>("readonly")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let direct = parser
|
|
|
|
.convert::<Toggle>("direct")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let queue_size = parser
|
|
|
|
.convert("queue_size")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_else(default_diskconfig_queue_size);
|
|
|
|
let num_queues = parser
|
|
|
|
.convert("num_queues")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_else(default_diskconfig_num_queues);
|
|
|
|
let vhost_user = parser
|
2020-04-30 12:43:11 +00:00
|
|
|
.convert::<Toggle>("vhost_user")
|
2020-03-31 10:45:43 +00:00
|
|
|
.map_err(Error::ParseDisk)?
|
2020-04-30 12:43:11 +00:00
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-03-31 10:45:43 +00:00
|
|
|
let vhost_socket = parser.get("socket");
|
|
|
|
let poll_queue = parser
|
2020-04-30 12:43:11 +00:00
|
|
|
.convert::<Toggle>("poll_queue")
|
2020-03-31 10:45:43 +00:00
|
|
|
.map_err(Error::ParseDisk)?
|
2020-04-30 12:43:11 +00:00
|
|
|
.unwrap_or_else(|| Toggle(default_diskconfig_poll_queue()))
|
|
|
|
.0;
|
2020-04-08 12:58:51 +00:00
|
|
|
let id = parser.get("id");
|
2020-11-17 17:11:53 +00:00
|
|
|
let disable_io_uring = parser
|
|
|
|
.convert::<Toggle>("_disable_io_uring")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2021-10-11 16:34:29 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert("pci_segment")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
2021-02-26 20:06:10 +00:00
|
|
|
let bw_size = parser
|
|
|
|
.convert("bw_size")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bw_one_time_burst = parser
|
|
|
|
.convert("bw_one_time_burst")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bw_refill_time = parser
|
|
|
|
.convert("bw_refill_time")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let ops_size = parser
|
|
|
|
.convert("ops_size")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let ops_one_time_burst = parser
|
|
|
|
.convert("ops_one_time_burst")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let ops_refill_time = parser
|
|
|
|
.convert("ops_refill_time")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 {
|
|
|
|
Some(TokenBucketConfig {
|
|
|
|
size: bw_size,
|
|
|
|
one_time_burst: Some(bw_one_time_burst),
|
|
|
|
refill_time: bw_refill_time,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let ops_tb_config = if ops_size != 0 && ops_refill_time != 0 {
|
|
|
|
Some(TokenBucketConfig {
|
|
|
|
size: ops_size,
|
|
|
|
one_time_burst: Some(ops_one_time_burst),
|
|
|
|
refill_time: ops_refill_time,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let rate_limiter_config = if bw_tb_config.is_some() || ops_tb_config.is_some() {
|
|
|
|
Some(RateLimiterConfig {
|
|
|
|
bandwidth: bw_tb_config,
|
|
|
|
ops: ops_tb_config,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2020-03-31 10:45:43 +00:00
|
|
|
|
|
|
|
if parser.is_set("poll_queue") && !vhost_user {
|
|
|
|
warn!("poll_queue parameter currently only has effect when used vhost_user=true");
|
2020-03-13 10:25:17 +00:00
|
|
|
}
|
2020-01-28 11:43:15 +00:00
|
|
|
|
2019-05-23 19:48:05 +00:00
|
|
|
Ok(DiskConfig {
|
2020-03-13 10:25:17 +00:00
|
|
|
path,
|
2020-03-31 10:45:43 +00:00
|
|
|
readonly,
|
|
|
|
direct,
|
|
|
|
iommu,
|
2020-01-24 15:31:13 +00:00
|
|
|
num_queues,
|
|
|
|
queue_size,
|
2020-01-28 11:43:15 +00:00
|
|
|
vhost_user,
|
2021-03-29 06:17:07 +00:00
|
|
|
vhost_socket,
|
2020-02-20 15:49:16 +00:00
|
|
|
poll_queue,
|
2021-03-29 06:17:07 +00:00
|
|
|
rate_limiter_config,
|
2020-04-08 12:58:51 +00:00
|
|
|
id,
|
2020-11-17 17:11:53 +00:00
|
|
|
disable_io_uring,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment,
|
2019-05-23 19:48:05 +00:00
|
|
|
})
|
|
|
|
}
|
2021-04-29 11:10:44 +00:00
|
|
|
|
|
|
|
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
|
|
|
|
if self.num_queues > vm_config.cpus.boot_vcpus as usize {
|
|
|
|
return Err(ValidationError::TooManyQueues);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 08:33:35 +00:00
|
|
|
#[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())),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:14:35 +00:00
|
|
|
pub struct NetConfig {
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_tap")]
|
2019-09-23 17:14:35 +00:00
|
|
|
pub tap: Option<String>,
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_ip")]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub ip: Ipv4Addr,
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_mask")]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub mask: Ipv4Addr,
|
2019-12-09 17:45:00 +00:00
|
|
|
#[serde(default = "default_netconfig_mac")]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub mac: MacAddr,
|
2020-06-05 11:00:34 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub host_mac: Option<MacAddr>,
|
2019-10-02 21:13:44 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-11-12 09:14:54 +00:00
|
|
|
#[serde(default = "default_netconfig_num_queues")]
|
|
|
|
pub num_queues: usize,
|
|
|
|
#[serde(default = "default_netconfig_queue_size")]
|
|
|
|
pub queue_size: u16,
|
2020-01-27 15:14:07 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub vhost_user: bool,
|
|
|
|
pub vhost_socket: Option<String>,
|
2020-04-08 12:58:51 +00:00
|
|
|
#[serde(default)]
|
2021-05-04 08:33:35 +00:00
|
|
|
pub vhost_mode: VhostMode,
|
|
|
|
#[serde(default)]
|
2020-04-08 12:58:51 +00:00
|
|
|
pub id: Option<String>,
|
2020-12-16 17:29:05 +00:00
|
|
|
#[serde(default)]
|
2021-01-27 04:52:50 +00:00
|
|
|
pub fds: Option<Vec<i32>>,
|
2021-03-17 22:41:52 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub rate_limiter_config: Option<RateLimiterConfig>,
|
2021-10-11 16:34:29 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 17:45:00 +00:00
|
|
|
fn default_netconfig_tap() -> Option<String> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_ip() -> Ipv4Addr {
|
|
|
|
Ipv4Addr::new(192, 168, 249, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_mask() -> Ipv4Addr {
|
|
|
|
Ipv4Addr::new(255, 255, 255, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_mac() -> MacAddr {
|
|
|
|
MacAddr::local_random()
|
|
|
|
}
|
|
|
|
|
2019-11-12 09:14:54 +00:00
|
|
|
fn default_netconfig_num_queues() -> usize {
|
|
|
|
DEFAULT_NUM_QUEUES_VUNET
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_netconfig_queue_size() -> u16 {
|
|
|
|
DEFAULT_QUEUE_SIZE_VUNET
|
|
|
|
}
|
|
|
|
|
2020-03-31 15:27:13 +00:00
|
|
|
impl Default for NetConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
tap: default_netconfig_tap(),
|
|
|
|
ip: default_netconfig_ip(),
|
|
|
|
mask: default_netconfig_mask(),
|
|
|
|
mac: default_netconfig_mac(),
|
2020-06-05 11:00:34 +00:00
|
|
|
host_mac: None,
|
2020-03-31 15:27:13 +00:00
|
|
|
iommu: false,
|
|
|
|
num_queues: default_netconfig_num_queues(),
|
|
|
|
queue_size: default_netconfig_queue_size(),
|
|
|
|
vhost_user: false,
|
|
|
|
vhost_socket: None,
|
2021-05-04 08:33:35 +00:00
|
|
|
vhost_mode: VhostMode::Client,
|
2020-04-08 12:58:51 +00:00
|
|
|
id: None,
|
2021-01-27 04:52:50 +00:00
|
|
|
fds: None,
|
2021-03-17 22:41:52 +00:00
|
|
|
rate_limiter_config: None,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment: 0,
|
2020-03-31 15:27:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:14:35 +00:00
|
|
|
impl NetConfig {
|
2020-03-12 16:31:26 +00:00
|
|
|
pub const SYNTAX: &'static str = "Network parameters \
|
2021-01-27 04:52:50 +00:00
|
|
|
\"tap=<if_name>,ip=<ip_addr>,mask=<net_mask>,mac=<mac_addr>,fd=<fd1:fd2...>,iommu=on|off,\
|
2021-05-04 08:33:35 +00:00
|
|
|
num_queues=<number_of_queues>,queue_size=<size_of_each_queue>,id=<device_id>,\
|
|
|
|
vhost_user=<vhost_user_enable>,socket=<vhost_user_socket_path>,vhost_mode=client|server,\
|
2021-03-17 22:41:52 +00:00
|
|
|
bw_size=<bytes>,bw_one_time_burst=<bytes>,bw_refill_time=<ms>,\
|
2021-10-11 16:34:29 +00:00
|
|
|
ops_size=<io_ops>,ops_one_time_burst=<io_ops>,ops_refill_time=<ms>,pci_segment=<segment_id>\"";
|
2020-03-12 16:31:26 +00:00
|
|
|
|
2019-09-23 17:14:35 +00:00
|
|
|
pub fn parse(net: &str) -> Result<Self> {
|
2020-03-31 15:54:42 +00:00
|
|
|
let mut parser = OptionParser::new();
|
2019-05-23 19:48:05 +00:00
|
|
|
|
2020-03-31 15:54:42 +00:00
|
|
|
parser
|
|
|
|
.add("tap")
|
|
|
|
.add("ip")
|
|
|
|
.add("mask")
|
|
|
|
.add("mac")
|
2020-05-15 09:00:38 +00:00
|
|
|
.add("host_mac")
|
2020-03-31 15:54:42 +00:00
|
|
|
.add("iommu")
|
|
|
|
.add("queue_size")
|
|
|
|
.add("num_queues")
|
|
|
|
.add("vhost_user")
|
2020-04-08 12:58:51 +00:00
|
|
|
.add("socket")
|
2021-05-04 08:33:35 +00:00
|
|
|
.add("vhost_mode")
|
2020-12-16 17:29:05 +00:00
|
|
|
.add("id")
|
2021-03-17 22:41:52 +00:00
|
|
|
.add("fd")
|
|
|
|
.add("bw_size")
|
|
|
|
.add("bw_one_time_burst")
|
|
|
|
.add("bw_refill_time")
|
|
|
|
.add("ops_size")
|
|
|
|
.add("ops_one_time_burst")
|
2021-10-11 16:34:29 +00:00
|
|
|
.add("ops_refill_time")
|
|
|
|
.add("pci_segment");
|
2020-03-31 15:54:42 +00:00
|
|
|
parser.parse(net).map_err(Error::ParseNetwork)?;
|
|
|
|
|
|
|
|
let tap = parser.get("tap");
|
|
|
|
let ip = parser
|
|
|
|
.convert("ip")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_ip);
|
|
|
|
let mask = parser
|
|
|
|
.convert("mask")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_mask);
|
|
|
|
let mac = parser
|
|
|
|
.convert("mac")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_mac);
|
2020-06-05 11:00:34 +00:00
|
|
|
let host_mac = parser.convert("host_mac").map_err(Error::ParseNetwork)?;
|
2020-03-31 15:54:42 +00:00
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let queue_size = parser
|
|
|
|
.convert("queue_size")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_queue_size);
|
|
|
|
let num_queues = parser
|
|
|
|
.convert("num_queues")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_else(default_netconfig_num_queues);
|
|
|
|
let vhost_user = parser
|
2020-04-30 12:43:11 +00:00
|
|
|
.convert::<Toggle>("vhost_user")
|
2020-03-31 15:54:42 +00:00
|
|
|
.map_err(Error::ParseNetwork)?
|
2020-04-30 12:43:11 +00:00
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-03-31 15:54:42 +00:00
|
|
|
let vhost_socket = parser.get("socket");
|
2021-05-04 08:33:35 +00:00
|
|
|
let vhost_mode = parser
|
|
|
|
.convert("vhost_mode")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_default();
|
2020-04-08 12:58:51 +00:00
|
|
|
let id = parser.get("id");
|
2021-01-27 04:52:50 +00:00
|
|
|
let fds = parser
|
|
|
|
.convert::<IntegerList>("fd")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.map(|v| v.0.iter().map(|e| *e as i32).collect());
|
2021-10-11 16:34:29 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert("pci_segment")
|
|
|
|
.map_err(Error::ParseNetwork)?
|
|
|
|
.unwrap_or_default();
|
2021-03-17 22:41:52 +00:00
|
|
|
let bw_size = parser
|
|
|
|
.convert("bw_size")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bw_one_time_burst = parser
|
|
|
|
.convert("bw_one_time_burst")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bw_refill_time = parser
|
|
|
|
.convert("bw_refill_time")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let ops_size = parser
|
|
|
|
.convert("ops_size")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let ops_one_time_burst = parser
|
|
|
|
.convert("ops_one_time_burst")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let ops_refill_time = parser
|
|
|
|
.convert("ops_refill_time")
|
|
|
|
.map_err(Error::ParseDisk)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 {
|
|
|
|
Some(TokenBucketConfig {
|
|
|
|
size: bw_size,
|
|
|
|
one_time_burst: Some(bw_one_time_burst),
|
|
|
|
refill_time: bw_refill_time,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let ops_tb_config = if ops_size != 0 && ops_refill_time != 0 {
|
|
|
|
Some(TokenBucketConfig {
|
|
|
|
size: ops_size,
|
|
|
|
one_time_burst: Some(ops_one_time_burst),
|
|
|
|
refill_time: ops_refill_time,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
let rate_limiter_config = if bw_tb_config.is_some() || ops_tb_config.is_some() {
|
|
|
|
Some(RateLimiterConfig {
|
|
|
|
bandwidth: bw_tb_config,
|
|
|
|
ops: ops_tb_config,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2020-07-29 20:20:50 +00:00
|
|
|
let config = NetConfig {
|
2019-10-02 21:13:44 +00:00
|
|
|
tap,
|
|
|
|
ip,
|
|
|
|
mask,
|
|
|
|
mac,
|
2020-05-15 09:00:38 +00:00
|
|
|
host_mac,
|
2019-10-02 21:13:44 +00:00
|
|
|
iommu,
|
2019-11-12 09:14:54 +00:00
|
|
|
num_queues,
|
|
|
|
queue_size,
|
2020-01-27 15:14:07 +00:00
|
|
|
vhost_user,
|
|
|
|
vhost_socket,
|
2021-05-04 08:33:35 +00:00
|
|
|
vhost_mode,
|
2020-04-08 12:58:51 +00:00
|
|
|
id,
|
2021-01-27 04:52:50 +00:00
|
|
|
fds,
|
2021-03-17 22:41:52 +00:00
|
|
|
rate_limiter_config,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment,
|
2020-07-29 20:20:50 +00:00
|
|
|
};
|
|
|
|
Ok(config)
|
|
|
|
}
|
2021-04-13 10:15:03 +00:00
|
|
|
|
2021-04-29 11:10:44 +00:00
|
|
|
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
|
2020-07-29 20:20:50 +00:00
|
|
|
if self.num_queues < 2 {
|
|
|
|
return Err(ValidationError::VnetQueueLowerThan2);
|
|
|
|
}
|
2021-01-27 04:52:50 +00:00
|
|
|
|
|
|
|
if self.fds.is_some() && self.fds.as_ref().unwrap().len() * 2 != self.num_queues {
|
|
|
|
return Err(ValidationError::VnetQueueFdMismatch);
|
|
|
|
}
|
|
|
|
|
2021-04-13 10:15:03 +00:00
|
|
|
if let Some(fds) = self.fds.as_ref() {
|
|
|
|
for fd in fds {
|
|
|
|
if *fd <= 2 {
|
|
|
|
return Err(ValidationError::VnetReservedFd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-29 11:10:44 +00:00
|
|
|
if (self.num_queues / 2) > vm_config.cpus.boot_vcpus as usize {
|
|
|
|
return Err(ValidationError::TooManyQueues);
|
|
|
|
}
|
|
|
|
|
2020-07-29 20:20:50 +00:00
|
|
|
Ok(())
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:15:50 +00:00
|
|
|
pub struct RngConfig {
|
|
|
|
pub src: PathBuf,
|
2019-10-04 18:18:49 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:15:50 +00:00
|
|
|
impl RngConfig {
|
|
|
|
pub fn parse(rng: &str) -> Result<Self> {
|
2020-04-02 09:17:10 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser.add("src").add("iommu");
|
2021-03-25 17:01:21 +00:00
|
|
|
parser.parse(rng).map_err(Error::ParseRng)?;
|
2020-04-02 09:17:10 +00:00
|
|
|
|
|
|
|
let src = PathBuf::from(
|
|
|
|
parser
|
|
|
|
.get("src")
|
|
|
|
.unwrap_or_else(|| DEFAULT_RNG_SOURCE.to_owned()),
|
|
|
|
);
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
2021-03-25 17:01:21 +00:00
|
|
|
.map_err(Error::ParseRng)?
|
2020-04-02 09:17:10 +00:00
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2019-10-04 18:18:49 +00:00
|
|
|
|
2020-04-02 09:17:10 +00:00
|
|
|
Ok(RngConfig { src, iommu })
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-27 09:40:50 +00:00
|
|
|
impl Default for RngConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
RngConfig {
|
|
|
|
src: PathBuf::from(DEFAULT_RNG_SOURCE),
|
2019-10-04 18:18:49 +00:00
|
|
|
iommu: false,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-14 08:48:50 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
pub struct BalloonConfig {
|
|
|
|
pub size: u64,
|
2021-01-21 11:22:29 +00:00
|
|
|
/// Option to deflate the balloon in case the guest is out of memory.
|
|
|
|
#[serde(default)]
|
|
|
|
pub deflate_on_oom: bool,
|
2020-10-14 08:48:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BalloonConfig {
|
2021-01-21 11:22:29 +00:00
|
|
|
pub const SYNTAX: &'static str =
|
|
|
|
"Balloon parameters \"size=<balloon_size>,deflate_on_oom=on|off\"";
|
2020-10-14 08:48:50 +00:00
|
|
|
|
|
|
|
pub fn parse(balloon: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser.add("size");
|
2021-01-21 11:22:29 +00:00
|
|
|
parser.add("deflate_on_oom");
|
2020-10-14 08:48:50 +00:00
|
|
|
parser.parse(balloon).map_err(Error::ParseBalloon)?;
|
|
|
|
|
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
|
|
|
.map_err(Error::ParseBalloon)?
|
|
|
|
.map(|v| v.0)
|
|
|
|
.unwrap_or(0);
|
|
|
|
|
2021-01-21 11:22:29 +00:00
|
|
|
let deflate_on_oom = parser
|
|
|
|
.convert::<Toggle>("deflate_on_oom")
|
|
|
|
.map_err(Error::ParseBalloon)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
|
|
|
|
Ok(BalloonConfig {
|
|
|
|
size,
|
|
|
|
deflate_on_oom,
|
|
|
|
})
|
2020-10-14 08:48:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:23:17 +00:00
|
|
|
pub struct FsConfig {
|
|
|
|
pub tag: String,
|
2020-06-04 19:19:24 +00:00
|
|
|
pub socket: PathBuf,
|
2019-12-09 17:22:51 +00:00
|
|
|
#[serde(default = "default_fsconfig_num_queues")]
|
2019-05-22 20:06:49 +00:00
|
|
|
pub num_queues: usize,
|
2019-12-09 17:22:51 +00:00
|
|
|
#[serde(default = "default_fsconfig_queue_size")]
|
2019-05-22 20:06:49 +00:00
|
|
|
pub queue_size: u16,
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
#[serde(default = "default_fsconfig_dax")]
|
|
|
|
pub dax: bool,
|
2019-12-09 17:22:51 +00:00
|
|
|
#[serde(default = "default_fsconfig_cache_size")]
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
pub cache_size: u64,
|
2020-04-27 07:55:25 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub id: Option<String>,
|
2021-10-11 16:34:29 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 17:22:51 +00:00
|
|
|
fn default_fsconfig_num_queues() -> usize {
|
|
|
|
1
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_fsconfig_queue_size() -> u16 {
|
|
|
|
1024
|
|
|
|
}
|
|
|
|
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
fn default_fsconfig_dax() -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn default_fsconfig_cache_size() -> u64 {
|
|
|
|
0x0002_0000_0000
|
2019-12-09 17:22:51 +00:00
|
|
|
}
|
|
|
|
|
2020-04-02 09:41:35 +00:00
|
|
|
impl Default for FsConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
tag: "".to_owned(),
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::new(),
|
2020-04-02 09:41:35 +00:00
|
|
|
num_queues: default_fsconfig_num_queues(),
|
|
|
|
queue_size: default_fsconfig_queue_size(),
|
|
|
|
dax: default_fsconfig_dax(),
|
|
|
|
cache_size: default_fsconfig_cache_size(),
|
2020-04-27 07:55:25 +00:00
|
|
|
id: None,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment: 0,
|
2020-04-02 09:41:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:23:17 +00:00
|
|
|
impl FsConfig {
|
2020-04-14 08:13:48 +00:00
|
|
|
pub const SYNTAX: &'static str = "virtio-fs parameters \
|
2020-06-04 19:19:24 +00:00
|
|
|
\"tag=<tag_name>,socket=<socket_path>,num_queues=<number_of_queues>,\
|
2020-04-14 08:13:48 +00:00
|
|
|
queue_size=<size_of_each_queue>,dax=on|off,cache_size=<DAX cache size: \
|
2021-10-11 16:34:29 +00:00
|
|
|
default 8Gib>,id=<device_id>,pci_segment=<segment_id>\"";
|
2020-04-14 08:13:48 +00:00
|
|
|
|
2019-09-23 17:23:17 +00:00
|
|
|
pub fn parse(fs: &str) -> Result<Self> {
|
2020-04-02 10:00:45 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("tag")
|
|
|
|
.add("dax")
|
|
|
|
.add("cache_size")
|
|
|
|
.add("queue_size")
|
|
|
|
.add("num_queues")
|
2020-06-04 19:19:24 +00:00
|
|
|
.add("socket")
|
2021-10-11 16:34:29 +00:00
|
|
|
.add("id")
|
|
|
|
.add("pci_segment");
|
2020-04-02 10:00:45 +00:00
|
|
|
parser.parse(fs).map_err(Error::ParseFileSystem)?;
|
2019-05-22 20:06:49 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let tag = parser.get("tag").ok_or(Error::ParseFsTagMissing)?;
|
2020-06-04 19:19:24 +00:00
|
|
|
let socket = PathBuf::from(parser.get("socket").ok_or(Error::ParseFsSockMissing)?);
|
2019-05-22 20:06:49 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let queue_size = parser
|
|
|
|
.convert("queue_size")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(default_fsconfig_queue_size);
|
|
|
|
let num_queues = parser
|
|
|
|
.convert("num_queues")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(default_fsconfig_num_queues);
|
2019-08-05 19:45:58 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let dax = parser
|
|
|
|
.convert::<Toggle>("dax")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(|| Toggle(default_fsconfig_dax()))
|
|
|
|
.0;
|
|
|
|
|
|
|
|
if parser.is_set("cache_size") && !dax {
|
|
|
|
return Err(Error::InvalidCacheSizeWithDaxOff);
|
2019-08-05 19:45:58 +00:00
|
|
|
}
|
2019-05-22 20:06:49 +00:00
|
|
|
|
2020-04-02 10:00:45 +00:00
|
|
|
let cache_size = parser
|
|
|
|
.convert::<ByteSized>("cache_size")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_else(|| ByteSized(default_fsconfig_cache_size()))
|
|
|
|
.0;
|
|
|
|
|
2020-04-27 07:55:25 +00:00
|
|
|
let id = parser.get("id");
|
|
|
|
|
2021-10-11 16:34:29 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert("pci_segment")
|
|
|
|
.map_err(Error::ParseFileSystem)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
2019-06-27 16:14:11 +00:00
|
|
|
Ok(FsConfig {
|
2020-04-02 10:00:45 +00:00
|
|
|
tag,
|
2020-06-04 19:19:24 +00:00
|
|
|
socket,
|
2019-05-22 20:06:49 +00:00
|
|
|
num_queues,
|
|
|
|
queue_size,
|
vmm: api: Adjust FsConfig for OpenAPI
The FsConfig structure has been recently adjusted so that the default
value matches between OpenAPI and CLI. Unfortunately, with the current
description, there is no way from the OpenAPI to describe a cache_size
value "None", so that DAX does not get enabled. Usually, using a Rust
"Option" works because the default value is None. But in this case, the
default value is Some(8G), which means we cannot describe a None.
This commit tackles the problem, introducing an explicit parameter
"dax", and leaving "cache_size" as a simple u64 integer.
This way, the default value is dax=true and cache_size=8G, but it lets
the opportunity to disable DAX entirely with dax=false, which will
simply ignore the cache_size value.
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
2019-12-11 09:44:26 +00:00
|
|
|
dax,
|
2019-08-05 19:45:58 +00:00
|
|
|
cache_size,
|
2020-04-27 07:55:25 +00:00
|
|
|
id,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment,
|
2019-06-27 16:14:11 +00:00
|
|
|
})
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
2021-04-29 11:10:44 +00:00
|
|
|
|
|
|
|
pub fn validate(&self, vm_config: &VmConfig) -> ValidationResult<()> {
|
|
|
|
if self.num_queues > vm_config.cpus.boot_vcpus as usize {
|
|
|
|
return Err(ValidationError::TooManyQueues);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-05-22 20:06:49 +00:00
|
|
|
}
|
|
|
|
|
2020-04-02 14:52:33 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
2019-09-23 17:27:56 +00:00
|
|
|
pub struct PmemConfig {
|
|
|
|
pub file: PathBuf,
|
2020-04-24 15:58:03 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub size: Option<u64>,
|
2019-10-04 18:27:22 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-11-19 23:47:31 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub mergeable: bool,
|
2020-03-19 10:13:53 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub discard_writes: bool,
|
2020-04-08 12:58:51 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub id: Option<String>,
|
2021-10-11 16:34:29 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2019-06-19 20:48:37 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:27:56 +00:00
|
|
|
impl PmemConfig {
|
2020-03-12 16:31:26 +00:00
|
|
|
pub const SYNTAX: &'static str = "Persistent memory parameters \
|
|
|
|
\"file=<backing_file_path>,size=<persistent_memory_size>,iommu=on|off,\
|
2021-10-11 16:34:29 +00:00
|
|
|
mergeable=on|off,discard_writes=on|off,id=<device_id>,pci_segment=<segment_id>\"";
|
2019-09-23 17:27:56 +00:00
|
|
|
pub fn parse(pmem: &str) -> Result<Self> {
|
2020-04-02 15:00:52 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("size")
|
|
|
|
.add("file")
|
|
|
|
.add("mergeable")
|
|
|
|
.add("iommu")
|
2020-04-08 12:58:51 +00:00
|
|
|
.add("discard_writes")
|
2021-10-11 16:34:29 +00:00
|
|
|
.add("id")
|
|
|
|
.add("pci_segment");
|
2020-04-02 15:00:52 +00:00
|
|
|
parser.parse(pmem).map_err(Error::ParsePersistentMemory)?;
|
2019-06-19 20:48:37 +00:00
|
|
|
|
2020-04-02 15:00:52 +00:00
|
|
|
let file = PathBuf::from(parser.get("file").ok_or(Error::ParsePmemFileMissing)?);
|
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
2020-04-24 15:58:03 +00:00
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.map(|v| v.0);
|
2020-04-02 15:00:52 +00:00
|
|
|
let mergeable = parser
|
|
|
|
.convert::<Toggle>("mergeable")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let discard_writes = parser
|
|
|
|
.convert::<Toggle>("discard_writes")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-04-08 12:58:51 +00:00
|
|
|
let id = parser.get("id");
|
2021-10-11 16:34:29 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert("pci_segment")
|
|
|
|
.map_err(Error::ParsePersistentMemory)?
|
|
|
|
.unwrap_or_default();
|
2019-06-19 20:48:37 +00:00
|
|
|
|
2019-06-28 08:45:58 +00:00
|
|
|
Ok(PmemConfig {
|
2020-04-02 15:00:52 +00:00
|
|
|
file,
|
|
|
|
size,
|
|
|
|
iommu,
|
|
|
|
mergeable,
|
|
|
|
discard_writes,
|
2020-04-08 12:58:51 +00:00
|
|
|
id,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment,
|
2019-06-28 08:45:58 +00:00
|
|
|
})
|
2019-06-19 20:48:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-07-22 19:29:02 +00:00
|
|
|
pub enum ConsoleOutputMode {
|
2019-07-10 14:41:46 +00:00
|
|
|
Off,
|
2021-01-14 03:03:53 +00:00
|
|
|
Pty,
|
2019-07-10 14:41:46 +00:00
|
|
|
Tty,
|
|
|
|
File,
|
2019-08-09 07:56:10 +00:00
|
|
|
Null,
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub struct ConsoleConfig {
|
2019-12-10 14:40:47 +00:00
|
|
|
#[serde(default = "default_consoleconfig_file")]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub file: Option<PathBuf>,
|
2019-07-22 19:29:02 +00:00
|
|
|
pub mode: ConsoleOutputMode,
|
2019-10-04 19:01:32 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
2019-12-10 14:40:47 +00:00
|
|
|
fn default_consoleconfig_file() -> Option<PathBuf> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:30:22 +00:00
|
|
|
impl ConsoleConfig {
|
2019-10-04 19:01:32 +00:00
|
|
|
pub fn parse(console: &str) -> Result<Self> {
|
2020-04-02 15:52:30 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add_valueless("off")
|
2021-01-14 03:03:53 +00:00
|
|
|
.add_valueless("pty")
|
2020-04-02 15:52:30 +00:00
|
|
|
.add_valueless("tty")
|
|
|
|
.add_valueless("null")
|
|
|
|
.add("file")
|
|
|
|
.add("iommu");
|
|
|
|
parser.parse(console).map_err(Error::ParseConsole)?;
|
2019-10-04 19:01:32 +00:00
|
|
|
|
2019-12-10 14:40:47 +00:00
|
|
|
let mut file: Option<PathBuf> = default_consoleconfig_file();
|
2019-10-04 19:01:32 +00:00
|
|
|
let mut mode: ConsoleOutputMode = ConsoleOutputMode::Off;
|
|
|
|
|
2020-04-02 15:52:30 +00:00
|
|
|
if parser.is_set("off") {
|
2021-01-14 03:03:53 +00:00
|
|
|
} else if parser.is_set("pty") {
|
|
|
|
mode = ConsoleOutputMode::Pty
|
2020-04-02 15:52:30 +00:00
|
|
|
} else if parser.is_set("tty") {
|
|
|
|
mode = ConsoleOutputMode::Tty
|
|
|
|
} else if parser.is_set("null") {
|
|
|
|
mode = ConsoleOutputMode::Null
|
|
|
|
} else if parser.is_set("file") {
|
|
|
|
mode = ConsoleOutputMode::File;
|
2020-04-07 14:28:20 +00:00
|
|
|
file =
|
|
|
|
Some(PathBuf::from(parser.get("file").ok_or(
|
|
|
|
Error::Validation(ValidationError::ConsoleFileMissing),
|
|
|
|
)?));
|
2020-04-02 15:52:30 +00:00
|
|
|
} else {
|
|
|
|
return Err(Error::ParseConsoleInvalidModeGiven);
|
2019-10-04 19:01:32 +00:00
|
|
|
}
|
2020-04-02 15:52:30 +00:00
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseConsole)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2019-10-04 19:01:32 +00:00
|
|
|
|
2021-03-29 06:17:07 +00:00
|
|
|
Ok(Self { file, mode, iommu })
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
2019-09-27 09:40:50 +00:00
|
|
|
|
|
|
|
pub fn default_serial() -> Self {
|
|
|
|
ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Null,
|
2019-10-04 19:01:32 +00:00
|
|
|
iommu: false,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default_console() -> Self {
|
|
|
|
ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Tty,
|
2019-10-04 19:01:32 +00:00
|
|
|
iommu: false,
|
2019-09-27 09:40:50 +00:00
|
|
|
}
|
|
|
|
}
|
2019-07-10 14:41:46 +00:00
|
|
|
}
|
|
|
|
|
2020-05-14 12:34:41 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
2019-09-23 17:31:41 +00:00
|
|
|
pub struct DeviceConfig {
|
|
|
|
pub path: PathBuf,
|
2019-10-07 16:03:58 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2020-03-09 13:08:59 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub id: Option<String>,
|
2021-10-07 15:54:32 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2019-07-15 09:42:40 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:31:41 +00:00
|
|
|
impl DeviceConfig {
|
2020-03-12 16:20:31 +00:00
|
|
|
pub const SYNTAX: &'static str =
|
2021-10-07 15:54:32 +00:00
|
|
|
"Direct device assignment parameters \"path=<device_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>\"";
|
2019-09-23 17:31:41 +00:00
|
|
|
pub fn parse(device: &str) -> Result<Self> {
|
2020-04-02 17:25:52 +00:00
|
|
|
let mut parser = OptionParser::new();
|
2021-10-07 15:54:32 +00:00
|
|
|
parser.add("path").add("id").add("iommu").add("pci_segment");
|
2020-04-02 17:25:52 +00:00
|
|
|
parser.parse(device).map_err(Error::ParseDevice)?;
|
2020-03-11 10:23:42 +00:00
|
|
|
|
2020-04-02 17:25:52 +00:00
|
|
|
let path = parser
|
|
|
|
.get("path")
|
|
|
|
.map(PathBuf::from)
|
|
|
|
.ok_or(Error::ParseDevicePathMissing)?;
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseDevice)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let id = parser.get("id");
|
2021-10-07 15:54:32 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert::<u16>("pci_segment")
|
|
|
|
.map_err(Error::ParseDevice)?
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
Ok(DeviceConfig {
|
|
|
|
path,
|
|
|
|
iommu,
|
|
|
|
id,
|
|
|
|
pci_segment,
|
|
|
|
})
|
2019-07-15 09:42:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-04 14:40:13 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
|
|
|
pub struct UserDeviceConfig {
|
|
|
|
pub socket: PathBuf,
|
|
|
|
#[serde(default)]
|
|
|
|
pub id: Option<String>,
|
2021-10-07 15:54:32 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2021-06-04 14:40:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UserDeviceConfig {
|
2021-10-07 15:54:32 +00:00
|
|
|
pub const SYNTAX: &'static str =
|
|
|
|
"Userspace device socket=<socket_path>,id=<device_id>,pci_segment=<segment_id>\"";
|
2021-06-04 14:40:13 +00:00
|
|
|
pub fn parse(user_device: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
2021-10-07 15:54:32 +00:00
|
|
|
parser.add("socket").add("id").add("pci_segment");
|
2021-06-04 14:40:13 +00:00
|
|
|
parser.parse(user_device).map_err(Error::ParseUserDevice)?;
|
|
|
|
|
|
|
|
let socket = parser
|
|
|
|
.get("socket")
|
|
|
|
.map(PathBuf::from)
|
|
|
|
.ok_or(Error::ParseUserDeviceSocketMissing)?;
|
|
|
|
let id = parser.get("id");
|
2021-10-07 15:54:32 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert::<u16>("pci_segment")
|
|
|
|
.map_err(Error::ParseUserDevice)?
|
|
|
|
.unwrap_or_default();
|
2021-06-04 14:40:13 +00:00
|
|
|
|
2021-10-07 15:54:32 +00:00
|
|
|
Ok(UserDeviceConfig {
|
|
|
|
socket,
|
|
|
|
id,
|
|
|
|
pci_segment,
|
|
|
|
})
|
2021-06-04 14:40:13 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-14 12:34:41 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
2019-09-23 17:46:35 +00:00
|
|
|
pub struct VsockConfig {
|
2019-09-04 17:39:17 +00:00
|
|
|
pub cid: u64,
|
2020-06-04 19:19:24 +00:00
|
|
|
pub socket: PathBuf,
|
2019-10-04 19:06:34 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2020-04-27 08:15:30 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub id: Option<String>,
|
2021-10-11 16:34:29 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub pci_segment: u16,
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:46:35 +00:00
|
|
|
impl VsockConfig {
|
2020-04-28 14:56:28 +00:00
|
|
|
pub const SYNTAX: &'static str = "Virtio VSOCK parameters \
|
2021-10-11 16:34:29 +00:00
|
|
|
\"cid=<context_id>,socket=<socket_path>,iommu=on|off,id=<device_id>,pci_segment=<segment_id>\"";
|
2019-09-23 17:46:35 +00:00
|
|
|
pub fn parse(vsock: &str) -> Result<Self> {
|
2020-04-02 17:39:19 +00:00
|
|
|
let mut parser = OptionParser::new();
|
2021-10-11 16:34:29 +00:00
|
|
|
parser
|
|
|
|
.add("socket")
|
|
|
|
.add("cid")
|
|
|
|
.add("iommu")
|
|
|
|
.add("id")
|
|
|
|
.add("pci_segment");
|
2020-04-02 17:39:19 +00:00
|
|
|
parser.parse(vsock).map_err(Error::ParseVsock)?;
|
2019-09-04 17:39:17 +00:00
|
|
|
|
2020-06-04 19:19:24 +00:00
|
|
|
let socket = parser
|
|
|
|
.get("socket")
|
2020-04-02 17:39:19 +00:00
|
|
|
.map(PathBuf::from)
|
|
|
|
.ok_or(Error::ParseVsockSockMissing)?;
|
|
|
|
let iommu = parser
|
|
|
|
.convert::<Toggle>("iommu")
|
|
|
|
.map_err(Error::ParseVsock)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
let cid = parser
|
|
|
|
.convert("cid")
|
|
|
|
.map_err(Error::ParseVsock)?
|
|
|
|
.ok_or(Error::ParseVsockCidMissing)?;
|
2020-04-27 08:15:30 +00:00
|
|
|
let id = parser.get("id");
|
2021-10-11 16:34:29 +00:00
|
|
|
let pci_segment = parser
|
|
|
|
.convert("pci_segment")
|
|
|
|
.map_err(Error::ParseVsock)?
|
|
|
|
.unwrap_or_default();
|
2019-09-04 17:39:17 +00:00
|
|
|
|
2020-04-27 08:15:30 +00:00
|
|
|
Ok(VsockConfig {
|
|
|
|
cid,
|
2020-06-04 19:19:24 +00:00
|
|
|
socket,
|
2020-04-27 08:15:30 +00:00
|
|
|
iommu,
|
|
|
|
id,
|
2021-10-11 16:34:29 +00:00
|
|
|
pci_segment,
|
2020-04-27 08:15:30 +00:00
|
|
|
})
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
|
|
|
pub struct TdxConfig {
|
|
|
|
pub firmware: PathBuf,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
impl TdxConfig {
|
|
|
|
pub fn parse(tdx: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser.add("firmware");
|
|
|
|
parser.parse(tdx).map_err(Error::ParseTdx)?;
|
|
|
|
let firmware = parser
|
|
|
|
.get("firmware")
|
|
|
|
.map(PathBuf::from)
|
|
|
|
.ok_or(Error::FirmwarePathMissing)?;
|
|
|
|
Ok(TdxConfig { firmware })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
|
|
|
pub struct SgxEpcConfig {
|
2021-07-09 08:48:32 +00:00
|
|
|
pub id: String,
|
2020-07-08 07:18:45 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub size: u64,
|
|
|
|
#[serde(default)]
|
|
|
|
pub prefault: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
impl SgxEpcConfig {
|
|
|
|
pub const SYNTAX: &'static str = "SGX EPC parameters \
|
2021-07-09 08:48:32 +00:00
|
|
|
\"id=<epc_section_identifier>,size=<epc_section_size>,prefault=on|off\"";
|
2020-07-08 07:18:45 +00:00
|
|
|
pub fn parse(sgx_epc: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
2021-07-09 08:48:32 +00:00
|
|
|
parser.add("id").add("size").add("prefault");
|
2020-07-08 07:18:45 +00:00
|
|
|
parser.parse(sgx_epc).map_err(Error::ParseSgxEpc)?;
|
|
|
|
|
2021-07-09 08:48:32 +00:00
|
|
|
let id = parser.get("id").ok_or(Error::ParseSgxEpcIdMissing)?;
|
2020-07-08 07:18:45 +00:00
|
|
|
let size = parser
|
|
|
|
.convert::<ByteSized>("size")
|
|
|
|
.map_err(Error::ParseSgxEpc)?
|
|
|
|
.unwrap_or(ByteSized(0))
|
|
|
|
.0;
|
|
|
|
let prefault = parser
|
|
|
|
.convert::<Toggle>("prefault")
|
|
|
|
.map_err(Error::ParseSgxEpc)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
|
|
|
|
2021-07-09 08:48:32 +00:00
|
|
|
Ok(SgxEpcConfig { id, size, prefault })
|
2020-07-08 07:18:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-31 12:20:20 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
|
|
|
pub struct NumaDistance {
|
|
|
|
#[serde(default)]
|
|
|
|
pub destination: u32,
|
|
|
|
#[serde(default)]
|
|
|
|
pub distance: u8,
|
|
|
|
}
|
|
|
|
|
2020-08-28 16:20:12 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
|
|
|
pub struct NumaConfig {
|
|
|
|
#[serde(default)]
|
2020-09-04 15:47:34 +00:00
|
|
|
pub guest_numa_id: u32,
|
2020-08-28 16:20:12 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub cpus: Option<Vec<u8>>,
|
2020-08-31 12:20:20 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub distances: Option<Vec<NumaDistance>>,
|
2020-09-04 07:36:10 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub memory_zones: Option<Vec<String>>,
|
2021-07-09 09:22:35 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
#[serde(default)]
|
|
|
|
pub sgx_epc_sections: Option<Vec<String>>,
|
2020-08-28 16:20:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl NumaConfig {
|
|
|
|
pub const SYNTAX: &'static str = "Settings related to a given NUMA node \
|
2020-09-04 15:47:34 +00:00
|
|
|
\"guest_numa_id=<node_id>,cpus=<cpus_id>,distances=<list_of_distances_to_destination_nodes>,\
|
2021-07-09 09:22:35 +00:00
|
|
|
memory_zones=<list_of_memory_zones>,sgx_epc_sections=<list_of_sgx_epc_sections>\"";
|
2020-08-28 16:20:12 +00:00
|
|
|
pub fn parse(numa: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
2020-09-04 07:36:10 +00:00
|
|
|
parser
|
2020-09-04 15:47:34 +00:00
|
|
|
.add("guest_numa_id")
|
2020-09-04 07:36:10 +00:00
|
|
|
.add("cpus")
|
|
|
|
.add("distances")
|
2021-07-09 09:22:35 +00:00
|
|
|
.add("memory_zones")
|
|
|
|
.add("sgx_epc_sections");
|
2020-08-28 16:20:12 +00:00
|
|
|
parser.parse(numa).map_err(Error::ParseNuma)?;
|
|
|
|
|
2020-09-04 15:47:34 +00:00
|
|
|
let guest_numa_id = parser
|
|
|
|
.convert::<u32>("guest_numa_id")
|
2020-08-28 16:20:12 +00:00
|
|
|
.map_err(Error::ParseNuma)?
|
|
|
|
.unwrap_or(0);
|
|
|
|
let cpus = parser
|
|
|
|
.convert::<IntegerList>("cpus")
|
|
|
|
.map_err(Error::ParseNuma)?
|
|
|
|
.map(|v| v.0.iter().map(|e| *e as u8).collect());
|
2020-08-31 12:20:20 +00:00
|
|
|
let distances = parser
|
|
|
|
.convert::<TupleTwoIntegers>("distances")
|
|
|
|
.map_err(Error::ParseNuma)?
|
|
|
|
.map(|v| {
|
|
|
|
v.0.iter()
|
|
|
|
.map(|(e1, e2)| NumaDistance {
|
|
|
|
destination: *e1 as u32,
|
|
|
|
distance: *e2 as u8,
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
});
|
2020-09-04 07:36:10 +00:00
|
|
|
let memory_zones = parser
|
|
|
|
.convert::<StringList>("memory_zones")
|
|
|
|
.map_err(Error::ParseNuma)?
|
|
|
|
.map(|v| v.0);
|
2021-07-09 09:22:35 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
let sgx_epc_sections = parser
|
|
|
|
.convert::<StringList>("sgx_epc_sections")
|
|
|
|
.map_err(Error::ParseNuma)?
|
|
|
|
.map(|v| v.0);
|
2020-08-31 12:20:20 +00:00
|
|
|
|
|
|
|
Ok(NumaConfig {
|
2020-09-04 15:47:34 +00:00
|
|
|
guest_numa_id,
|
2020-08-31 12:20:20 +00:00
|
|
|
cpus,
|
|
|
|
distances,
|
2020-09-04 07:36:10 +00:00
|
|
|
memory_zones,
|
2021-07-09 09:22:35 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
sgx_epc_sections,
|
2020-08-31 12:20:20 +00:00
|
|
|
})
|
2020-08-28 16:20:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-14 12:34:41 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Default)]
|
2020-04-07 12:50:19 +00:00
|
|
|
pub struct RestoreConfig {
|
|
|
|
pub source_url: PathBuf,
|
2020-04-07 13:54:33 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub prefault: bool,
|
2020-04-07 12:50:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RestoreConfig {
|
|
|
|
pub const SYNTAX: &'static str = "Restore from a VM snapshot. \
|
2020-04-07 13:54:33 +00:00
|
|
|
\nRestore parameters \"source_url=<source_url>,prefault=on|off\" \
|
|
|
|
\n`source_url` should be a valid URL (e.g file:///foo/bar or tcp://192.168.1.10/foo) \
|
|
|
|
\n`prefault` brings memory pages in when enabled (disabled by default)";
|
2020-04-07 12:50:19 +00:00
|
|
|
pub fn parse(restore: &str) -> Result<Self> {
|
|
|
|
let mut parser = OptionParser::new();
|
2020-04-07 13:54:33 +00:00
|
|
|
parser.add("source_url").add("prefault");
|
2020-04-07 12:50:19 +00:00
|
|
|
parser.parse(restore).map_err(Error::ParseRestore)?;
|
|
|
|
|
|
|
|
let source_url = parser
|
|
|
|
.get("source_url")
|
|
|
|
.map(PathBuf::from)
|
|
|
|
.ok_or(Error::ParseRestoreSourceUrlMissing)?;
|
2020-04-07 13:54:33 +00:00
|
|
|
let prefault = parser
|
|
|
|
.convert::<Toggle>("prefault")
|
|
|
|
.map_err(Error::ParseRestore)?
|
|
|
|
.unwrap_or(Toggle(false))
|
|
|
|
.0;
|
2020-04-07 12:50:19 +00:00
|
|
|
|
2020-04-07 13:54:33 +00:00
|
|
|
Ok(RestoreConfig {
|
|
|
|
source_url,
|
|
|
|
prefault,
|
|
|
|
})
|
2020-04-07 12:50:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-13 10:56:37 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
2019-09-23 17:46:35 +00:00
|
|
|
pub struct VmConfig {
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default)]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub cpus: CpusConfig,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default)]
|
2019-09-23 16:51:36 +00:00
|
|
|
pub memory: MemoryConfig,
|
2019-09-19 07:52:55 +00:00
|
|
|
pub kernel: Option<KernelConfig>,
|
2020-04-09 14:15:03 +00:00
|
|
|
#[serde(default)]
|
2020-03-15 17:51:17 +00:00
|
|
|
pub initramfs: Option<InitramfsConfig>,
|
2019-12-13 11:10:13 +00:00
|
|
|
#[serde(default)]
|
2019-05-23 19:48:05 +00:00
|
|
|
pub cmdline: CmdlineConfig,
|
2019-09-23 17:12:19 +00:00
|
|
|
pub disks: Option<Vec<DiskConfig>>,
|
2019-09-23 17:14:35 +00:00
|
|
|
pub net: Option<Vec<NetConfig>>,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default)]
|
2019-09-23 17:15:50 +00:00
|
|
|
pub rng: RngConfig,
|
2020-10-14 08:48:50 +00:00
|
|
|
pub balloon: Option<BalloonConfig>,
|
2019-09-23 17:23:17 +00:00
|
|
|
pub fs: Option<Vec<FsConfig>>,
|
2019-09-23 17:27:56 +00:00
|
|
|
pub pmem: Option<Vec<PmemConfig>>,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default = "ConsoleConfig::default_serial")]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub serial: ConsoleConfig,
|
2019-09-27 09:40:50 +00:00
|
|
|
#[serde(default = "ConsoleConfig::default_console")]
|
2019-09-23 17:30:22 +00:00
|
|
|
pub console: ConsoleConfig,
|
2019-09-23 17:31:41 +00:00
|
|
|
pub devices: Option<Vec<DeviceConfig>>,
|
2021-06-04 14:40:13 +00:00
|
|
|
pub user_devices: Option<Vec<UserDeviceConfig>>,
|
2020-04-28 17:10:32 +00:00
|
|
|
pub vsock: Option<VsockConfig>,
|
2019-09-18 14:13:56 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub iommu: bool,
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
pub sgx_epc: Option<Vec<SgxEpcConfig>>,
|
2020-08-28 16:20:12 +00:00
|
|
|
pub numa: Option<Vec<NumaConfig>>,
|
2020-09-25 09:39:45 +00:00
|
|
|
#[serde(default)]
|
|
|
|
pub watchdog: bool,
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
pub tdx: Option<TdxConfig>,
|
2021-10-11 14:51:30 +00:00
|
|
|
pub platform: Option<PlatformConfig>,
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 17:46:35 +00:00
|
|
|
impl VmConfig {
|
2020-04-06 15:20:27 +00:00
|
|
|
pub fn validate(&self) -> ValidationResult<()> {
|
2021-02-12 16:45:02 +00:00
|
|
|
#[cfg(not(feature = "tdx"))]
|
2020-04-06 16:00:38 +00:00
|
|
|
self.kernel.as_ref().ok_or(ValidationError::KernelMissing)?;
|
|
|
|
|
2021-03-03 15:38:02 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
{
|
|
|
|
let tdx_enabled = self.tdx.is_some();
|
|
|
|
if !tdx_enabled && self.kernel.is_none() {
|
|
|
|
return Err(ValidationError::KernelMissing);
|
|
|
|
}
|
|
|
|
if tdx_enabled && (self.cpus.max_vcpus != self.cpus.boot_vcpus) {
|
2021-03-25 17:01:21 +00:00
|
|
|
return Err(ValidationError::TdxNoCpuHotplug);
|
2021-03-03 15:38:02 +00:00
|
|
|
}
|
2021-05-10 13:12:12 +00:00
|
|
|
if tdx_enabled && self.kernel.is_some() {
|
|
|
|
return Err(ValidationError::TdxKernelSpecified);
|
|
|
|
}
|
2021-03-03 15:38:02 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 15:20:27 +00:00
|
|
|
if self.console.mode == ConsoleOutputMode::Tty && self.serial.mode == ConsoleOutputMode::Tty
|
|
|
|
{
|
|
|
|
return Err(ValidationError::DoubleTtyMode);
|
|
|
|
}
|
|
|
|
|
2020-04-07 14:28:20 +00:00
|
|
|
if self.console.mode == ConsoleOutputMode::File && self.console.file.is_none() {
|
|
|
|
return Err(ValidationError::ConsoleFileMissing);
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.serial.mode == ConsoleOutputMode::File && self.serial.file.is_none() {
|
|
|
|
return Err(ValidationError::ConsoleFileMissing);
|
|
|
|
}
|
|
|
|
|
2020-04-07 14:53:41 +00:00
|
|
|
if self.cpus.max_vcpus < self.cpus.boot_vcpus {
|
|
|
|
return Err(ValidationError::CpusMaxLowerThanBoot);
|
|
|
|
}
|
|
|
|
|
2020-04-07 14:53:41 +00:00
|
|
|
if let Some(disks) = &self.disks {
|
|
|
|
for disk in disks {
|
|
|
|
if disk.vhost_socket.as_ref().and(disk.path.as_ref()).is_some() {
|
|
|
|
return Err(ValidationError::DiskSocketAndPath);
|
|
|
|
}
|
2020-04-24 13:46:48 +00:00
|
|
|
if disk.vhost_user && !self.memory.shared {
|
|
|
|
return Err(ValidationError::VhostUserRequiresSharedMemory);
|
|
|
|
}
|
2020-11-06 09:44:17 +00:00
|
|
|
if disk.vhost_user && disk.vhost_socket.is_none() {
|
|
|
|
return Err(ValidationError::VhostUserMissingSocket);
|
|
|
|
}
|
2021-04-29 11:10:44 +00:00
|
|
|
disk.validate(self)?;
|
2020-04-24 13:46:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(nets) = &self.net {
|
|
|
|
for net in nets {
|
|
|
|
if net.vhost_user && !self.memory.shared {
|
|
|
|
return Err(ValidationError::VhostUserRequiresSharedMemory);
|
|
|
|
}
|
2021-04-29 11:10:44 +00:00
|
|
|
net.validate(self)?;
|
2020-04-24 13:46:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(fses) = &self.fs {
|
|
|
|
if !fses.is_empty() && !self.memory.shared {
|
|
|
|
return Err(ValidationError::VhostUserRequiresSharedMemory);
|
2020-04-07 14:53:41 +00:00
|
|
|
}
|
2021-04-29 11:10:44 +00:00
|
|
|
for fs in fses {
|
|
|
|
fs.validate(self)?;
|
|
|
|
}
|
2020-04-07 14:53:41 +00:00
|
|
|
}
|
|
|
|
|
2020-06-16 09:56:36 +00:00
|
|
|
if let Some(t) = &self.cpus.topology {
|
|
|
|
if t.threads_per_core == 0
|
|
|
|
|| t.cores_per_die == 0
|
|
|
|
|| t.dies_per_package == 0
|
|
|
|
|| t.packages == 0
|
|
|
|
{
|
|
|
|
return Err(ValidationError::CpuTopologyZeroPart);
|
|
|
|
}
|
|
|
|
|
|
|
|
let total = t.threads_per_core * t.cores_per_die * t.dies_per_package * t.packages;
|
|
|
|
if total != self.cpus.max_vcpus {
|
|
|
|
return Err(ValidationError::CpuTopologyCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-04 16:21:53 +00:00
|
|
|
if let Some(hugepage_size) = &self.memory.hugepage_size {
|
|
|
|
if !self.memory.hugepages {
|
|
|
|
return Err(ValidationError::HugePageSizeWithoutHugePages);
|
|
|
|
}
|
|
|
|
if !hugepage_size.is_power_of_two() {
|
|
|
|
return Err(ValidationError::InvalidHugePageSize(*hugepage_size));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-04 15:14:24 +00:00
|
|
|
if let Some(user_devices) = &self.user_devices {
|
|
|
|
if !user_devices.is_empty() && !self.memory.shared {
|
|
|
|
return Err(ValidationError::UserDevicesRequireSharedMemory);
|
|
|
|
}
|
|
|
|
}
|
2021-08-07 10:15:13 +00:00
|
|
|
|
|
|
|
if let Some(numa) = &self.numa {
|
|
|
|
let mut used_numa_node_memory_zones = HashMap::new();
|
|
|
|
for numa_node in numa.iter() {
|
|
|
|
for memory_zone in numa_node.memory_zones.clone().unwrap().iter() {
|
|
|
|
if !used_numa_node_memory_zones.contains_key(memory_zone) {
|
|
|
|
used_numa_node_memory_zones
|
|
|
|
.insert(memory_zone.to_string(), numa_node.guest_numa_id);
|
|
|
|
} else {
|
|
|
|
return Err(ValidationError::MemoryZoneReused(
|
|
|
|
memory_zone.to_string(),
|
|
|
|
*used_numa_node_memory_zones
|
|
|
|
.get(&memory_zone.to_string())
|
|
|
|
.unwrap(),
|
|
|
|
numa_node.guest_numa_id,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-11 14:51:30 +00:00
|
|
|
self.platform.as_ref().map(|p| p.validate()).transpose()?;
|
|
|
|
|
2020-04-06 15:20:27 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-09-23 17:46:35 +00:00
|
|
|
pub fn parse(vm_params: VmParams) -> Result<Self> {
|
2019-10-02 21:01:36 +00:00
|
|
|
let mut iommu = false;
|
2019-09-18 14:13:56 +00:00
|
|
|
|
2019-07-08 22:48:39 +00:00
|
|
|
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() {
|
2019-10-02 21:01:36 +00:00
|
|
|
let disk_config = DiskConfig::parse(item)?;
|
|
|
|
if disk_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
disk_config_list.push(disk_config);
|
2019-07-08 22:48:39 +00:00
|
|
|
}
|
|
|
|
disks = Some(disk_config_list);
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
|
2019-07-08 22:31:13 +00:00
|
|
|
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() {
|
2019-10-02 21:13:44 +00:00
|
|
|
let net_config = NetConfig::parse(item)?;
|
|
|
|
if net_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
net_config_list.push(net_config);
|
2019-07-08 22:31:13 +00:00
|
|
|
}
|
|
|
|
net = Some(net_config_list);
|
|
|
|
}
|
|
|
|
|
2019-10-04 18:18:49 +00:00
|
|
|
let rng = RngConfig::parse(vm_params.rng)?;
|
|
|
|
if rng.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
|
2020-10-14 08:48:50 +00:00
|
|
|
let mut balloon: Option<BalloonConfig> = None;
|
|
|
|
if let Some(balloon_params) = &vm_params.balloon {
|
|
|
|
balloon = Some(BalloonConfig::parse(balloon_params)?);
|
|
|
|
}
|
|
|
|
|
2019-06-27 16:14:11 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-06-28 08:45:58 +00:00
|
|
|
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() {
|
2019-10-04 18:27:22 +00:00
|
|
|
let pmem_config = PmemConfig::parse(item)?;
|
|
|
|
if pmem_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
pmem_config_list.push(pmem_config);
|
2019-06-28 08:45:58 +00:00
|
|
|
}
|
|
|
|
pmem = Some(pmem_config_list);
|
|
|
|
}
|
|
|
|
|
2019-07-22 19:29:02 +00:00
|
|
|
let console = ConsoleConfig::parse(vm_params.console)?;
|
2019-10-04 19:01:32 +00:00
|
|
|
if console.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
2019-07-22 19:29:02 +00:00
|
|
|
let serial = ConsoleConfig::parse(vm_params.serial)?;
|
|
|
|
|
2019-07-15 09:42:40 +00:00
|
|
|
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() {
|
2019-10-07 16:03:58 +00:00
|
|
|
let device_config = DeviceConfig::parse(item)?;
|
|
|
|
if device_config.iommu {
|
|
|
|
iommu = true;
|
|
|
|
}
|
|
|
|
device_config_list.push(device_config);
|
2019-07-15 09:42:40 +00:00
|
|
|
}
|
|
|
|
devices = Some(device_config_list);
|
|
|
|
}
|
|
|
|
|
2021-06-04 14:40:13 +00:00
|
|
|
let mut user_devices: Option<Vec<UserDeviceConfig>> = None;
|
|
|
|
if let Some(user_device_list) = &vm_params.user_devices {
|
|
|
|
let mut user_device_config_list = Vec::new();
|
|
|
|
for item in user_device_list.iter() {
|
|
|
|
let user_device_config = UserDeviceConfig::parse(item)?;
|
|
|
|
user_device_config_list.push(user_device_config);
|
|
|
|
}
|
|
|
|
user_devices = Some(user_device_config_list);
|
|
|
|
}
|
|
|
|
|
2020-04-28 17:10:32 +00:00
|
|
|
let mut vsock: Option<VsockConfig> = None;
|
|
|
|
if let Some(vs) = &vm_params.vsock {
|
|
|
|
let vsock_config = VsockConfig::parse(vs)?;
|
|
|
|
if vsock_config.iommu {
|
|
|
|
iommu = true;
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
2020-04-28 17:10:32 +00:00
|
|
|
vsock = Some(vsock_config);
|
2019-09-04 17:39:17 +00:00
|
|
|
}
|
2020-07-08 07:18:45 +00:00
|
|
|
|
2021-10-11 14:51:30 +00:00
|
|
|
let platform = vm_params.platform.map(PlatformConfig::parse).transpose()?;
|
|
|
|
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
let mut sgx_epc: Option<Vec<SgxEpcConfig>> = None;
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
{
|
|
|
|
if let Some(sgx_epc_list) = &vm_params.sgx_epc {
|
|
|
|
let mut sgx_epc_config_list = Vec::new();
|
|
|
|
for item in sgx_epc_list.iter() {
|
|
|
|
let sgx_epc_config = SgxEpcConfig::parse(item)?;
|
|
|
|
sgx_epc_config_list.push(sgx_epc_config);
|
|
|
|
}
|
|
|
|
sgx_epc = Some(sgx_epc_config_list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-28 16:20:12 +00:00
|
|
|
let mut numa: Option<Vec<NumaConfig>> = None;
|
|
|
|
if let Some(numa_list) = &vm_params.numa {
|
|
|
|
let mut numa_config_list = Vec::new();
|
|
|
|
for item in numa_list.iter() {
|
|
|
|
let numa_config = NumaConfig::parse(item)?;
|
|
|
|
numa_config_list.push(numa_config);
|
|
|
|
}
|
|
|
|
numa = Some(numa_config_list);
|
|
|
|
}
|
|
|
|
|
2019-09-19 07:52:55 +00:00
|
|
|
let mut kernel: Option<KernelConfig> = None;
|
|
|
|
if let Some(k) = vm_params.kernel {
|
|
|
|
kernel = Some(KernelConfig {
|
|
|
|
path: PathBuf::from(k),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-03-15 17:51:17 +00:00
|
|
|
let mut initramfs: Option<InitramfsConfig> = None;
|
|
|
|
if let Some(k) = vm_params.initramfs {
|
|
|
|
initramfs = Some(InitramfsConfig {
|
|
|
|
path: PathBuf::from(k),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
let tdx = vm_params.tdx.map(TdxConfig::parse).transpose()?;
|
|
|
|
|
2020-04-06 15:20:27 +00:00
|
|
|
let config = VmConfig {
|
2019-05-23 19:48:05 +00:00
|
|
|
cpus: CpusConfig::parse(vm_params.cpus)?,
|
2020-08-18 13:19:49 +00:00
|
|
|
memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?,
|
2019-09-19 07:52:55 +00:00
|
|
|
kernel,
|
2020-03-15 17:51:17 +00:00
|
|
|
initramfs,
|
2019-05-23 19:48:05 +00:00
|
|
|
cmdline: CmdlineConfig::parse(vm_params.cmdline)?,
|
|
|
|
disks,
|
2019-07-08 22:31:13 +00:00
|
|
|
net,
|
2019-10-04 18:18:49 +00:00
|
|
|
rng,
|
2020-10-14 08:48:50 +00:00
|
|
|
balloon,
|
2019-06-27 16:14:11 +00:00
|
|
|
fs,
|
2019-06-28 08:45:58 +00:00
|
|
|
pmem,
|
2019-07-22 19:29:02 +00:00
|
|
|
serial,
|
|
|
|
console,
|
2019-07-15 09:42:40 +00:00
|
|
|
devices,
|
2021-06-04 14:40:13 +00:00
|
|
|
user_devices,
|
2019-09-04 17:39:17 +00:00
|
|
|
vsock,
|
2019-09-18 14:13:56 +00:00
|
|
|
iommu,
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
sgx_epc,
|
2020-08-28 16:20:12 +00:00
|
|
|
numa,
|
2020-09-25 09:39:45 +00:00
|
|
|
watchdog: vm_params.watchdog,
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
tdx,
|
2021-10-11 14:51:30 +00:00
|
|
|
platform,
|
2020-04-06 15:20:27 +00:00
|
|
|
};
|
|
|
|
config.validate().map_err(Error::Validation)?;
|
|
|
|
Ok(config)
|
2019-05-23 19:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-27 11:15:55 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
2021-02-11 16:00:53 +00:00
|
|
|
fn test_option_parser() {
|
2020-03-27 11:15:55 +00:00
|
|
|
let mut parser = OptionParser::new();
|
|
|
|
parser
|
|
|
|
.add("size")
|
|
|
|
.add("mergeable")
|
|
|
|
.add("hotplug_method")
|
|
|
|
.add("hotplug_size");
|
|
|
|
|
2020-08-24 09:09:01 +00:00
|
|
|
assert!(parser.parse("size=128M,hanging_param").is_err());
|
|
|
|
assert!(parser.parse("size=128M,too_many_equals=foo=bar").is_err());
|
|
|
|
assert!(parser.parse("size=128M,file=/dev/shm").is_err());
|
|
|
|
assert!(parser.parse("size=128M").is_ok());
|
2020-03-27 11:15:55 +00:00
|
|
|
|
|
|
|
assert_eq!(parser.get("size"), Some("128M".to_owned()));
|
|
|
|
assert!(!parser.is_set("mergeable"));
|
|
|
|
assert!(parser.is_set("size"));
|
|
|
|
}
|
2020-03-27 12:30:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cpu_parsing() -> Result<()> {
|
|
|
|
assert_eq!(CpusConfig::parse("")?, CpusConfig::default());
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
CpusConfig::parse("boot=1")?,
|
|
|
|
CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
2020-06-16 09:56:36 +00:00
|
|
|
max_vcpus: 1,
|
2020-09-15 15:13:48 +00:00
|
|
|
..Default::default()
|
2020-03-27 12:30:13 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
CpusConfig::parse("boot=1,max=2")?,
|
|
|
|
CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
|
|
|
max_vcpus: 2,
|
2020-09-15 15:13:48 +00:00
|
|
|
..Default::default()
|
2020-06-16 09:56:36 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
CpusConfig::parse("boot=8,topology=2:2:1:2")?,
|
|
|
|
CpusConfig {
|
|
|
|
boot_vcpus: 8,
|
|
|
|
max_vcpus: 8,
|
|
|
|
topology: Some(CpuTopology {
|
|
|
|
threads_per_core: 2,
|
|
|
|
cores_per_die: 2,
|
|
|
|
dies_per_package: 1,
|
|
|
|
packages: 2
|
2020-09-15 15:13:48 +00:00
|
|
|
}),
|
|
|
|
..Default::default()
|
2020-03-27 12:30:13 +00:00
|
|
|
}
|
|
|
|
);
|
2020-06-16 09:56:36 +00:00
|
|
|
|
|
|
|
assert!(CpusConfig::parse("boot=8,topology=2:2:1").is_err());
|
|
|
|
assert!(CpusConfig::parse("boot=8,topology=2:2:1:x").is_err());
|
2020-09-15 15:13:48 +00:00
|
|
|
assert_eq!(
|
|
|
|
CpusConfig::parse("boot=1,kvm_hyperv=on")?,
|
|
|
|
CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
|
|
|
max_vcpus: 1,
|
|
|
|
kvm_hyperv: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
2020-03-27 12:30:13 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-27 12:54:48 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_mem_parsing() -> Result<()> {
|
2020-08-18 13:19:49 +00:00
|
|
|
assert_eq!(MemoryConfig::parse("", None)?, MemoryConfig::default());
|
2020-03-27 12:54:48 +00:00
|
|
|
// Default string
|
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("size=512M", None)?,
|
|
|
|
MemoryConfig::default()
|
|
|
|
);
|
2020-03-27 12:54:48 +00:00
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("size=512M,mergeable=on", None)?,
|
2020-03-27 12:54:48 +00:00
|
|
|
MemoryConfig {
|
|
|
|
size: 512 << 20,
|
|
|
|
mergeable: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("mergeable=on", None)?,
|
2020-03-27 12:54:48 +00:00
|
|
|
MemoryConfig {
|
|
|
|
mergeable: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("size=1G,mergeable=off", None)?,
|
2020-03-27 12:54:48 +00:00
|
|
|
MemoryConfig {
|
|
|
|
size: 1 << 30,
|
|
|
|
mergeable: false,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("hotplug_method=acpi", None)?,
|
2020-03-27 12:54:48 +00:00
|
|
|
MemoryConfig {
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("hotplug_method=acpi,hotplug_size=512M", None)?,
|
2020-03-27 12:54:48 +00:00
|
|
|
MemoryConfig {
|
|
|
|
hotplug_size: Some(512 << 20),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-08-18 13:19:49 +00:00
|
|
|
MemoryConfig::parse("hotplug_method=virtio-mem,hotplug_size=512M", None)?,
|
2020-03-27 12:54:48 +00:00
|
|
|
MemoryConfig {
|
|
|
|
hotplug_size: Some(512 << 20),
|
|
|
|
hotplug_method: HotplugMethod::VirtioMem,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
2021-02-04 16:21:53 +00:00
|
|
|
assert_eq!(
|
|
|
|
MemoryConfig::parse("hugepages=on,size=1G,hugepage_size=2M", None)?,
|
|
|
|
MemoryConfig {
|
|
|
|
hugepage_size: Some(2 << 20),
|
|
|
|
size: 1 << 30,
|
|
|
|
hugepages: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
2020-03-27 12:54:48 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-31 10:46:11 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_disk_parsing() -> Result<()> {
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
2020-04-08 12:58:51 +00:00
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,id=mydisk0")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
id: Some("mydisk0".to_owned()),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
2020-03-31 10:46:11 +00:00
|
|
|
assert_eq!(
|
2020-11-06 09:44:17 +00:00
|
|
|
DiskConfig::parse("vhost_user=true,socket=/tmp/sock")?,
|
2020-03-31 10:46:11 +00:00
|
|
|
DiskConfig {
|
2020-11-06 09:44:17 +00:00
|
|
|
vhost_socket: Some(String::from("/tmp/sock")),
|
2020-03-31 10:46:11 +00:00
|
|
|
vhost_user: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,iommu=on")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,iommu=on,queue_size=256")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
iommu: true,
|
|
|
|
queue_size: 256,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,iommu=on,queue_size=256,num_queues=4")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
iommu: true,
|
|
|
|
queue_size: 256,
|
|
|
|
num_queues: 4,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,direct=on")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
direct: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-05-20 16:39:30 +00:00
|
|
|
DiskConfig::parse("path=/path/to_file")?,
|
2020-03-31 10:46:11 +00:00
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-05-20 16:39:30 +00:00
|
|
|
DiskConfig::parse("path=/path/to_file")?,
|
2020-03-31 10:46:11 +00:00
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,poll_queue=false")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
poll_queue: false,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
DiskConfig::parse("path=/path/to_file,poll_queue=true")?,
|
|
|
|
DiskConfig {
|
|
|
|
path: Some(PathBuf::from("/path/to_file")),
|
|
|
|
poll_queue: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-03-31 15:27:13 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_net_parsing() -> Result<()> {
|
|
|
|
// mac address is random
|
|
|
|
assert_eq!(
|
2020-05-15 09:00:38 +00:00
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef")?,
|
2020-03-31 15:27:13 +00:00
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
2020-06-05 11:00:34 +00:00
|
|
|
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
2020-03-31 15:27:13 +00:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-04-08 12:58:51 +00:00
|
|
|
assert_eq!(
|
2020-05-15 09:00:38 +00:00
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,id=mynet0")?,
|
2020-04-08 12:58:51 +00:00
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
2020-06-05 11:00:34 +00:00
|
|
|
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
2020-04-08 12:58:51 +00:00
|
|
|
id: Some("mynet0".to_owned()),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-03-31 15:27:13 +00:00
|
|
|
assert_eq!(
|
|
|
|
NetConfig::parse(
|
2020-05-15 09:00:38 +00:00
|
|
|
"mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,tap=tap0,ip=192.168.100.1,mask=255.255.255.128"
|
2020-03-31 15:27:13 +00:00
|
|
|
)?,
|
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
2020-06-05 11:00:34 +00:00
|
|
|
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
2020-03-31 15:27:13 +00:00
|
|
|
tap: Some("tap0".to_owned()),
|
|
|
|
ip: "192.168.100.1".parse().unwrap(),
|
|
|
|
mask: "255.255.255.128".parse().unwrap(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
NetConfig::parse(
|
|
|
|
"mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,vhost_user=true,socket=/tmp/sock"
|
|
|
|
)?,
|
2020-03-31 15:27:13 +00:00
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
2020-06-05 11:00:34 +00:00
|
|
|
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
2020-03-31 15:27:13 +00:00
|
|
|
vhost_user: true,
|
2020-06-04 19:19:24 +00:00
|
|
|
vhost_socket: Some("/tmp/sock".to_owned()),
|
2020-03-31 15:27:13 +00:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
2020-05-15 09:00:38 +00:00
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34,host_mac=12:34:de:ad:be:ef,num_queues=4,queue_size=1024,iommu=on")?,
|
2020-03-31 15:27:13 +00:00
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
2020-06-05 11:00:34 +00:00
|
|
|
host_mac: Some(MacAddr::parse_str("12:34:de:ad:be:ef").unwrap()),
|
2020-03-31 15:27:13 +00:00
|
|
|
num_queues: 4,
|
|
|
|
queue_size: 1024,
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-12-16 17:29:05 +00:00
|
|
|
assert_eq!(
|
2021-01-27 04:52:50 +00:00
|
|
|
NetConfig::parse("mac=de:ad:be:ef:12:34,fd=3:7,num_queues=4")?,
|
2020-12-16 17:29:05 +00:00
|
|
|
NetConfig {
|
|
|
|
mac: MacAddr::parse_str("de:ad:be:ef:12:34").unwrap(),
|
2021-01-27 04:52:50 +00:00
|
|
|
fds: Some(vec![3, 7]),
|
|
|
|
num_queues: 4,
|
2020-12-16 17:29:05 +00:00
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2020-03-31 15:27:13 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 09:08:02 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_rng() -> Result<()> {
|
|
|
|
assert_eq!(RngConfig::parse("")?, RngConfig::default());
|
|
|
|
assert_eq!(
|
|
|
|
RngConfig::parse("src=/dev/random")?,
|
|
|
|
RngConfig {
|
|
|
|
src: PathBuf::from("/dev/random"),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
RngConfig::parse("src=/dev/random,iommu=on")?,
|
|
|
|
RngConfig {
|
|
|
|
src: PathBuf::from("/dev/random"),
|
|
|
|
iommu: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
RngConfig::parse("iommu=on")?,
|
|
|
|
RngConfig {
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 09:41:35 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_parse_fs() -> Result<()> {
|
|
|
|
// "tag" and "socket" must be supplied
|
|
|
|
assert!(FsConfig::parse("").is_err());
|
|
|
|
assert!(FsConfig::parse("tag=mytag").is_err());
|
2020-06-04 19:19:24 +00:00
|
|
|
assert!(FsConfig::parse("socket=/tmp/sock").is_err());
|
2020-04-02 09:41:35 +00:00
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
FsConfig::parse("tag=mytag,socket=/tmp/sock")?,
|
2020-04-02 09:41:35 +00:00
|
|
|
FsConfig {
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-02 09:41:35 +00:00
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
FsConfig::parse("tag=mytag,socket=/tmp/sock")?,
|
2020-04-02 09:41:35 +00:00
|
|
|
FsConfig {
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-02 09:41:35 +00:00
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
FsConfig::parse("tag=mytag,socket=/tmp/sock,num_queues=4,queue_size=1024")?,
|
2020-04-02 09:41:35 +00:00
|
|
|
FsConfig {
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-02 09:41:35 +00:00
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
num_queues: 4,
|
|
|
|
queue_size: 1024,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
// DAX on -> default cache size
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
FsConfig::parse("tag=mytag,socket=/tmp/sock,dax=on")?,
|
2020-04-02 09:41:35 +00:00
|
|
|
FsConfig {
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-02 09:41:35 +00:00
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
dax: true,
|
|
|
|
cache_size: default_fsconfig_cache_size(),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
FsConfig::parse("tag=mytag,socket=/tmp/sock,dax=on,cache_size=4G")?,
|
2020-04-02 09:41:35 +00:00
|
|
|
FsConfig {
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-02 09:41:35 +00:00
|
|
|
tag: "mytag".to_owned(),
|
|
|
|
dax: true,
|
|
|
|
cache_size: 4 << 30,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
// Cache size without DAX is an error
|
2020-06-04 19:19:24 +00:00
|
|
|
assert!(FsConfig::parse("tag=mytag,socket=/tmp/sock,dax=off,cache_size=4G").is_err());
|
2020-04-02 09:41:35 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 14:52:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pmem_parsing() -> Result<()> {
|
|
|
|
// Must always give a file and size
|
|
|
|
assert!(PmemConfig::parse("").is_err());
|
|
|
|
assert!(PmemConfig::parse("size=128M").is_err());
|
|
|
|
assert_eq!(
|
|
|
|
PmemConfig::parse("file=/tmp/pmem,size=128M")?,
|
|
|
|
PmemConfig {
|
|
|
|
file: PathBuf::from("/tmp/pmem"),
|
2020-04-24 15:58:03 +00:00
|
|
|
size: Some(128 << 20),
|
2020-04-02 14:52:33 +00:00
|
|
|
..Default::default()
|
2020-04-08 12:58:51 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
PmemConfig::parse("file=/tmp/pmem,size=128M,id=mypmem0")?,
|
|
|
|
PmemConfig {
|
|
|
|
file: PathBuf::from("/tmp/pmem"),
|
2020-04-24 15:58:03 +00:00
|
|
|
size: Some(128 << 20),
|
2020-04-08 12:58:51 +00:00
|
|
|
id: Some("mypmem0".to_owned()),
|
|
|
|
..Default::default()
|
2020-04-02 14:52:33 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
PmemConfig::parse("file=/tmp/pmem,size=128M,iommu=on,mergeable=on,discard_writes=on")?,
|
|
|
|
PmemConfig {
|
|
|
|
file: PathBuf::from("/tmp/pmem"),
|
2020-04-24 15:58:03 +00:00
|
|
|
size: Some(128 << 20),
|
2020-04-02 14:52:33 +00:00
|
|
|
mergeable: true,
|
|
|
|
discard_writes: true,
|
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 15:52:58 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_console_parsing() -> Result<()> {
|
|
|
|
assert!(ConsoleConfig::parse("").is_err());
|
|
|
|
assert!(ConsoleConfig::parse("badmode").is_err());
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("off")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Off,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
2021-01-14 03:03:53 +00:00
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("pty")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Pty,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
2020-04-02 15:52:58 +00:00
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("tty")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Tty,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("null")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Null,
|
|
|
|
iommu: false,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("file=/tmp/console")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::File,
|
|
|
|
iommu: false,
|
|
|
|
file: Some(PathBuf::from("/tmp/console"))
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("null,iommu=on")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::Null,
|
|
|
|
iommu: true,
|
|
|
|
file: None,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
ConsoleConfig::parse("file=/tmp/console,iommu=on")?,
|
|
|
|
ConsoleConfig {
|
|
|
|
mode: ConsoleOutputMode::File,
|
|
|
|
iommu: true,
|
|
|
|
file: Some(PathBuf::from("/tmp/console"))
|
|
|
|
}
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 17:16:18 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_device_parsing() -> Result<()> {
|
2020-04-02 17:25:52 +00:00
|
|
|
// Device must have a path provided
|
|
|
|
assert!(DeviceConfig::parse("").is_err());
|
2020-04-02 17:16:18 +00:00
|
|
|
assert_eq!(
|
|
|
|
DeviceConfig::parse("path=/path/to/device")?,
|
|
|
|
DeviceConfig {
|
|
|
|
path: PathBuf::from("/path/to/device"),
|
|
|
|
id: None,
|
2021-10-07 15:54:32 +00:00
|
|
|
iommu: false,
|
|
|
|
..Default::default()
|
2020-04-02 17:16:18 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
DeviceConfig::parse("path=/path/to/device,iommu=on")?,
|
|
|
|
DeviceConfig {
|
|
|
|
path: PathBuf::from("/path/to/device"),
|
|
|
|
id: None,
|
2021-10-07 15:54:32 +00:00
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
2020-04-02 17:16:18 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
DeviceConfig::parse("path=/path/to/device,iommu=on,id=mydevice0")?,
|
|
|
|
DeviceConfig {
|
|
|
|
path: PathBuf::from("/path/to/device"),
|
|
|
|
id: Some("mydevice0".to_owned()),
|
2021-10-07 15:54:32 +00:00
|
|
|
iommu: true,
|
|
|
|
..Default::default()
|
2020-04-02 17:16:18 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-02 17:21:04 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_vsock_parsing() -> Result<()> {
|
2020-06-04 19:19:24 +00:00
|
|
|
// socket and cid is required
|
2020-04-02 17:21:04 +00:00
|
|
|
assert!(VsockConfig::parse("").is_err());
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
VsockConfig::parse("socket=/tmp/sock,cid=1")?,
|
2020-04-02 17:21:04 +00:00
|
|
|
VsockConfig {
|
|
|
|
cid: 1,
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-27 08:15:30 +00:00
|
|
|
iommu: false,
|
|
|
|
id: None,
|
2021-10-11 16:34:29 +00:00
|
|
|
..Default::default()
|
2020-04-02 17:21:04 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2020-06-04 19:19:24 +00:00
|
|
|
VsockConfig::parse("socket=/tmp/sock,cid=1,iommu=on")?,
|
2020-04-02 17:21:04 +00:00
|
|
|
VsockConfig {
|
|
|
|
cid: 1,
|
2020-06-04 19:19:24 +00:00
|
|
|
socket: PathBuf::from("/tmp/sock"),
|
2020-04-27 08:15:30 +00:00
|
|
|
iommu: true,
|
|
|
|
id: None,
|
2021-10-11 16:34:29 +00:00
|
|
|
..Default::default()
|
2020-04-02 17:21:04 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-04-06 15:20:27 +00:00
|
|
|
|
|
|
|
#[test]
|
2021-02-11 16:00:53 +00:00
|
|
|
fn test_config_validation() {
|
2020-04-06 15:20:27 +00:00
|
|
|
let valid_config = VmConfig {
|
|
|
|
cpus: CpusConfig {
|
|
|
|
boot_vcpus: 1,
|
|
|
|
max_vcpus: 1,
|
2020-09-15 15:13:48 +00:00
|
|
|
..Default::default()
|
2020-04-06 15:20:27 +00:00
|
|
|
},
|
|
|
|
memory: MemoryConfig {
|
|
|
|
size: 536_870_912,
|
|
|
|
mergeable: false,
|
|
|
|
hotplug_method: HotplugMethod::Acpi,
|
|
|
|
hotplug_size: None,
|
2020-09-14 18:23:45 +00:00
|
|
|
hotplugged_size: None,
|
2020-04-22 21:20:17 +00:00
|
|
|
shared: false,
|
|
|
|
hugepages: false,
|
2021-02-04 16:21:53 +00:00
|
|
|
hugepage_size: None,
|
2021-09-29 04:54:22 +00:00
|
|
|
prefault: false,
|
2020-08-18 13:19:49 +00:00
|
|
|
zones: None,
|
2020-04-06 15:20:27 +00:00
|
|
|
},
|
2020-04-06 16:00:38 +00:00
|
|
|
kernel: Some(KernelConfig {
|
|
|
|
path: PathBuf::from("/path/to/kernel"),
|
|
|
|
}),
|
2020-04-06 15:20:27 +00:00
|
|
|
initramfs: None,
|
|
|
|
cmdline: CmdlineConfig {
|
|
|
|
args: String::from(""),
|
|
|
|
},
|
|
|
|
disks: None,
|
|
|
|
net: None,
|
|
|
|
rng: RngConfig {
|
|
|
|
src: PathBuf::from("/dev/urandom"),
|
|
|
|
iommu: false,
|
|
|
|
},
|
2020-10-14 08:48:50 +00:00
|
|
|
balloon: None,
|
2020-04-06 15:20:27 +00:00
|
|
|
fs: None,
|
|
|
|
pmem: None,
|
|
|
|
serial: ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Null,
|
|
|
|
iommu: false,
|
|
|
|
},
|
|
|
|
console: ConsoleConfig {
|
|
|
|
file: None,
|
|
|
|
mode: ConsoleOutputMode::Tty,
|
|
|
|
iommu: false,
|
|
|
|
},
|
|
|
|
devices: None,
|
2021-06-04 14:40:13 +00:00
|
|
|
user_devices: None,
|
2020-04-06 15:20:27 +00:00
|
|
|
vsock: None,
|
|
|
|
iommu: false,
|
2020-07-08 07:18:45 +00:00
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
|
|
sgx_epc: None,
|
2020-08-28 16:20:12 +00:00
|
|
|
numa: None,
|
2020-09-25 09:39:45 +00:00
|
|
|
watchdog: false,
|
2021-02-08 15:29:42 +00:00
|
|
|
#[cfg(feature = "tdx")]
|
|
|
|
tdx: None,
|
2021-10-11 14:51:30 +00:00
|
|
|
platform: None,
|
2020-04-06 15:20:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
assert!(valid_config.validate().is_ok());
|
|
|
|
|
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.serial.mode = ConsoleOutputMode::Tty;
|
|
|
|
invalid_config.console.mode = ConsoleOutputMode::Tty;
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-06 16:00:38 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.kernel = None;
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-07 14:28:20 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.serial.mode = ConsoleOutputMode::File;
|
|
|
|
invalid_config.serial.file = None;
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-07 14:53:41 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.cpus.max_vcpus = 16;
|
|
|
|
invalid_config.cpus.boot_vcpus = 32;
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-06-16 09:56:36 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.cpus.max_vcpus = 16;
|
|
|
|
invalid_config.cpus.boot_vcpus = 16;
|
|
|
|
invalid_config.cpus.topology = Some(CpuTopology {
|
|
|
|
threads_per_core: 2,
|
|
|
|
cores_per_die: 8,
|
|
|
|
dies_per_package: 1,
|
|
|
|
packages: 2,
|
|
|
|
});
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-07 14:53:41 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.disks = Some(vec![DiskConfig {
|
2020-06-04 19:19:24 +00:00
|
|
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
2020-04-07 14:53:41 +00:00
|
|
|
path: Some(PathBuf::from("/path/to/image")),
|
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-24 13:46:48 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.disks = Some(vec![DiskConfig {
|
|
|
|
vhost_user: true,
|
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-11-06 09:44:17 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.disks = Some(vec![DiskConfig {
|
|
|
|
vhost_user: true,
|
|
|
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-24 13:46:48 +00:00
|
|
|
let mut still_valid_config = valid_config.clone();
|
|
|
|
still_valid_config.disks = Some(vec![DiskConfig {
|
|
|
|
vhost_user: true,
|
2020-11-06 09:44:17 +00:00
|
|
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
2020-04-24 13:46:48 +00:00
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
still_valid_config.memory.shared = true;
|
|
|
|
assert!(still_valid_config.validate().is_ok());
|
|
|
|
|
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.net = Some(vec![NetConfig {
|
|
|
|
vhost_user: true,
|
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
|
|
|
let mut still_valid_config = valid_config.clone();
|
|
|
|
still_valid_config.net = Some(vec![NetConfig {
|
|
|
|
vhost_user: true,
|
2020-11-06 09:44:17 +00:00
|
|
|
vhost_socket: Some("/path/to/sock".to_owned()),
|
2020-04-24 13:46:48 +00:00
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
still_valid_config.memory.shared = true;
|
|
|
|
assert!(still_valid_config.validate().is_ok());
|
|
|
|
|
2021-04-13 10:15:03 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.net = Some(vec![NetConfig {
|
|
|
|
fds: Some(vec![0]),
|
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2020-04-24 13:46:48 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.fs = Some(vec![FsConfig {
|
|
|
|
..Default::default()
|
|
|
|
}]);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2021-02-04 16:21:53 +00:00
|
|
|
let mut still_valid_config = valid_config.clone();
|
2020-04-24 13:46:48 +00:00
|
|
|
still_valid_config.memory.shared = true;
|
|
|
|
assert!(still_valid_config.validate().is_ok());
|
|
|
|
|
2021-02-04 16:21:53 +00:00
|
|
|
let mut still_valid_config = valid_config.clone();
|
|
|
|
still_valid_config.memory.hugepages = true;
|
|
|
|
assert!(still_valid_config.validate().is_ok());
|
|
|
|
|
|
|
|
let mut still_valid_config = valid_config.clone();
|
|
|
|
still_valid_config.memory.hugepages = true;
|
|
|
|
still_valid_config.memory.hugepage_size = Some(2 << 20);
|
|
|
|
assert!(still_valid_config.validate().is_ok());
|
|
|
|
|
|
|
|
let mut invalid_config = valid_config.clone();
|
|
|
|
invalid_config.memory.hugepages = false;
|
|
|
|
invalid_config.memory.hugepage_size = Some(2 << 20);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
|
|
|
|
2021-10-11 14:51:30 +00:00
|
|
|
let mut invalid_config = valid_config.clone();
|
2021-02-04 16:21:53 +00:00
|
|
|
invalid_config.memory.hugepages = true;
|
|
|
|
invalid_config.memory.hugepage_size = Some(3 << 20);
|
|
|
|
assert!(invalid_config.validate().is_err());
|
2021-10-11 14:51:30 +00:00
|
|
|
|
|
|
|
let mut still_valid_config = valid_config.clone();
|
|
|
|
still_valid_config.platform = Some(PlatformConfig {
|
|
|
|
num_pci_segments: 16,
|
|
|
|
});
|
|
|
|
assert!(still_valid_config.validate().is_ok());
|
|
|
|
|
|
|
|
let mut invalid_config = valid_config;
|
|
|
|
invalid_config.platform = Some(PlatformConfig {
|
|
|
|
num_pci_segments: 17,
|
|
|
|
});
|
|
|
|
assert!(invalid_config.validate().is_err());
|
2020-04-06 15:20:27 +00:00
|
|
|
}
|
2020-03-27 11:15:55 +00:00
|
|
|
}
|