virtio-devices: Add support for adding a single memory region

Assuming vhost-user devices support CONFIGURE_MEM_SLOTS protocol
feature, we introduce a new method to the VirtioDevice trait in order to
update one single memory at a time.

In case CONFIGURE_MEM_SLOTS is not supported by the backend (feature not
acked), we fallback onto the current way of updating the memory
mappings, that is with SET_MEM_TABLE.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2021-03-05 14:32:57 +01:00
parent 4f22f57651
commit 9e53efa3ca
7 changed files with 140 additions and 19 deletions

View File

@ -16,7 +16,7 @@ use std::sync::{
Arc, Barrier,
};
use std::thread;
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, GuestUsize};
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap, GuestRegionMmap, GuestUsize};
use vm_migration::{MigratableError, Pausable};
use vm_virtio::VirtioDeviceType;
use vmm_sys_util::eventfd::EventFd;
@ -143,6 +143,13 @@ pub trait VirtioDevice: Send {
Ok(())
}
fn add_memory_region(
&mut self,
_region: &Arc<GuestRegionMmap>,
) -> std::result::Result<(), Error> {
Ok(())
}
/// Returns the list of userspace mappings associated with this device.
fn userspace_mappings(&self) -> Vec<UserspaceMapping> {
Vec::new()

View File

@ -119,6 +119,7 @@ pub enum Error {
EpollWait(io::Error),
FailedSignalingDriver(io::Error),
VhostUserUpdateMemory(vhost_user::Error),
VhostUserAddMemoryRegion(vhost_user::Error),
EventfdError(io::Error),
SetShmRegionsNotSupported,
EpollHander(String),

View File

@ -12,6 +12,7 @@ use crate::VirtioInterrupt;
use block_util::VirtioBlockConfig;
use seccomp::{SeccompAction, SeccompFilter};
use std::mem;
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::{Arc, Barrier};
@ -24,7 +25,9 @@ use vhost::vhost_user::{Master, VhostUserMaster, VhostUserMasterReqHandler};
use vhost::VhostBackend;
use virtio_bindings::bindings::virtio_blk::*;
use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
use vm_memory::{
ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap, GuestRegionMmap,
};
use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable};
use vmm_sys_util::eventfd::EventFd;
@ -37,6 +40,8 @@ pub struct Blk {
vhost_user_blk: Master,
config: VirtioBlockConfig,
seccomp_action: SeccompAction,
guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
acked_protocol_features: u64,
}
impl Blk {
@ -79,17 +84,21 @@ impl Blk {
// Identify if protocol features are supported by the slave.
let mut acked_features = 0;
let mut acked_protocol_features = 0;
if avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() != 0 {
acked_features |= VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
let mut protocol_features = vhost_user_blk
.get_protocol_features()
.map_err(Error::VhostUserGetProtocolFeatures)?;
protocol_features |= VhostUserProtocolFeatures::MQ;
protocol_features &= !VhostUserProtocolFeatures::INFLIGHT_SHMFD;
protocol_features &= VhostUserProtocolFeatures::CONFIG
| VhostUserProtocolFeatures::MQ
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS;
vhost_user_blk
.set_protocol_features(protocol_features)
.map_err(Error::VhostUserSetProtocolFeatures)?;
acked_protocol_features = protocol_features.bits();
}
// Get the max queues number from backend, and the queue number set
// should be less than this max queue number.
@ -142,6 +151,8 @@ impl Blk {
vhost_user_blk,
config,
seccomp_action,
guest_memory: None,
acked_protocol_features,
})
}
}
@ -206,6 +217,8 @@ impl VirtioDevice for Blk {
) -> ActivateResult {
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
self.guest_memory = Some(mem.clone());
let mut vu_interrupt_list = setup_vhost_user(
&mut self.vhost_user_blk,
&mem.memory(),
@ -305,6 +318,22 @@ impl VirtioDevice for Blk {
fn update_memory(&mut self, mem: &GuestMemoryMmap) -> std::result::Result<(), crate::Error> {
update_mem_table(&mut self.vhost_user_blk, mem).map_err(crate::Error::VhostUserUpdateMemory)
}
fn add_memory_region(
&mut self,
region: &Arc<GuestRegionMmap>,
) -> std::result::Result<(), crate::Error> {
if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() != 0
{
add_memory_region(&mut self.vhost_user_blk, region)
.map_err(crate::Error::VhostUserAddMemoryRegion)
} else if let Some(guest_memory) = &self.guest_memory {
update_mem_table(&mut self.vhost_user_blk, guest_memory.memory().deref())
.map_err(crate::Error::VhostUserUpdateMemory)
} else {
Ok(())
}
}
}
impl Pausable for Blk {

View File

@ -1,7 +1,9 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use super::vu_common_ctrl::{reset_vhost_user, setup_vhost_user, update_mem_table};
use super::vu_common_ctrl::{
add_memory_region, reset_vhost_user, setup_vhost_user, update_mem_table,
};
use super::{Error, Result};
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::vhost_user::handler::{VhostUserEpollConfig, VhostUserEpollHandler};
@ -12,6 +14,7 @@ use crate::{
use libc::{self, c_void, off64_t, pread64, pwrite64};
use seccomp::{SeccompAction, SeccompFilter};
use std::io;
use std::ops::Deref;
use std::os::unix::io::{AsRawFd, RawFd};
use std::result;
use std::sync::{Arc, Barrier};
@ -26,7 +29,7 @@ use vhost::vhost_user::{
use vhost::VhostBackend;
use vm_memory::{
Address, ByteValued, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryAtomic,
GuestMemoryMmap, MmapRegion,
GuestMemoryMmap, GuestRegionMmap, MmapRegion,
};
use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable};
use vmm_sys_util::eventfd::EventFd;
@ -274,6 +277,8 @@ pub struct Fs {
cache: Option<(VirtioSharedMemoryList, MmapRegion)>,
slave_req_support: bool,
seccomp_action: SeccompAction,
guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
acked_protocol_features: u64,
}
impl Fs {
@ -315,6 +320,7 @@ impl Fs {
// Identify if protocol features are supported by the slave.
let mut acked_features = 0;
let mut acked_protocol_features = 0;
if avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() != 0 {
acked_features |= VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
@ -322,20 +328,23 @@ impl Fs {
.get_protocol_features()
.map_err(Error::VhostUserGetProtocolFeatures)?;
let mut supported_protocol_features = VhostUserProtocolFeatures::MQ
| VhostUserProtocolFeatures::REPLY_ACK
| VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS;
if cache.is_some() {
protocol_features &= VhostUserProtocolFeatures::MQ
| VhostUserProtocolFeatures::REPLY_ACK
| VhostUserProtocolFeatures::SLAVE_REQ
| VhostUserProtocolFeatures::SLAVE_SEND_FD;
} else {
protocol_features &=
VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::REPLY_ACK;
supported_protocol_features |=
VhostUserProtocolFeatures::SLAVE_REQ | VhostUserProtocolFeatures::SLAVE_SEND_FD
}
protocol_features &= supported_protocol_features;
master
.set_protocol_features(protocol_features)
.map_err(Error::VhostUserSetProtocolFeatures)?;
acked_protocol_features = protocol_features.bits();
slave_req_support = true;
}
@ -361,6 +370,8 @@ impl Fs {
cache,
slave_req_support,
seccomp_action,
guest_memory: None,
acked_protocol_features,
})
}
}
@ -404,6 +415,8 @@ impl VirtioDevice for Fs {
) -> ActivateResult {
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
self.guest_memory = Some(mem.clone());
let kill_evt = self
.common
.kill_evt
@ -548,6 +561,21 @@ impl VirtioDevice for Fs {
update_mem_table(&mut self.vu, mem).map_err(crate::Error::VhostUserUpdateMemory)
}
fn add_memory_region(
&mut self,
region: &Arc<GuestRegionMmap>,
) -> std::result::Result<(), crate::Error> {
if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() != 0
{
add_memory_region(&mut self.vu, region).map_err(crate::Error::VhostUserAddMemoryRegion)
} else if let Some(guest_memory) = &self.guest_memory {
update_mem_table(&mut self.vu, guest_memory.memory().deref())
.map_err(crate::Error::VhostUserUpdateMemory)
} else {
Ok(())
}
}
fn userspace_mappings(&self) -> Vec<UserspaceMapping> {
let mut mappings = Vec::new();
if let Some(cache) = self.cache.as_ref() {

View File

@ -84,9 +84,13 @@ pub enum Error {
MasterReqHandlerCreation(vhost::vhost_user::Error),
/// Set slave request fd failed.
VhostUserSetSlaveRequestFd(vhost::Error),
/// Add memory region failed.
VhostUserAddMemReg(VhostError),
/// Invalid used address.
UsedAddress,
/// Invalid features provided from vhost-user backend
InvalidFeatures,
/// Missing file descriptor for the region.
MissingRegionFd,
}
type Result<T> = std::result::Result<T, Error>;

View File

@ -14,6 +14,7 @@ use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::VirtioInterrupt;
use net_util::MacAddr;
use seccomp::{SeccompAction, SeccompFilter};
use std::ops::Deref;
use std::os::unix::io::AsRawFd;
use std::result;
use std::sync::{Arc, Barrier};
@ -24,7 +25,9 @@ use vhost::vhost_user::{Master, VhostUserMaster, VhostUserMasterReqHandler};
use vhost::VhostBackend;
use virtio_bindings::bindings::virtio_net;
use virtio_bindings::bindings::virtio_ring;
use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
use vm_memory::{
ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap, GuestRegionMmap,
};
use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable};
use vmm_sys_util::eventfd::EventFd;
@ -41,6 +44,8 @@ pub struct Net {
config: VirtioNetConfig,
ctrl_queue_epoll_thread: Option<thread::JoinHandle<()>>,
seccomp_action: SeccompAction,
guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>,
acked_protocol_features: u64,
}
impl Net {
@ -88,7 +93,7 @@ impl Net {
.set_features(avail_features)
.map_err(Error::VhostUserSetFeatures)?;
let protocol_features;
let mut protocol_features;
let mut acked_features = 0;
if avail_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() != 0 {
acked_features |= VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
@ -99,11 +104,17 @@ impl Net {
return Err(Error::VhostUserProtocolNotSupport);
}
protocol_features &=
VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS;
vhost_user_net
.set_protocol_features(protocol_features)
.map_err(Error::VhostUserSetProtocolFeatures)?;
let acked_protocol_features = protocol_features.bits();
let max_queue_number =
if protocol_features.bits() & VhostUserProtocolFeatures::MQ.bits() != 0 {
vhost_user_net
.set_protocol_features(protocol_features & VhostUserProtocolFeatures::MQ)
.map_err(Error::VhostUserSetProtocolFeatures)?;
match vhost_user_net.get_queue_num() {
Ok(qn) => qn,
Err(_) => DEFAULT_QUEUE_NUMBER as u64,
@ -111,6 +122,7 @@ impl Net {
} else {
DEFAULT_QUEUE_NUMBER as u64
};
if vu_cfg.num_queues > max_queue_number as usize {
error!("vhost-user-net has queue number: {} larger than the max queue number: {} backend allowed\n",
vu_cfg.num_queues, max_queue_number);
@ -152,6 +164,8 @@ impl Net {
config,
ctrl_queue_epoll_thread: None,
seccomp_action,
guest_memory: None,
acked_protocol_features,
})
}
}
@ -196,6 +210,8 @@ impl VirtioDevice for Net {
) -> ActivateResult {
self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
self.guest_memory = Some(mem.clone());
let queue_num = self.common.queue_evts.as_ref().unwrap().len();
if self
@ -360,6 +376,22 @@ impl VirtioDevice for Net {
fn update_memory(&mut self, mem: &GuestMemoryMmap) -> std::result::Result<(), crate::Error> {
update_mem_table(&mut self.vhost_user_net, mem).map_err(crate::Error::VhostUserUpdateMemory)
}
fn add_memory_region(
&mut self,
region: &Arc<GuestRegionMmap>,
) -> std::result::Result<(), crate::Error> {
if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() != 0
{
add_memory_region(&mut self.vhost_user_net, region)
.map_err(crate::Error::VhostUserAddMemoryRegion)
} else if let Some(guest_memory) = &self.guest_memory {
update_mem_table(&mut self.vhost_user_net, guest_memory.memory().deref())
.map_err(crate::Error::VhostUserUpdateMemory)
} else {
Ok(())
}
}
}
impl Pausable for Net {

View File

@ -11,7 +11,9 @@ use std::sync::Arc;
use std::vec::Vec;
use vhost::vhost_user::{Master, VhostUserMaster};
use vhost::{VhostBackend, VhostUserMemoryRegionInfo, VringConfigData};
use vm_memory::{Address, Error as MmapError, GuestMemory, GuestMemoryMmap, GuestMemoryRegion};
use vm_memory::{
Address, Error as MmapError, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, GuestRegionMmap,
};
use vmm_sys_util::eventfd::EventFd;
#[derive(Debug, Clone)]
@ -49,6 +51,24 @@ pub fn update_mem_table(vu: &mut Master, mem: &GuestMemoryMmap) -> Result<()> {
Ok(())
}
pub fn add_memory_region(vu: &mut Master, region: &Arc<GuestRegionMmap>) -> Result<()> {
let (mmap_handle, mmap_offset) = match region.file_offset() {
Some(file_offset) => (file_offset.file().as_raw_fd(), file_offset.start()),
None => return Err(Error::MissingRegionFd),
};
let region = 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,
};
vu.add_mem_region(&region)
.map_err(Error::VhostUserAddMemReg)
}
pub fn setup_vhost_user_vring(
vu: &mut Master,
mem: &GuestMemoryMmap,