From 9e53efa3ca6523daf39288214da65d55ddd7771a Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Fri, 5 Mar 2021 14:32:57 +0100 Subject: [PATCH] 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 --- virtio-devices/src/device.rs | 9 +++- virtio-devices/src/lib.rs | 1 + virtio-devices/src/vhost_user/blk.rs | 35 ++++++++++++-- virtio-devices/src/vhost_user/fs.rs | 46 +++++++++++++++---- virtio-devices/src/vhost_user/mod.rs | 4 ++ virtio-devices/src/vhost_user/net.rs | 42 +++++++++++++++-- .../src/vhost_user/vu_common_ctrl.rs | 22 ++++++++- 7 files changed, 140 insertions(+), 19 deletions(-) diff --git a/virtio-devices/src/device.rs b/virtio-devices/src/device.rs index bbe457861..604368522 100644 --- a/virtio-devices/src/device.rs +++ b/virtio-devices/src/device.rs @@ -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, + ) -> std::result::Result<(), Error> { + Ok(()) + } + /// Returns the list of userspace mappings associated with this device. fn userspace_mappings(&self) -> Vec { Vec::new() diff --git a/virtio-devices/src/lib.rs b/virtio-devices/src/lib.rs index 6231ca8af..9b37c9190 100644 --- a/virtio-devices/src/lib.rs +++ b/virtio-devices/src/lib.rs @@ -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), diff --git a/virtio-devices/src/vhost_user/blk.rs b/virtio-devices/src/vhost_user/blk.rs index b86c29473..c0772a507 100644 --- a/virtio-devices/src/vhost_user/blk.rs +++ b/virtio-devices/src/vhost_user/blk.rs @@ -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>, + 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, + ) -> 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 { diff --git a/virtio-devices/src/vhost_user/fs.rs b/virtio-devices/src/vhost_user/fs.rs index d56d20a97..831a9ecde 100644 --- a/virtio-devices/src/vhost_user/fs.rs +++ b/virtio-devices/src/vhost_user/fs.rs @@ -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>, + 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, + ) -> 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 { let mut mappings = Vec::new(); if let Some(cache) = self.cache.as_ref() { diff --git a/virtio-devices/src/vhost_user/mod.rs b/virtio-devices/src/vhost_user/mod.rs index a75415bd1..2851c3579 100644 --- a/virtio-devices/src/vhost_user/mod.rs +++ b/virtio-devices/src/vhost_user/mod.rs @@ -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 = std::result::Result; diff --git a/virtio-devices/src/vhost_user/net.rs b/virtio-devices/src/vhost_user/net.rs index f55667593..debe6ef08 100644 --- a/virtio-devices/src/vhost_user/net.rs +++ b/virtio-devices/src/vhost_user/net.rs @@ -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>, seccomp_action: SeccompAction, + guest_memory: Option>, + 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, + ) -> 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 { diff --git a/virtio-devices/src/vhost_user/vu_common_ctrl.rs b/virtio-devices/src/vhost_user/vu_common_ctrl.rs index 4d42ecceb..925c4861c 100644 --- a/virtio-devices/src/vhost_user/vu_common_ctrl.rs +++ b/virtio-devices/src/vhost_user/vu_common_ctrl.rs @@ -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) -> 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(®ion) + .map_err(Error::VhostUserAddMemReg) +} + pub fn setup_vhost_user_vring( vu: &mut Master, mem: &GuestMemoryMmap,