diff --git a/vhost-user-backend/README.md b/vhost-user-backend/README.md index 46b771ce..7bb319d9 100644 --- a/vhost-user-backend/README.md +++ b/vhost-user-backend/README.md @@ -19,7 +19,7 @@ where V: VringT> + Clone + Send + Sync + 'static, B: Bitmap + 'static, { - pub fn new(name: String, backend: S, atomic_mem: GuestMemoryAtomic>) -> Result; + pub fn new(name: String, backend: S, atomic_mem: GM) -> Result; pub fn start(&mut self, listener: Listener) -> Result<()>; pub fn wait(&mut self) -> Result<()>; pub fn get_epoll_handlers(&self) -> Vec>>; diff --git a/vhost-user-backend/src/backend.rs b/vhost-user-backend/src/backend.rs index 20e7daf3..a8daee49 100644 --- a/vhost-user-backend/src/backend.rs +++ b/vhost-user-backend/src/backend.rs @@ -685,7 +685,7 @@ pub mod tests { Ok(()) } - fn update_memory(&mut self, _atomic_mem: GuestMemoryAtomic) -> Result<()> { + fn update_memory(&mut self, _atomic_mem: GM) -> Result<()> { Ok(()) } diff --git a/vhost-user-backend/src/handler.rs b/vhost-user-backend/src/handler.rs index 33008756..f55f5406 100644 --- a/vhost-user-backend/src/handler.rs +++ b/vhost-user-backend/src/handler.rs @@ -30,7 +30,7 @@ use vhost::vhost_user::{ use virtio_bindings::bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX; use virtio_queue::{Error as VirtQueError, QueueT}; use vm_memory::mmap::NewBitmap; -use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryMmap, GuestRegionMmap}; +use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemory, GuestRegionCollection}; use vmm_sys_util::epoll::EventSet; use super::backend::VhostUserBackend; @@ -314,11 +314,7 @@ where let mut mappings: Vec = Vec::new(); for (region, file) in ctx.iter().zip(files) { - let guest_region = GuestRegionMmap::new( - region.mmap_region(file)?, - GuestAddress(region.guest_phys_addr), - ) - .map_err(|e| VhostUserError::ReqHandlerError(io::Error::other(e)))?; + let guest_region = region.memory_region(file)?; mappings.push(AddrMapping { #[cfg(feature = "postcopy")] local_addr: guest_region.as_ptr() as u64, @@ -329,7 +325,7 @@ where regions.push(guest_region); } - let mem = GuestMemoryMmap::from_regions(regions) + let mem = GuestRegionCollection::from_regions(regions) .map_err(|e| VhostUserError::ReqHandlerError(io::Error::other(e)))?; // Updating the inner GuestMemory object here will cause all our vrings to @@ -601,13 +597,7 @@ where region: &VhostUserSingleMemoryRegion, file: File, ) -> VhostUserResult<()> { - let guest_region = Arc::new( - GuestRegionMmap::new( - region.mmap_region(file)?, - GuestAddress(region.guest_phys_addr), - ) - .map_err(|e| VhostUserError::ReqHandlerError(io::Error::other(e)))?, - ); + let guest_region = Arc::new(region.memory_region(file)?); let addr_mapping = AddrMapping { #[cfg(feature = "postcopy")] diff --git a/vhost-user-backend/src/lib.rs b/vhost-user-backend/src/lib.rs index 7e1db9e4..0775a2f1 100644 --- a/vhost-user-backend/src/lib.rs +++ b/vhost-user-backend/src/lib.rs @@ -13,11 +13,11 @@ use std::path::Path; use std::sync::{Arc, Mutex}; use std::thread; +use self::handler::VhostUserHandler; use vhost::vhost_user::{BackendListener, BackendReqHandler, Error as VhostUserError, Listener}; +use vhost::MemoryRegion; use vm_memory::mmap::NewBitmap; -use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; - -use self::handler::VhostUserHandler; +use vm_memory::{GuestMemoryAtomic, GuestRegionCollection}; mod backend; pub use self::backend::{VhostUserBackend, VhostUserBackendMut}; @@ -43,7 +43,7 @@ pub use self::vring::{ compile_error!("Both `postcopy` and `xen` features can not be enabled at the same time."); /// An alias for `GuestMemoryAtomic>` to simplify code. -type GM = GuestMemoryAtomic>; +type GM = GuestMemoryAtomic>>; #[derive(Debug)] /// Errors related to vhost-user daemon. @@ -106,11 +106,7 @@ where /// Under the hood, this will start a dedicated thread responsible for listening onto /// registered event. Those events can be vring events or custom events from the backend, /// but they get to be registered later during the sequence. - pub fn new( - name: String, - backend: T, - atomic_mem: GuestMemoryAtomic>, - ) -> Result { + pub fn new(name: String, backend: T, atomic_mem: GM) -> Result { let handler = Arc::new(Mutex::new( VhostUserHandler::new(backend, atomic_mem).map_err(Error::NewVhostUserHandler)?, )); diff --git a/vhost-user-backend/src/vring.rs b/vhost-user-backend/src/vring.rs index 3b4284ce..fe3def25 100644 --- a/vhost-user-backend/src/vring.rs +++ b/vhost-user-backend/src/vring.rs @@ -13,8 +13,9 @@ use std::result::Result; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; +use crate::GM; use virtio_queue::{Error as VirtQueError, Queue, QueueT}; -use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap}; +use vm_memory::{GuestAddress, GuestAddressSpace}; use vmm_sys_util::event::{EventConsumer, EventNotifier}; /// Trait for objects returned by `VringT::get_ref()`. @@ -107,7 +108,7 @@ pub trait VringT: /// /// This struct maintains all information of a virito queue, and could be used as an `VringT` /// object for single-threaded context. -pub struct VringState> { +pub struct VringState { queue: Queue, kick: Option, call: Option, @@ -269,7 +270,7 @@ impl VringState { /// A `VringState` object protected by Mutex for multi-threading context. #[derive(Clone)] -pub struct VringMutex> { +pub struct VringMutex { state: Arc>>, } @@ -384,7 +385,7 @@ impl VringT for VringMutex { /// A `VringState` object protected by RwLock for multi-threading context. #[derive(Clone)] -pub struct VringRwLock> { +pub struct VringRwLock { state: Arc>>, } diff --git a/vhost-user-backend/tests/vhost-user-server.rs b/vhost-user-backend/tests/vhost-user-server.rs index 3c8205a5..3b859c39 100644 --- a/vhost-user-backend/tests/vhost-user-server.rs +++ b/vhost-user-backend/tests/vhost-user-server.rs @@ -90,7 +90,7 @@ impl VhostUserBackendMut for MockVhostBackend { Ok(()) } - fn update_memory(&mut self, atomic_mem: GuestMemoryAtomic) -> Result<()> { + fn update_memory(&mut self, atomic_mem: GM) -> Result<()> { let mem = atomic_mem.memory(); let region = mem.find_region(GuestAddress(0x100000)).unwrap(); assert_eq!(region.size(), 0x100000); diff --git a/vhost/src/backend.rs b/vhost/src/backend.rs index bd4a83b2..48dc300d 100644 --- a/vhost/src/backend.rs +++ b/vhost/src/backend.rs @@ -14,6 +14,8 @@ use std::os::unix::io::AsRawFd; use std::os::unix::io::RawFd; use std::sync::RwLock; +#[cfg(feature = "xen")] +use vm_memory::GuestRegionXen; use vm_memory::{bitmap::Bitmap, Address, GuestMemoryRegion, GuestRegionMmap}; use vmm_sys_util::eventfd::EventFd; @@ -86,61 +88,67 @@ pub struct VhostUserMemoryRegionInfo { pub xen_mmap_data: u32, } -impl VhostUserMemoryRegionInfo { - /// Creates Self from GuestRegionMmap. - pub fn from_guest_region(region: &GuestRegionMmap) -> Result { +#[cfg(feature = "vhost-user")] +impl From<&VhostUserMemoryRegionInfo> for VhostUserMemoryRegion { + fn from(region: &VhostUserMemoryRegionInfo) -> Self { + VhostUserMemoryRegion { + guest_phys_addr: region.guest_phys_addr, + memory_size: region.memory_size, + user_addr: region.userspace_addr, + mmap_offset: region.mmap_offset, + #[cfg(feature = "xen")] + xen_mmap_flags: region.xen_mmap_flags, + #[cfg(feature = "xen")] + xen_mmap_data: region.xen_mmap_data, + } + } +} + +impl TryFrom<&GuestRegionMmap> for VhostUserMemoryRegionInfo { + type Error = Error; + + fn try_from(region: &GuestRegionMmap) -> Result { let file_offset = region .file_offset() .ok_or(Error::InvalidGuestMemoryRegion)?; - Ok(Self { + Ok(VhostUserMemoryRegionInfo { guest_phys_addr: region.start_addr().raw_value(), memory_size: region.len(), userspace_addr: region.as_ptr() as u64, mmap_offset: file_offset.start(), mmap_handle: file_offset.file().as_raw_fd(), - #[cfg(feature = "xen")] - xen_mmap_flags: region.xen_mmap_flags(), - #[cfg(feature = "xen")] - xen_mmap_data: region.xen_mmap_data(), + ..Default::default() }) } +} - /// Creates VhostUserMemoryRegion from Self. - #[cfg(feature = "vhost-user")] - pub fn to_region(&self) -> VhostUserMemoryRegion { - #[cfg(not(feature = "xen"))] - return VhostUserMemoryRegion::new( - self.guest_phys_addr, - self.memory_size, - self.userspace_addr, - self.mmap_offset, - ); - - #[cfg(feature = "xen")] - VhostUserMemoryRegion::with_xen( - self.guest_phys_addr, - self.memory_size, - self.userspace_addr, - self.mmap_offset, - self.xen_mmap_flags, - self.xen_mmap_data, - ) +#[cfg(feature = "xen")] +impl TryFrom<&GuestRegionXen> for VhostUserMemoryRegionInfo { + type Error = Error; + + fn try_from(region: &GuestRegionXen) -> std::result::Result { + let file_offset = region + .file_offset() + .ok_or(Error::InvalidGuestMemoryRegion)?; + + Ok(VhostUserMemoryRegionInfo { + guest_phys_addr: region.start_addr().raw_value(), + memory_size: region.len(), + userspace_addr: region.as_ptr() as u64, + mmap_offset: file_offset.start(), + mmap_handle: file_offset.file().as_raw_fd(), + xen_mmap_flags: region.xen_mmap_flags(), + xen_mmap_data: region.xen_mmap_data(), + }) } +} +impl VhostUserMemoryRegionInfo { /// Creates VhostUserSingleMemoryRegion from Self. #[cfg(feature = "vhost-user")] pub fn to_single_region(&self) -> VhostUserSingleMemoryRegion { - VhostUserSingleMemoryRegion::new( - self.guest_phys_addr, - self.memory_size, - self.userspace_addr, - self.mmap_offset, - #[cfg(feature = "xen")] - self.xen_mmap_flags, - #[cfg(feature = "xen")] - self.xen_mmap_data, - ) + self.into() } } diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs index 6a2c0e1a..204825ac 100644 --- a/vhost/src/lib.rs +++ b/vhost/src/lib.rs @@ -30,15 +30,23 @@ //! that shares its virtqueues. Backend is the consumer of the virtqueues. Frontend and backend can be //! either a client (i.e. connecting) or server (listening) in the socket communication. -#![deny(missing_docs)] - #[cfg_attr(feature = "vhost-user", macro_use)] extern crate bitflags; #[cfg_attr(feature = "vhost-kern", macro_use)] extern crate vmm_sys_util; mod backend; + +use std::ops::Deref; + pub use backend::*; +use vm_memory::bitmap::{Bitmap, BS}; +#[cfg(feature = "xen")] +use vm_memory::GuestRegionXen; +use vm_memory::{ + GuestAddress, GuestMemoryRegion, GuestMemoryRegionBytes, GuestRegionMmap, GuestUsize, + MemoryRegionAddress, VolatileSlice, +}; #[cfg(feature = "vhost-net")] pub mod net; @@ -128,6 +136,75 @@ impl std::convert::From for Error { /// Result of vhost operations pub type Result = std::result::Result; +pub enum MemoryRegion { + Unix(GuestRegionMmap), + #[cfg(feature = "xen")] + Xen(GuestRegionXen), +} + +impl MemoryRegion { + pub fn bitmap(&self) -> &B { + match self { + MemoryRegion::Unix(r) => (*r).deref().bitmap(), + #[cfg(feature = "xen")] + MemoryRegion::Xen(r) => r.bitmap(), + } + } +} + +impl GuestMemoryRegion for MemoryRegion { + type B = B; + + fn len(&self) -> GuestUsize { + match self { + MemoryRegion::Unix(r) => r.len(), + #[cfg(feature = "xen")] + MemoryRegion::Xen(r) => r.len(), + } + } + + fn start_addr(&self) -> GuestAddress { + match self { + MemoryRegion::Unix(r) => r.start_addr(), + #[cfg(feature = "xen")] + MemoryRegion::Xen(r) => r.start_addr(), + } + } + + fn bitmap(&self) -> BS<'_, Self::B> { + match self { + MemoryRegion::Unix(r) => r.bitmap(), + #[cfg(feature = "xen")] + MemoryRegion::Xen(r) => as GuestMemoryRegion>::bitmap(r), + } + } + + fn get_host_address( + &self, + addr: MemoryRegionAddress, + ) -> vm_memory::guest_memory::Result<*mut u8> { + match self { + MemoryRegion::Unix(r) => r.get_host_address(addr), + #[cfg(feature = "xen")] + MemoryRegion::Xen(r) => r.get_host_address(addr), + } + } + + fn get_slice( + &self, + offset: MemoryRegionAddress, + count: usize, + ) -> vm_memory::guest_memory::Result>> { + match self { + MemoryRegion::Unix(r) => r.get_slice(offset, count), + #[cfg(feature = "xen")] + MemoryRegion::Xen(r) => r.get_slice(offset, count), + } + } +} + +impl GuestMemoryRegionBytes for MemoryRegion {} + #[cfg(test)] mod tests { use super::*; diff --git a/vhost/src/vhost_user/frontend.rs b/vhost/src/vhost_user/frontend.rs index bda291f2..80554c86 100644 --- a/vhost/src/vhost_user/frontend.rs +++ b/vhost/src/vhost_user/frontend.rs @@ -223,7 +223,7 @@ impl VhostBackend for Frontend { return error_code(VhostUserError::InvalidParam); } - ctx.append(®ion.to_region(), region.mmap_handle); + ctx.append(®ion.into(), region.mmap_handle); } let mut node = self.node(); diff --git a/vhost/src/vhost_user/message.rs b/vhost/src/vhost_user/message.rs index 7fd93ce7..abec6ae4 100644 --- a/vhost/src/vhost_user/message.rs +++ b/vhost/src/vhost_user/message.rs @@ -17,13 +17,15 @@ use std::ops::Deref; use uuid::Uuid; -use vm_memory::{mmap::NewBitmap, ByteValued, Error as MmapError, FileOffset, MmapRegion}; +use vm_memory::{ + mmap::NewBitmap, ByteValued, FileOffset, GuestAddress, GuestRegionMmap, MmapRegion, +}; #[cfg(feature = "xen")] -use vm_memory::{GuestAddress, MmapRange, MmapXenFlags}; +use vm_memory::{GuestRegionXen, MmapRange, MmapXenFlags}; use super::{enum_value, Error, Result}; -use crate::VringConfigData; +use crate::{MemoryRegion, VhostUserMemoryRegionInfo, VringConfigData}; /* TODO: Consider deprecating this. We don't actually have any preallocated buffers except in tests, @@ -530,81 +532,61 @@ impl VhostUserMemoryRegion { && self.user_addr.checked_add(self.memory_size).is_some() && self.mmap_offset.checked_add(self.memory_size).is_some() } -} -#[cfg(not(feature = "xen"))] -impl VhostUserMemoryRegion { - /// Create a new instance. - pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self { - VhostUserMemoryRegion { - guest_phys_addr, - memory_size, - user_addr, - mmap_offset, + fn is_xen(&self) -> bool { + #[cfg(feature = "xen")] + { + !(self.xen_mmap_flags == 0 && self.xen_mmap_data == 0) + } + #[cfg(not(feature = "xen"))] + { + false } } - /// Creates mmap region from Self. - pub fn mmap_region(&self, file: File) -> Result> { - MmapRegion::::from_file( - FileOffset::new(file, self.mmap_offset), - self.memory_size as usize, - ) - .map_err(MmapError::MmapRegion) - .map_err(|e| Error::ReqHandlerError(io::Error::other(e))) - } + pub fn memory_region(&self, file: File) -> Result> { + let size = self.memory_size as usize; + let guest_addr = GuestAddress(self.guest_phys_addr); - fn is_valid(&self) -> bool { - self.is_valid_common() - } -} + #[cfg(feature = "xen")] + if self.is_xen() { + let range = MmapRange::new( + size, + Some(FileOffset::new(file, self.mmap_offset)), + guest_addr, + self.xen_mmap_flags, + self.xen_mmap_data, + ); -#[cfg(feature = "xen")] -impl VhostUserMemoryRegion { - /// Create a new instance. - pub fn with_xen( - guest_phys_addr: u64, - memory_size: u64, - user_addr: u64, - mmap_offset: u64, - xen_mmap_flags: u32, - xen_mmap_data: u32, - ) -> Self { - VhostUserMemoryRegion { - guest_phys_addr, - memory_size, - user_addr, - mmap_offset, - xen_mmap_flags, - xen_mmap_data, + return GuestRegionXen::::from_range(range) + .map_err(|e| Error::ReqHandlerError(io::Error::other(e))) + .map(MemoryRegion::Xen); } - } - /// Creates mmap region from Self. - pub fn mmap_region(&self, file: File) -> Result> { - let range = MmapRange::new( - self.memory_size as usize, - Some(FileOffset::new(file, self.mmap_offset)), - GuestAddress(self.guest_phys_addr), - self.xen_mmap_flags, - self.xen_mmap_data, - ); - - MmapRegion::::from_range(range) - .map_err(MmapError::MmapRegion) + MmapRegion::from_file(FileOffset::new(file, self.mmap_offset), size) .map_err(|e| Error::ReqHandlerError(io::Error::other(e))) + .and_then(|region| { + GuestRegionMmap::new(region, guest_addr) + .ok_or(Error::ReqHandlerError(io::Error::other( + "invalid memory region", + ))) + .map(MemoryRegion::Unix) + }) } fn is_valid(&self) -> bool { if !self.is_valid_common() { - false - } else { + return false; + } + + #[cfg(feature = "xen")] + if self.is_xen() { // Only of one of FOREIGN or GRANT should be set. - match MmapXenFlags::from_bits(self.xen_mmap_flags) { - Some(flags) => flags.is_valid(), - None => false, - } + return MmapXenFlags::from_bits(self.xen_mmap_flags) + .is_none_or(|flags| flags.is_valid()); } + + true } } @@ -639,43 +621,12 @@ impl Deref for VhostUserSingleMemoryRegion { } } -#[cfg(not(feature = "xen"))] -impl VhostUserSingleMemoryRegion { - /// Create a new instance. - pub fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self { - VhostUserSingleMemoryRegion { - padding: 0, - region: VhostUserMemoryRegion::new( - guest_phys_addr, - memory_size, - user_addr, - mmap_offset, - ), - } - } -} - -#[cfg(feature = "xen")] -impl VhostUserSingleMemoryRegion { - /// Create a new instance. - pub fn new( - guest_phys_addr: u64, - memory_size: u64, - user_addr: u64, - mmap_offset: u64, - xen_mmap_flags: u32, - xen_mmap_data: u32, - ) -> Self { +#[cfg(feature = "vhost-user")] +impl From<&VhostUserMemoryRegionInfo> for VhostUserSingleMemoryRegion { + fn from(value: &VhostUserMemoryRegionInfo) -> Self { VhostUserSingleMemoryRegion { padding: 0, - region: VhostUserMemoryRegion::with_xen( - guest_phys_addr, - memory_size, - user_addr, - mmap_offset, - xen_mmap_flags, - xen_mmap_data, - ), + region: value.into(), } } } @@ -1126,20 +1077,6 @@ mod tests { use super::*; use std::mem; - #[cfg(feature = "xen")] - impl VhostUserMemoryRegion { - fn new(guest_phys_addr: u64, memory_size: u64, user_addr: u64, mmap_offset: u64) -> Self { - Self::with_xen( - guest_phys_addr, - memory_size, - user_addr, - mmap_offset, - MmapXenFlags::FOREIGN.bits(), - 0, - ) - } - } - #[test] fn check_transfer_state_direction_code() { let load_code: u32 = VhostTransferStateDirection::LOAD.into(); @@ -1261,7 +1198,15 @@ mod tests { #[test] fn check_user_memory_region() { - let mut msg = VhostUserMemoryRegion::new(0, 0x1000, 0, 0); + let mut msg = VhostUserMemoryRegion { + guest_phys_addr: 0, + memory_size: 0x1000, + user_addr: 0, + mmap_offset: 0, + #[cfg(feature = "xen")] + xen_mmap_flags: MmapXenFlags::FOREIGN.bits(), + ..Default::default() + }; assert!(msg.is_valid()); msg.guest_phys_addr = 0xFFFFFFFFFFFFEFFF; assert!(msg.is_valid());