vmm: Add virtio-fs support to the VMM

The user can now share some files and directories with the guest by
providing the corresponding vhost-user socket. The virtiofsd daemon
should be started by the user before to start the VM.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-05-22 13:06:49 -07:00 committed by Samuel Ortiz
parent 1ddc8f2f0d
commit 2ede30b6d3
3 changed files with 150 additions and 17 deletions

View File

@ -63,6 +63,16 @@ fn main() {
.help("Path to entropy source")
.default_value(config::DEFAULT_RNG_SOURCE),
)
.arg(
Arg::with_name("fs")
.long("fs")
.help(
"virtio-fs parameters \"tag=<tag_name>,\
sock=<socket_path>,num_queues=<number_of_queues>,\
queue_size=<size_of_each_queue>\"",
)
.takes_value(true),
)
.get_matches();
// These .unwrap()s cannot fail as there is a default value defined
@ -85,14 +95,17 @@ fn main() {
// This .unwrap() cannot fail as there is a default value defined
let rng = cmd_arguments.value_of("rng").unwrap();
let fs = cmd_arguments.value_of("fs");
let vm_config = match config::VmConfig::parse(config::VmParams {
cpus,
memory,
kernel,
cmdline,
disks,
rng,
net,
rng,
fs,
}) {
Ok(config) => config,
Err(e) => {

View File

@ -38,6 +38,14 @@ pub enum Error<'a> {
ParseNetMaskParam(AddrParseError),
/// Failed parsing network mac parameter.
ParseNetMacParam(&'a str),
/// Failed parsing fs tag parameter.
ParseFsTagParam,
/// Failed parsing fs socket path parameter.
ParseFsSockParam,
/// Failed parsing fs number of queues parameter.
ParseFsNumQueuesParam(std::num::ParseIntError),
/// Failed parsing fs queue size parameter.
ParseFsQueueSizeParam(std::num::ParseIntError),
}
pub type Result<'a, T> = result::Result<T, Error<'a>>;
@ -47,8 +55,9 @@ pub struct VmParams<'a> {
pub kernel: &'a str,
pub cmdline: Option<&'a str>,
pub disks: Vec<&'a str>,
pub rng: &'a str,
pub net: Option<&'a str>,
pub rng: &'a str,
pub fs: Option<&'a str>,
}
pub struct CpusConfig(pub u8);
@ -128,18 +137,6 @@ impl<'a> DiskConfig<'a> {
}
}
pub struct RngConfig<'a> {
pub src: &'a Path,
}
impl<'a> RngConfig<'a> {
pub fn parse(rng: &'a str) -> Result<Self> {
Ok(RngConfig {
src: Path::new(rng),
})
}
}
pub struct NetConfig<'a> {
pub tap: Option<&'a str>,
pub ip: Ipv4Addr,
@ -195,14 +192,89 @@ impl<'a> NetConfig<'a> {
}
}
pub struct RngConfig<'a> {
pub src: &'a Path,
}
impl<'a> RngConfig<'a> {
pub fn parse(rng: &'a str) -> Result<Self> {
Ok(RngConfig {
src: Path::new(rng),
})
}
}
pub struct FsConfig<'a> {
pub tag: &'a str,
pub sock: &'a Path,
pub num_queues: usize,
pub queue_size: u16,
}
impl<'a> FsConfig<'a> {
pub fn parse(fs: Option<&'a str>) -> Result<Option<Self>> {
if fs.is_none() {
return Ok(None);
}
// Split the parameters based on the comma delimiter
let params_list: Vec<&str> = fs.unwrap().split(',').collect();
let mut tag: &str = "";
let mut sock: &str = "";
let mut num_queues_str: &str = "";
let mut queue_size_str: &str = "";
for param in params_list.iter() {
if param.starts_with("tag=") {
tag = &param[4..];
} else if param.starts_with("sock=") {
sock = &param[5..];
} else if param.starts_with("num_queues=") {
num_queues_str = &param[11..];
} else if param.starts_with("queue_size=") {
queue_size_str = &param[11..];
}
}
let mut num_queues: usize = 1;
let mut queue_size: u16 = 1024;
if tag.is_empty() {
return Err(Error::ParseFsTagParam);
}
if sock.is_empty() {
return Err(Error::ParseFsSockParam);
}
if !num_queues_str.is_empty() {
num_queues = num_queues_str
.parse()
.map_err(Error::ParseFsNumQueuesParam)?;
}
if !queue_size_str.is_empty() {
queue_size = queue_size_str
.parse()
.map_err(Error::ParseFsQueueSizeParam)?;
}
Ok(Some(FsConfig {
tag,
sock: Path::new(sock),
num_queues,
queue_size,
}))
}
}
pub struct VmConfig<'a> {
pub cpus: CpusConfig,
pub memory: MemoryConfig,
pub kernel: KernelConfig<'a>,
pub cmdline: CmdlineConfig,
pub disks: Vec<DiskConfig<'a>>,
pub rng: RngConfig<'a>,
pub net: Option<NetConfig<'a>>,
pub rng: RngConfig<'a>,
pub fs: Option<FsConfig<'a>>,
}
impl<'a> VmConfig<'a> {
@ -218,8 +290,9 @@ impl<'a> VmConfig<'a> {
kernel: KernelConfig::parse(vm_params.kernel)?,
cmdline: CmdlineConfig::parse(vm_params.cmdline)?,
disks,
rng: RngConfig::parse(vm_params.rng)?,
net: NetConfig::parse(vm_params.net)?,
rng: RngConfig::parse(vm_params.rng)?,
fs: FsConfig::parse(vm_params.fs)?,
})
}
}

View File

@ -28,6 +28,7 @@ use kvm_bindings::{
KVM_PIT_SPEAKER_DUMMY,
};
use kvm_ioctls::*;
use libc::O_TMPFILE;
use libc::{c_void, siginfo_t, EFD_NONBLOCK};
use linux_loader::loader::KernelLoader;
use net_util::Tap;
@ -38,10 +39,12 @@ use qcow::{self, ImageType, QcowFile};
use std::ffi::CString;
use std::fs::{File, OpenOptions};
use std::io::{self, stdout};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::{Arc, Barrier, Mutex};
use std::{result, str, thread};
use vm_allocator::SystemAllocator;
use vm_memory::guest_memory::FileOffset;
use vm_memory::{
Address, Bytes, Error as MmapError, GuestAddress, GuestMemory, GuestMemoryMmap,
GuestMemoryRegion, GuestUsize,
@ -150,6 +153,12 @@ pub enum Error {
/// Memory is overflow
MemOverflow,
/// Failed to create shared file.
SharedFileCreate(io::Error),
/// Failed to set shared file length.
SharedFileSetLen(io::Error),
}
pub type Result<T> = result::Result<T, Error>;
@ -171,6 +180,9 @@ pub enum DeviceManagerError {
/// Cannot create virtio-rng device
CreateVirtioRng(io::Error),
/// Cannot create virtio-fs device
CreateVirtioFs(vm_virtio::fs::Error),
/// Failed parsing disk image format
DetectImageType(qcow::Error),
@ -582,6 +594,25 @@ impl DeviceManager {
)?;
}
// Add virtio-fs if required
if let Some(fs_cfg) = &vm_cfg.fs {
if let Some(fs_sock) = fs_cfg.sock.to_str() {
let virtio_fs_device =
vm_virtio::Fs::new(fs_sock, fs_cfg.tag, fs_cfg.num_queues, fs_cfg.queue_size)
.map_err(DeviceManagerError::CreateVirtioFs)?;
DeviceManager::add_virtio_pci_device(
Box::new(virtio_fs_device),
memory.clone(),
allocator,
vm_fd,
&mut pci_root,
&mut mmio_bus,
&interrupt_info,
)?;
}
}
let pci = Arc::new(Mutex::new(PciConfigIo::new(pci_root)));
Ok(DeviceManager {
@ -813,7 +844,23 @@ impl<'a> Vm<'a> {
// Init guest memory
let arch_mem_regions = arch::arch_memory_regions(u64::from(&config.memory) << 20);
let guest_memory = GuestMemoryMmap::new(&arch_mem_regions).map_err(Error::GuestMemory)?;
let mut mem_regions = Vec::<(GuestAddress, usize, Option<FileOffset>)>::new();
for region in arch_mem_regions.iter() {
let file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(O_TMPFILE)
.open("/dev/shm")
.map_err(Error::SharedFileCreate)?;
file.set_len(region.1 as u64)
.map_err(Error::SharedFileSetLen)?;
mem_regions.push((region.0, region.1, Some(FileOffset::new(file, 0))));
}
let guest_memory = GuestMemoryMmap::with_files(&mem_regions).map_err(Error::GuestMemory)?;
guest_memory
.with_regions(|index, region| {