mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-11-04 19:11:11 +00:00
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:
parent
1ddc8f2f0d
commit
2ede30b6d3
15
src/main.rs
15
src/main.rs
@ -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) => {
|
||||
|
@ -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 = ¶m[4..];
|
||||
} else if param.starts_with("sock=") {
|
||||
sock = ¶m[5..];
|
||||
} else if param.starts_with("num_queues=") {
|
||||
num_queues_str = ¶m[11..];
|
||||
} else if param.starts_with("queue_size=") {
|
||||
queue_size_str = ¶m[11..];
|
||||
}
|
||||
}
|
||||
|
||||
let mut 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)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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| {
|
||||
|
Loading…
Reference in New Issue
Block a user