vm-virtio: fs: Factorize vhost-user setup

This patch factorizes the existing virtio-fs code by relying onto the
common code part of the vhost_user module in the vm-virtio crate.

In details, it factorizes the vhost-user setup, and reuses the error
types defined by the module instead of defining its own types.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2019-08-30 10:40:33 -07:00 committed by Rob Bradford
parent 56cad00f2e
commit b7d3ad9063
4 changed files with 55 additions and 182 deletions

View File

@ -47,7 +47,6 @@ const DEVICE_FEATURES_OK: u32 = 0x08;
const DEVICE_FAILED: u32 = 0x80;
const VIRTIO_F_VERSION_1: u32 = 32;
const VIRTIO_F_VERSION_1_BITMASK: u64 = 1 << VIRTIO_F_VERSION_1;
// Types taken from linux/virtio_ids.h
#[derive(Copy, Clone)]
@ -124,7 +123,7 @@ pub enum ActivateError {
/// Failed to create Vhost-user interrupt eventfd
VhostIrqCreate,
/// Failed to setup vhost-user daemon.
VhostUserSetup(vhost_user::fs::Error),
VhostUserSetup(vhost_user::Error),
/// Failed to setup vhost-user daemon.
VhostUserNetSetup(vhost_user::Error),
}

View File

@ -1,11 +1,11 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use super::Error as DeviceError;
use super::vu_common_ctrl::setup_vhost_user;
use super::{Error, Result};
use crate::{
ActivateError, ActivateResult, Queue, VirtioDevice, VirtioDeviceType,
VirtioInterrupt, VirtioInterruptType, VirtioSharedMemoryList,
VIRTIO_F_VERSION_1_BITMASK,
ActivateError, ActivateResult, Queue, VirtioDevice, VirtioDeviceType, VirtioInterrupt,
VirtioInterruptType, VirtioSharedMemoryList, VIRTIO_F_VERSION_1,
};
use epoll;
use libc::{self, EFD_NONBLOCK};
@ -22,8 +22,8 @@ use vhost_rs::vhost_user::message::{
use vhost_rs::vhost_user::{
HandlerResult, Master, MasterReqHandler, VhostUserMaster, VhostUserMasterReqHandler,
};
use vhost_rs::{VhostBackend, VhostUserMemoryRegionInfo, VringConfigData};
use vm_memory::{Address, Error as MmapError, GuestMemory, GuestMemoryMmap, GuestMemoryRegion};
use vhost_rs::VhostBackend;
use vm_memory::GuestMemoryMmap;
use vmm_sys_util::eventfd::EventFd;
const CONFIG_SPACE_TAG_SIZE: usize = 36;
@ -31,73 +31,6 @@ const CONFIG_SPACE_NUM_QUEUES_SIZE: usize = 4;
const CONFIG_SPACE_SIZE: usize = CONFIG_SPACE_TAG_SIZE + CONFIG_SPACE_NUM_QUEUES_SIZE;
const NUM_QUEUE_OFFSET: usize = 1;
#[derive(Debug)]
pub enum Error {
/// common
/// Invalid descriptor table address.
DescriptorTableAddress,
/// Invalid used address.
UsedAddress,
/// Invalid available address.
AvailAddress,
/// vhost
/// Creating kill eventfd failed.
CreateKillEventFd(io::Error),
/// Cloning kill eventfd failed.
CloneKillEventFd(io::Error),
/// Error while polling for events.
PollError(io::Error),
/// Failed to create irq eventfd.
IrqEventCreate(io::Error),
/// Failed to read vhost eventfd.
VhostIrqRead(io::Error),
/// vhost-user
/// Connection to socket failed.
VhostUserConnect(vhost_rs::Error),
/// Get features failed.
VhostUserGetFeatures(vhost_rs::Error),
/// Get protocol features failed.
VhostUserGetProtocolFeatures(vhost_rs::Error),
/// Set owner failed.
VhostUserSetOwner(vhost_rs::Error),
/// Set features failed.
VhostUserSetFeatures(vhost_rs::Error),
/// Set protocol features failed.
VhostUserSetProtocolFeatures(vhost_rs::Error),
/// Set mem table failed.
VhostUserSetMemTable(vhost_rs::Error),
/// Set vring num failed.
VhostUserSetVringNum(vhost_rs::Error),
/// Set vring addr failed.
VhostUserSetVringAddr(vhost_rs::Error),
/// Set vring base failed.
VhostUserSetVringBase(vhost_rs::Error),
/// Set vring call failed.
VhostUserSetVringCall(vhost_rs::Error),
/// Set vring kick failed.
VhostUserSetVringKick(vhost_rs::Error),
/// Set slave request fd failed.
VhostUserSetSlaveRequestFd(vhost_rs::Error),
/// Invalid features provided from vhost-user backend.
InvalidFeatures,
/// Missing file descriptor.
FdMissing,
/// Failure going through memory regions.
MemoryRegions(MmapError),
/// Failed to create the master request handler from slave.
MasterReqHandlerCreation(vhost_rs::vhost_user::Error),
}
type Result<T> = result::Result<T, Error>;
struct SlaveReqHandler {
cache_size: u64,
mmap_cache_addr: u64,
@ -185,9 +118,9 @@ struct FsEpollHandler<S: VhostUserMasterReqHandler> {
}
impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
fn run(&mut self) -> result::Result<(), DeviceError> {
fn run(&mut self) -> result::Result<(), Error> {
// Create the epoll file descriptor
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
let epoll_fd = epoll::create(true).map_err(Error::EpollCreateFd)?;
for (evt_index, vu_call_evt_queue) in self.vu_call_evt_queue_list.iter().enumerate() {
// Add events
@ -197,7 +130,7 @@ impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
vu_call_evt_queue.0.as_raw_fd(),
epoll::Event::new(epoll::Events::EPOLLIN, evt_index as u64),
)
.map_err(DeviceError::EpollCtl)?;
.map_err(Error::EpollCtl)?;
}
let kill_evt_index = self.vu_call_evt_queue_list.len();
@ -207,7 +140,7 @@ impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
self.kill_evt.as_raw_fd(),
epoll::Event::new(epoll::Events::EPOLLIN, kill_evt_index as u64),
)
.map_err(DeviceError::EpollCtl)?;
.map_err(Error::EpollCtl)?;
let slave_evt_index = if let Some(self_req_handler) = &self.slave_req_handler {
let index = kill_evt_index + 1;
@ -217,7 +150,7 @@ impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
self_req_handler.as_raw_fd(),
epoll::Event::new(epoll::Events::EPOLLIN, index as u64),
)
.map_err(DeviceError::EpollCtl)?;
.map_err(Error::EpollCtl)?;
Some(index)
} else {
@ -241,7 +174,7 @@ impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
// appropriate to retry, by calling into epoll_wait().
continue;
}
return Err(DeviceError::EpollWait(e));
return Err(Error::EpollWait(e));
}
};
@ -259,7 +192,7 @@ impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
) {
error!(
"Failed to signal used queue: {:?}",
DeviceError::FailedSignalingUsedQueue(e)
Error::FailedSignalingUsedQueue(e)
);
break 'epoll;
}
@ -272,7 +205,7 @@ impl<S: VhostUserMasterReqHandler> FsEpollHandler<S> {
if let Some(slave_req_handler) = self.slave_req_handler.as_mut() {
slave_req_handler
.handle_request()
.map_err(DeviceError::VhostUserSlaveRequest)?;
.map_err(Error::VhostUserSlaveRequest)?;
}
}
_ => {
@ -307,26 +240,36 @@ impl Fs {
cache: Option<(VirtioSharedMemoryList, u64)>,
) -> Result<Fs> {
let mut slave_req_support = false;
// Calculate the actual number of queues needed.
let num_queues = NUM_QUEUE_OFFSET + req_num_queues;
// Connect to the vhost-user socket.
let mut master =
Master::connect(path, num_queues as u64).map_err(Error::VhostUserConnect)?;
// Retrieve available features only when connecting the first time.
let mut avail_features = master.get_features().map_err(Error::VhostUserGetFeatures)?;
// Let only ack features we expect, that is VIRTIO_F_VERSION_1.
if (avail_features & VIRTIO_F_VERSION_1_BITMASK) != VIRTIO_F_VERSION_1_BITMASK {
return Err(Error::InvalidFeatures);
}
avail_features =
VIRTIO_F_VERSION_1_BITMASK | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
Master::connect(path, num_queues as u64).map_err(Error::VhostUserCreateMaster)?;
// Filling device and vring features VMM supports.
let mut avail_features =
1 << VIRTIO_F_VERSION_1 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
// Set vhost-user owner.
master.set_owner().map_err(Error::VhostUserSetOwner)?;
// Get features from backend, do negotiation to get a feature collection which
// both VMM and backend support.
let backend_features = master.get_features().map_err(Error::VhostUserGetFeatures)?;
avail_features &= backend_features;
// Set features back is required by the vhost crate mechanism, since the
// later vhost call will check if features is filled in master before execution.
master
.set_features(avail_features)
.map_err(Error::VhostUserSetFeatures)?;
// Identify if protocol features are supported by the slave.
if (avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits())
== VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
{
let mut acked_features = 0;
if avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() != 0 {
acked_features |= VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
let mut protocol_features = master
.get_protocol_features()
.map_err(Error::VhostUserGetProtocolFeatures)?;
@ -347,10 +290,12 @@ impl Fs {
slave_req_support = true;
}
// Create virtio device config space.
// First by adding the tag.
let mut config_space = tag.to_string().into_bytes();
config_space.resize(CONFIG_SPACE_SIZE, 0);
// And then by copying the number of queues.
let num_queues_slice = (req_num_queues as u32).to_le_bytes();
config_space[CONFIG_SPACE_TAG_SIZE..CONFIG_SPACE_SIZE].copy_from_slice(&num_queues_slice);
@ -359,95 +304,13 @@ impl Fs {
vu: master,
queue_sizes: vec![queue_size; num_queues],
avail_features,
acked_features: avail_features,
acked_features,
config_space,
kill_evt: None,
cache,
slave_req_support,
})
}
fn setup_vu(
&mut self,
mem: &GuestMemoryMmap,
queues: Vec<Queue>,
queue_evts: Vec<EventFd>,
) -> Result<Vec<(EventFd, Queue)>> {
// Set vhost-user owner.
self.vu.set_owner().map_err(Error::VhostUserSetOwner)?;
let mut regions: Vec<VhostUserMemoryRegionInfo> = Vec::new();
mem.with_regions_mut(|_, region| {
let (mmap_handle, mmap_offset) = match region.file_offset() {
Some(fo) => (fo.file().as_raw_fd(), fo.start()),
None => return Err(MmapError::NoMemoryRegion),
};
let vu_mem_reg = VhostUserMemoryRegionInfo {
guest_phys_addr: region.start_addr().raw_value(),
memory_size: region.len() as u64,
userspace_addr: region.as_ptr() as u64,
mmap_offset,
mmap_handle,
};
regions.push(vu_mem_reg);
Ok(())
})
.map_err(Error::MemoryRegions)?;
self.vu
.set_mem_table(regions.as_slice())
.map_err(Error::VhostUserSetMemTable)?;
let mut result = Vec::new();
for (queue_index, queue) in queues.into_iter().enumerate() {
self.vu
.set_vring_num(queue_index, queue.get_max_size())
.map_err(Error::VhostUserSetVringNum)?;
let vring_config = VringConfigData {
queue_max_size: queue.get_max_size(),
queue_size: queue.size,
flags: 0u32,
desc_table_addr: mem
.get_host_address(queue.desc_table)
.ok_or_else(|| Error::DescriptorTableAddress)?
as u64,
used_ring_addr: mem
.get_host_address(queue.used_ring)
.ok_or_else(|| Error::UsedAddress)? as u64,
avail_ring_addr: mem
.get_host_address(queue.avail_ring)
.ok_or_else(|| Error::AvailAddress)? as u64,
log_addr: None,
};
self.vu
.set_vring_addr(queue_index, &vring_config)
.map_err(Error::VhostUserSetVringAddr)?;
self.vu
.set_vring_base(queue_index, 0u16)
.map_err(Error::VhostUserSetVringBase)?;
let vu_call_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::IrqEventCreate)?;
self.vu
.set_vring_call(queue_index, &vu_call_evt)
.map_err(Error::VhostUserSetVringCall)?;
result.push((vu_call_evt, queue));
self.vu
.set_vring_kick(queue_index, &queue_evts[queue_index])
.map_err(Error::VhostUserSetVringKick)?;
}
Ok(result)
}
}
impl Drop for Fs {
@ -552,9 +415,14 @@ impl VirtioDevice for Fs {
};
self.kill_evt = Some(self_kill_evt);
let vu_call_evt_queue_list = self
.setup_vu(&mem.read().unwrap(), queues, queue_evts)
.map_err(ActivateError::VhostUserSetup)?;
let vu_call_evt_queue_list = setup_vhost_user(
&mut self.vu,
&mem.read().unwrap(),
queues,
queue_evts,
self.acked_features,
)
.map_err(ActivateError::VhostUserSetup)?;
// Initialize slave communication.
let slave_req_handler = if self.slave_req_support {

View File

@ -49,6 +49,8 @@ pub enum Error {
VhostUserCreateMaster(VhostError),
/// Failed to open vhost device.
VhostUserOpen(VhostError),
/// Connection to socket failed.
VhostUserConnect(vhost_rs::Error),
/// Get features failed.
VhostUserGetFeatures(VhostError),
/// Get protocol features failed.
@ -85,6 +87,10 @@ pub enum Error {
VhostUserMemoryRegion(MmapError),
/// Failed to handle vhost-user slave request.
VhostUserSlaveRequest(vhost_rs::vhost_user::Error),
/// Failed to create the master request handler from slave.
MasterReqHandlerCreation(vhost_rs::vhost_user::Error),
/// Set slave request fd failed.
VhostUserSetSlaveRequestFd(vhost_rs::Error),
/// Invalid used address.
UsedAddress,
}

View File

@ -250,7 +250,7 @@ pub enum DeviceManagerError {
CreateVirtioRng(io::Error),
/// Cannot create virtio-fs device
CreateVirtioFs(vm_virtio::vhost_user::fs::Error),
CreateVirtioFs(vm_virtio::vhost_user::Error),
/// Cannot create virtio-pmem device
CreateVirtioPmem(io::Error),