// Copyright (C) 2019-2021 Alibaba Cloud. All rights reserved. // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause // // Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD-Google file. //! Common traits and structs for vhost-kern and vhost-user backend drivers. use std::cell::RefCell; use std::os::unix::io::AsRawFd; use std::os::unix::io::RawFd; use std::sync::RwLock; use vm_memory::{bitmap::Bitmap, Address, GuestMemoryRegion, GuestRegionMmap}; use vmm_sys_util::eventfd::EventFd; #[cfg(feature = "vhost-user")] use super::vhost_user::message::{VhostUserMemoryRegion, VhostUserSingleMemoryRegion}; use super::{Error, Result}; /// Maximum number of memory regions supported. pub const VHOST_MAX_MEMORY_REGIONS: usize = 255; /// Vring configuration data. #[derive(Default, Clone, Copy)] pub struct VringConfigData { /// Maximum queue size supported by the driver. pub queue_max_size: u16, /// Actual queue size negotiated by the driver. pub queue_size: u16, /// Bitmask of vring flags. pub flags: u32, /// Descriptor table address. pub desc_table_addr: u64, /// Used ring buffer address. pub used_ring_addr: u64, /// Available ring buffer address. pub avail_ring_addr: u64, /// Optional address for logging. pub log_addr: Option, } impl VringConfigData { /// Check whether the log (flag, address) pair is valid. pub fn is_log_addr_valid(&self) -> bool { if self.flags & 0x1 != 0 && self.log_addr.is_none() { return false; } true } /// Get the log address, default to zero if not available. pub fn get_log_addr(&self) -> u64 { if self.flags & 0x1 != 0 && self.log_addr.is_some() { self.log_addr.unwrap() } else { 0 } } } /// Memory region configuration data. #[derive(Default, Clone, Copy)] pub struct VhostUserMemoryRegionInfo { /// Guest physical address of the memory region. pub guest_phys_addr: u64, /// Size of the memory region. pub memory_size: u64, /// Virtual address in the current process. pub userspace_addr: u64, /// Optional offset where region starts in the mapped memory. pub mmap_offset: u64, /// Optional file descriptor for mmap. pub mmap_handle: RawFd, #[cfg(feature = "xen")] /// Xen specific flags. pub xen_mmap_flags: u32, #[cfg(feature = "xen")] /// Xen specific data. pub xen_mmap_data: u32, } impl VhostUserMemoryRegionInfo { /// Creates Self from GuestRegionMmap. pub fn from_guest_region(region: &GuestRegionMmap) -> Result { let file_offset = region .file_offset() .ok_or(Error::InvalidGuestMemoryRegion)?; Ok(Self { 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(), }) } /// 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, ) } /// 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, ) } } /// Shared memory region data for logging dirty pages #[derive(Default, Clone, Copy)] pub struct VhostUserDirtyLogRegion { /// Size of the shared memory region for logging dirty pages pub mmap_size: u64, /// Offset where region starts pub mmap_offset: u64, /// File descriptor for mmap pub mmap_handle: RawFd, } /// Vhost memory access permission (VHOST_ACCESS_* mapping) #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum VhostAccess { /// No access. #[default] No = 0, /// Read-Only access. ReadOnly = 1, /// Write-Only access. WriteOnly = 2, /// Read and Write access. ReadWrite = 3, } /// Vhost IOTLB message type (VHOST_IOTLB_* mapping) #[repr(u8)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum VhostIotlbType { /// Empty message (not valid). #[default] Empty = 0, /// I/O virtual address mapping is missing or invalidated. Miss = 1, /// Update the I/O virtual address mapping. Update = 2, /// Invalidate the I/O virtual address mapping. Invalidate = 3, /// Access failed to an I/O virtual address. AccessFail = 4, /// Batch of multiple `Update` messages begins. BatchBegin = 5, /// Batch of multiple `Update` messages ends. BatchEnd = 6, } /// Vhost IOTLB message structure. #[derive(Default, Clone, Copy)] pub struct VhostIotlbMsg { /// I/O virtual address. pub iova: u64, /// Size of the I/O mapping. pub size: u64, /// Virtual address in the current process. pub userspace_addr: u64, /// Access permissions. pub perm: VhostAccess, /// Type of the message. pub msg_type: VhostIotlbType, } /// Vhost IOTLB message parser. pub trait VhostIotlbMsgParser { /// Parse the IOTLB message and fill a VhostIotlbMsg. /// /// # Arguments /// * `msg` - IOTLB message parsed. fn parse(&self, msg: &mut VhostIotlbMsg) -> Result<()>; } /// An interface for IOTLB messages support for vhost-based backend pub trait VhostIotlbBackend: std::marker::Sized { /// Send an IOTLB message to the vhost-based backend. /// /// # Arguments /// * `msg` - IOTLB message to send. fn send_iotlb_msg(&self, msg: &VhostIotlbMsg) -> Result<()>; } /// An interface for setting up vhost-based backend drivers with interior mutability. /// /// Vhost devices are subset of virtio devices, which improve virtio device's performance by /// delegating data plane operations to dedicated IO service processes. Vhost devices use the /// same virtqueue layout as virtio devices to allow vhost devices to be mapped directly to /// virtio devices. /// /// The purpose of vhost is to implement a subset of a virtio device's functionality outside the /// VMM process. Typically fast paths for IO operations are delegated to the dedicated IO service /// processes, and slow path for device configuration are still handled by the VMM process. It may /// also be used to control access permissions of virtio backend devices. pub trait VhostBackend: std::marker::Sized { /// Get a bitmask of supported virtio/vhost features. fn get_features(&self) -> Result; /// Inform the vhost subsystem which features to enable. /// This should be a subset of supported features from get_features(). /// /// # Arguments /// * `features` - Bitmask of features to set. fn set_features(&self, features: u64) -> Result<()>; /// Set the current process as the owner of the vhost backend. /// This must be run before any other vhost commands. fn set_owner(&self) -> Result<()>; /// Used to be sent to request disabling all rings /// This is no longer used. fn reset_owner(&self) -> Result<()>; /// Set the guest memory mappings for vhost to use. fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>; /// Set base address for page modification logging. fn set_log_base(&self, base: u64, region: Option) -> Result<()>; /// Specify an eventfd file descriptor to signal on log write. fn set_log_fd(&self, fd: RawFd) -> Result<()>; /// Set the number of descriptors in the vring. /// /// # Arguments /// * `queue_index` - Index of the queue to set descriptor count for. /// * `num` - Number of descriptors in the queue. fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()>; /// Set the addresses for a given vring. /// /// # Arguments /// * `queue_index` - Index of the queue to set addresses for. /// * `config_data` - Configuration data for a vring. fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()>; /// Set the first index to look for available descriptors. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `num` - Index where available descriptors start. fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()>; /// Get the available vring base offset. fn get_vring_base(&self, queue_index: usize) -> Result; /// Set the eventfd to trigger when buffers have been used by the host. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd to trigger. fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()>; /// Set the eventfd that will be signaled by the guest when buffers are /// available for the host to process. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()>; /// Set the eventfd that will be signaled by the guest when error happens. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()>; } /// An interface for setting up vhost-based backend drivers. /// /// Vhost devices are subset of virtio devices, which improve virtio device's performance by /// delegating data plane operations to dedicated IO service processes. Vhost devices use the /// same virtqueue layout as virtio devices to allow vhost devices to be mapped directly to /// virtio devices. /// /// The purpose of vhost is to implement a subset of a virtio device's functionality outside the /// VMM process. Typically fast paths for IO operations are delegated to the dedicated IO service /// processes, and slow path for device configuration are still handled by the VMM process. It may /// also be used to control access permissions of virtio backend devices. pub trait VhostBackendMut: std::marker::Sized { /// Get a bitmask of supported virtio/vhost features. fn get_features(&mut self) -> Result; /// Inform the vhost subsystem which features to enable. /// This should be a subset of supported features from get_features(). /// /// # Arguments /// * `features` - Bitmask of features to set. fn set_features(&mut self, features: u64) -> Result<()>; /// Set the current process as the owner of the vhost backend. /// This must be run before any other vhost commands. fn set_owner(&mut self) -> Result<()>; /// Used to be sent to request disabling all rings /// This is no longer used. fn reset_owner(&mut self) -> Result<()>; /// Set the guest memory mappings for vhost to use. fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>; /// Set base address for page modification logging. fn set_log_base(&mut self, base: u64, region: Option) -> Result<()>; /// Specify an eventfd file descriptor to signal on log write. fn set_log_fd(&mut self, fd: RawFd) -> Result<()>; /// Set the number of descriptors in the vring. /// /// # Arguments /// * `queue_index` - Index of the queue to set descriptor count for. /// * `num` - Number of descriptors in the queue. fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>; /// Set the addresses for a given vring. /// /// # Arguments /// * `queue_index` - Index of the queue to set addresses for. /// * `config_data` - Configuration data for a vring. fn set_vring_addr(&mut self, queue_index: usize, config_data: &VringConfigData) -> Result<()>; /// Set the first index to look for available descriptors. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `num` - Index where available descriptors start. fn set_vring_base(&mut self, queue_index: usize, base: u16) -> Result<()>; /// Get the available vring base offset. fn get_vring_base(&mut self, queue_index: usize) -> Result; /// Set the eventfd to trigger when buffers have been used by the host. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd to trigger. fn set_vring_call(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>; /// Set the eventfd that will be signaled by the guest when buffers are /// available for the host to process. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. fn set_vring_kick(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>; /// Set the eventfd that will be signaled by the guest when error happens. /// /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. fn set_vring_err(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>; } impl VhostBackend for RwLock { fn get_features(&self) -> Result { self.write().unwrap().get_features() } fn set_features(&self, features: u64) -> Result<()> { self.write().unwrap().set_features(features) } fn set_owner(&self) -> Result<()> { self.write().unwrap().set_owner() } fn reset_owner(&self) -> Result<()> { self.write().unwrap().reset_owner() } fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> { self.write().unwrap().set_mem_table(regions) } fn set_log_base(&self, base: u64, region: Option) -> Result<()> { self.write().unwrap().set_log_base(base, region) } fn set_log_fd(&self, fd: RawFd) -> Result<()> { self.write().unwrap().set_log_fd(fd) } fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> { self.write().unwrap().set_vring_num(queue_index, num) } fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> { self.write() .unwrap() .set_vring_addr(queue_index, config_data) } fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> { self.write().unwrap().set_vring_base(queue_index, base) } fn get_vring_base(&self, queue_index: usize) -> Result { self.write().unwrap().get_vring_base(queue_index) } fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> { self.write().unwrap().set_vring_call(queue_index, fd) } fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> { self.write().unwrap().set_vring_kick(queue_index, fd) } fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> { self.write().unwrap().set_vring_err(queue_index, fd) } } impl VhostBackend for RefCell { fn get_features(&self) -> Result { self.borrow_mut().get_features() } fn set_features(&self, features: u64) -> Result<()> { self.borrow_mut().set_features(features) } fn set_owner(&self) -> Result<()> { self.borrow_mut().set_owner() } fn reset_owner(&self) -> Result<()> { self.borrow_mut().reset_owner() } fn set_mem_table(&self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()> { self.borrow_mut().set_mem_table(regions) } fn set_log_base(&self, base: u64, region: Option) -> Result<()> { self.borrow_mut().set_log_base(base, region) } fn set_log_fd(&self, fd: RawFd) -> Result<()> { self.borrow_mut().set_log_fd(fd) } fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> { self.borrow_mut().set_vring_num(queue_index, num) } fn set_vring_addr(&self, queue_index: usize, config_data: &VringConfigData) -> Result<()> { self.borrow_mut().set_vring_addr(queue_index, config_data) } fn set_vring_base(&self, queue_index: usize, base: u16) -> Result<()> { self.borrow_mut().set_vring_base(queue_index, base) } fn get_vring_base(&self, queue_index: usize) -> Result { self.borrow_mut().get_vring_base(queue_index) } fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> { self.borrow_mut().set_vring_call(queue_index, fd) } fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> { self.borrow_mut().set_vring_kick(queue_index, fd) } fn set_vring_err(&self, queue_index: usize, fd: &EventFd) -> Result<()> { self.borrow_mut().set_vring_err(queue_index, fd) } } #[cfg(any(test, feature = "test-utils"))] impl VhostUserMemoryRegionInfo { /// creates instance of `VhostUserMemoryRegionInfo`. pub fn new( guest_phys_addr: u64, memory_size: u64, userspace_addr: u64, mmap_offset: u64, mmap_handle: RawFd, ) -> Self { Self { guest_phys_addr, memory_size, userspace_addr, mmap_offset, mmap_handle, #[cfg(feature = "xen")] xen_mmap_flags: vm_memory::MmapXenFlags::UNIX.bits(), #[cfg(feature = "xen")] xen_mmap_data: 0, } } } #[cfg(test)] mod tests { use super::*; struct MockBackend {} impl VhostBackendMut for MockBackend { fn get_features(&mut self) -> Result { Ok(0x1) } fn set_features(&mut self, features: u64) -> Result<()> { assert_eq!(features, 0x1); Ok(()) } fn set_owner(&mut self) -> Result<()> { Ok(()) } fn reset_owner(&mut self) -> Result<()> { Ok(()) } fn set_mem_table(&mut self, _regions: &[VhostUserMemoryRegionInfo]) -> Result<()> { Ok(()) } fn set_log_base( &mut self, base: u64, region: Option, ) -> Result<()> { assert_eq!(base, 0x100); let region = region.unwrap(); assert_eq!(region.mmap_size, 0x1000); assert_eq!(region.mmap_offset, 0x10); assert_eq!(region.mmap_handle, 100); Ok(()) } fn set_log_fd(&mut self, fd: RawFd) -> Result<()> { assert_eq!(fd, 100); Ok(()) } fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()> { assert_eq!(queue_index, 1); assert_eq!(num, 256); Ok(()) } fn set_vring_addr( &mut self, queue_index: usize, _config_data: &VringConfigData, ) -> Result<()> { assert_eq!(queue_index, 1); Ok(()) } fn set_vring_base(&mut self, queue_index: usize, base: u16) -> Result<()> { assert_eq!(queue_index, 1); assert_eq!(base, 2); Ok(()) } fn get_vring_base(&mut self, queue_index: usize) -> Result { assert_eq!(queue_index, 1); Ok(2) } fn set_vring_call(&mut self, queue_index: usize, _fd: &EventFd) -> Result<()> { assert_eq!(queue_index, 1); Ok(()) } fn set_vring_kick(&mut self, queue_index: usize, _fd: &EventFd) -> Result<()> { assert_eq!(queue_index, 1); Ok(()) } fn set_vring_err(&mut self, queue_index: usize, _fd: &EventFd) -> Result<()> { assert_eq!(queue_index, 1); Ok(()) } } #[test] fn test_vring_backend_mut() { let b = RwLock::new(MockBackend {}); assert_eq!(b.get_features().unwrap(), 0x1); b.set_features(0x1).unwrap(); b.set_owner().unwrap(); b.reset_owner().unwrap(); b.set_mem_table(&[]).unwrap(); b.set_log_base( 0x100, Some(VhostUserDirtyLogRegion { mmap_size: 0x1000, mmap_offset: 0x10, mmap_handle: 100, }), ) .unwrap(); b.set_log_fd(100).unwrap(); b.set_vring_num(1, 256).unwrap(); let config = VringConfigData { queue_max_size: 0x1000, queue_size: 0x2000, flags: 0x0, desc_table_addr: 0x4000, used_ring_addr: 0x5000, avail_ring_addr: 0x6000, log_addr: None, }; b.set_vring_addr(1, &config).unwrap(); b.set_vring_base(1, 2).unwrap(); assert_eq!(b.get_vring_base(1).unwrap(), 2); let eventfd = EventFd::new(0).unwrap(); b.set_vring_call(1, &eventfd).unwrap(); b.set_vring_kick(1, &eventfd).unwrap(); b.set_vring_err(1, &eventfd).unwrap(); } #[test] fn test_vring_config_data() { let mut config = VringConfigData { queue_max_size: 0x1000, queue_size: 0x2000, flags: 0x0, desc_table_addr: 0x4000, used_ring_addr: 0x5000, avail_ring_addr: 0x6000, log_addr: None, }; assert!(config.is_log_addr_valid()); assert_eq!(config.get_log_addr(), 0); config.flags = 0x1; assert!(!config.is_log_addr_valid()); assert_eq!(config.get_log_addr(), 0); config.log_addr = Some(0x7000); assert!(config.is_log_addr_valid()); assert_eq!(config.get_log_addr(), 0x7000); config.flags = 0x0; assert!(config.is_log_addr_valid()); assert_eq!(config.get_log_addr(), 0); } }