// Copyright 2019 Intel Corporation. All Rights Reserved. // Copyright 2019-2021 Alibaba Cloud. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 //! Traits for vhost user backend servers to implement virtio data plain services. //! //! Define two traits for vhost user backend servers to implement virtio data plane services. //! The only difference between the two traits is mutability. The [VhostUserBackend] trait is //! designed with interior mutability, so the implementor may choose the suitable way to protect //! itself from concurrent accesses. The [VhostUserBackendMut] is designed without interior //! mutability, and an implementation of: //! ```ignore //! impl VhostUserBackend for RwLock { } //! ``` //! is provided for convenience. //! //! [VhostUserBackend]: trait.VhostUserBackend.html //! [VhostUserBackendMut]: trait.VhostUserBackendMut.html use std::io::Result; use std::ops::Deref; use std::sync::{Arc, Mutex, RwLock}; use vhost::vhost_user::message::VhostUserProtocolFeatures; use vhost::vhost_user::Slave; use vm_memory::bitmap::Bitmap; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use super::vring::VringT; use super::GM; /// Trait with interior mutability for vhost user backend servers to implement concrete services. /// /// To support multi-threading and asynchronous IO, we enforce `Send + Sync` bound. pub trait VhostUserBackend: Send + Sync where V: VringT>, B: Bitmap + 'static, { /// Get number of queues supported. fn num_queues(&self) -> usize; /// Get maximum queue size supported. fn max_queue_size(&self) -> usize; /// Get available virtio features. fn features(&self) -> u64; /// Set acknowledged virtio features. fn acked_features(&self, _features: u64) {} /// Get available vhost protocol features. fn protocol_features(&self) -> VhostUserProtocolFeatures; /// Enable or disable the virtio EVENT_IDX feature fn set_event_idx(&self, enabled: bool); /// Get virtio device configuration. /// /// A default implementation is provided as we cannot expect all backends to implement this /// function. fn get_config(&self, _offset: u32, _size: u32) -> Vec { Vec::new() } /// Set virtio device configuration. /// /// A default implementation is provided as we cannot expect all backends to implement this /// function. fn set_config(&self, _offset: u32, _buf: &[u8]) -> Result<()> { Ok(()) } /// Update guest memory regions. fn update_memory(&self, mem: GM) -> Result<()>; /// Set handler for communicating with the master by the slave communication channel. /// /// A default implementation is provided as we cannot expect all backends to implement this /// function. fn set_slave_req_fd(&self, _slave: Slave) {} /// Get the map to map queue index to worker thread index. /// /// A return value of [2, 2, 4] means: the first two queues will be handled by worker thread 0, /// the following two queues will be handled by worker thread 1, and the last four queues will /// be handled by worker thread 2. fn queues_per_thread(&self) -> Vec { vec![0xffff_ffff] } /// Provide an optional exit EventFd for the specified worker thread. /// /// If an (`EventFd`, `token`) pair is returned, the returned `EventFd` will be monitored for IO /// events by using epoll with the specified `token`. When the returned EventFd is written to, /// the worker thread will exit. fn exit_event(&self, _thread_index: usize) -> Option { None } /// Handle IO events for backend registered file descriptors. /// /// This function gets called if the backend registered some additional listeners onto specific /// file descriptors. The library can handle virtqueues on its own, but does not know what to /// do with events happening on custom listeners. fn handle_event( &self, device_event: u16, evset: EventSet, vrings: &[V], thread_id: usize, ) -> Result; } /// Trait without interior mutability for vhost user backend servers to implement concrete services. pub trait VhostUserBackendMut: Send + Sync where V: VringT>, B: Bitmap + 'static, { /// Get number of queues supported. fn num_queues(&self) -> usize; /// Get maximum queue size supported. fn max_queue_size(&self) -> usize; /// Get available virtio features. fn features(&self) -> u64; /// Set acknowledged virtio features. fn acked_features(&mut self, _features: u64) {} /// Get available vhost protocol features. fn protocol_features(&self) -> VhostUserProtocolFeatures; /// Enable or disable the virtio EVENT_IDX feature fn set_event_idx(&mut self, enabled: bool); /// Get virtio device configuration. /// /// A default implementation is provided as we cannot expect all backends to implement this /// function. fn get_config(&self, _offset: u32, _size: u32) -> Vec { Vec::new() } /// Set virtio device configuration. /// /// A default implementation is provided as we cannot expect all backends to implement this /// function. fn set_config(&mut self, _offset: u32, _buf: &[u8]) -> Result<()> { Ok(()) } /// Update guest memory regions. fn update_memory(&mut self, mem: GM) -> Result<()>; /// Set handler for communicating with the master by the slave communication channel. /// /// A default implementation is provided as we cannot expect all backends to implement this /// function. fn set_slave_req_fd(&mut self, _slave: Slave) {} /// Get the map to map queue index to worker thread index. /// /// A return value of [2, 2, 4] means: the first two queues will be handled by worker thread 0, /// the following two queues will be handled by worker thread 1, and the last four queues will /// be handled by worker thread 2. fn queues_per_thread(&self) -> Vec { vec![0xffff_ffff] } /// Provide an optional exit EventFd for the specified worker thread. /// /// If an (`EventFd`, `token`) pair is returned, the returned `EventFd` will be monitored for IO /// events by using epoll with the specified `token`. When the returned EventFd is written to, /// the worker thread will exit. fn exit_event(&self, _thread_index: usize) -> Option { None } /// Handle IO events for backend registered file descriptors. /// /// This function gets called if the backend registered some additional listeners onto specific /// file descriptors. The library can handle virtqueues on its own, but does not know what to /// do with events happening on custom listeners. fn handle_event( &mut self, device_event: u16, evset: EventSet, vrings: &[V], thread_id: usize, ) -> Result; } impl, V, B> VhostUserBackend for Arc where V: VringT>, B: Bitmap + 'static, { fn num_queues(&self) -> usize { self.deref().num_queues() } fn max_queue_size(&self) -> usize { self.deref().max_queue_size() } fn features(&self) -> u64 { self.deref().features() } fn acked_features(&self, features: u64) { self.deref().acked_features(features) } fn protocol_features(&self) -> VhostUserProtocolFeatures { self.deref().protocol_features() } fn set_event_idx(&self, enabled: bool) { self.deref().set_event_idx(enabled) } fn get_config(&self, offset: u32, size: u32) -> Vec { self.deref().get_config(offset, size) } fn set_config(&self, offset: u32, buf: &[u8]) -> Result<()> { self.deref().set_config(offset, buf) } fn update_memory(&self, mem: GM) -> Result<()> { self.deref().update_memory(mem) } fn set_slave_req_fd(&self, slave: Slave) { self.deref().set_slave_req_fd(slave) } fn queues_per_thread(&self) -> Vec { self.deref().queues_per_thread() } fn exit_event(&self, thread_index: usize) -> Option { self.deref().exit_event(thread_index) } fn handle_event( &self, device_event: u16, evset: EventSet, vrings: &[V], thread_id: usize, ) -> Result { self.deref() .handle_event(device_event, evset, vrings, thread_id) } } impl, V, B> VhostUserBackend for Mutex where V: VringT>, B: Bitmap + 'static, { fn num_queues(&self) -> usize { self.lock().unwrap().num_queues() } fn max_queue_size(&self) -> usize { self.lock().unwrap().max_queue_size() } fn features(&self) -> u64 { self.lock().unwrap().features() } fn acked_features(&self, features: u64) { self.lock().unwrap().acked_features(features) } fn protocol_features(&self) -> VhostUserProtocolFeatures { self.lock().unwrap().protocol_features() } fn set_event_idx(&self, enabled: bool) { self.lock().unwrap().set_event_idx(enabled) } fn get_config(&self, offset: u32, size: u32) -> Vec { self.lock().unwrap().get_config(offset, size) } fn set_config(&self, offset: u32, buf: &[u8]) -> Result<()> { self.lock().unwrap().set_config(offset, buf) } fn update_memory(&self, mem: GM) -> Result<()> { self.lock().unwrap().update_memory(mem) } fn set_slave_req_fd(&self, slave: Slave) { self.lock().unwrap().set_slave_req_fd(slave) } fn queues_per_thread(&self) -> Vec { self.lock().unwrap().queues_per_thread() } fn exit_event(&self, thread_index: usize) -> Option { self.lock().unwrap().exit_event(thread_index) } fn handle_event( &self, device_event: u16, evset: EventSet, vrings: &[V], thread_id: usize, ) -> Result { self.lock() .unwrap() .handle_event(device_event, evset, vrings, thread_id) } } impl, V, B> VhostUserBackend for RwLock where V: VringT>, B: Bitmap + 'static, { fn num_queues(&self) -> usize { self.read().unwrap().num_queues() } fn max_queue_size(&self) -> usize { self.read().unwrap().max_queue_size() } fn features(&self) -> u64 { self.read().unwrap().features() } fn acked_features(&self, features: u64) { self.write().unwrap().acked_features(features) } fn protocol_features(&self) -> VhostUserProtocolFeatures { self.read().unwrap().protocol_features() } fn set_event_idx(&self, enabled: bool) { self.write().unwrap().set_event_idx(enabled) } fn get_config(&self, offset: u32, size: u32) -> Vec { self.read().unwrap().get_config(offset, size) } fn set_config(&self, offset: u32, buf: &[u8]) -> Result<()> { self.write().unwrap().set_config(offset, buf) } fn update_memory(&self, mem: GM) -> Result<()> { self.write().unwrap().update_memory(mem) } fn set_slave_req_fd(&self, slave: Slave) { self.write().unwrap().set_slave_req_fd(slave) } fn queues_per_thread(&self) -> Vec { self.read().unwrap().queues_per_thread() } fn exit_event(&self, thread_index: usize) -> Option { self.read().unwrap().exit_event(thread_index) } fn handle_event( &self, device_event: u16, evset: EventSet, vrings: &[V], thread_id: usize, ) -> Result { self.write() .unwrap() .handle_event(device_event, evset, vrings, thread_id) } } #[cfg(test)] pub mod tests { use super::*; use crate::VringRwLock; use std::sync::Mutex; use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; pub struct MockVhostBackend { events: u64, event_idx: bool, acked_features: u64, } impl MockVhostBackend { pub fn new() -> Self { MockVhostBackend { events: 0, event_idx: false, acked_features: 0, } } } impl VhostUserBackendMut for MockVhostBackend { fn num_queues(&self) -> usize { 2 } fn max_queue_size(&self) -> usize { 256 } fn features(&self) -> u64 { 0xffff_ffff_ffff_ffff } fn acked_features(&mut self, features: u64) { self.acked_features = features; } fn protocol_features(&self) -> VhostUserProtocolFeatures { VhostUserProtocolFeatures::all() } fn set_event_idx(&mut self, enabled: bool) { self.event_idx = enabled; } fn get_config(&self, offset: u32, size: u32) -> Vec { assert_eq!(offset, 0x200); assert_eq!(size, 8); vec![0xa5u8; 8] } fn set_config(&mut self, offset: u32, buf: &[u8]) -> Result<()> { assert_eq!(offset, 0x200); assert_eq!(buf.len(), 8); assert_eq!(buf, &[0xa5u8; 8]); Ok(()) } fn update_memory(&mut self, _atomic_mem: GuestMemoryAtomic) -> Result<()> { Ok(()) } fn set_slave_req_fd(&mut self, _slave: Slave) {} fn queues_per_thread(&self) -> Vec { vec![1, 1] } fn exit_event(&self, _thread_index: usize) -> Option { let event_fd = EventFd::new(0).unwrap(); Some(event_fd) } fn handle_event( &mut self, _device_event: u16, _evset: EventSet, _vrings: &[VringRwLock], _thread_id: usize, ) -> Result { self.events += 1; Ok(false) } } #[test] fn test_new_mock_backend_mutex() { let backend = Arc::new(Mutex::new(MockVhostBackend::new())); assert_eq!(backend.num_queues(), 2); assert_eq!(backend.max_queue_size(), 256); assert_eq!(backend.features(), 0xffff_ffff_ffff_ffff); assert_eq!( backend.protocol_features(), VhostUserProtocolFeatures::all() ); assert_eq!(backend.queues_per_thread(), [1, 1]); assert_eq!(backend.get_config(0x200, 8), vec![0xa5; 8]); backend.set_config(0x200, &[0xa5; 8]).unwrap(); backend.acked_features(0xffff); assert_eq!(backend.lock().unwrap().acked_features, 0xffff); backend.set_event_idx(true); assert!(backend.lock().unwrap().event_idx); let _ = backend.exit_event(0).unwrap(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x100000), 0x10000)]).unwrap(), ); backend.update_memory(mem).unwrap(); } #[test] fn test_new_mock_backend_rwlock() { let backend = Arc::new(RwLock::new(MockVhostBackend::new())); assert_eq!(backend.num_queues(), 2); assert_eq!(backend.max_queue_size(), 256); assert_eq!(backend.features(), 0xffff_ffff_ffff_ffff); assert_eq!( backend.protocol_features(), VhostUserProtocolFeatures::all() ); assert_eq!(backend.queues_per_thread(), [1, 1]); assert_eq!(backend.get_config(0x200, 8), vec![0xa5; 8]); backend.set_config(0x200, &[0xa5; 8]).unwrap(); backend.acked_features(0xffff); assert_eq!(backend.read().unwrap().acked_features, 0xffff); backend.set_event_idx(true); assert!(backend.read().unwrap().event_idx); let _ = backend.exit_event(0).unwrap(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x100000), 0x10000)]).unwrap(), ); backend.update_memory(mem.clone()).unwrap(); let vring = VringRwLock::new(mem, 0x1000).unwrap(); backend .handle_event(0x1, EventSet::IN, &[vring], 0) .unwrap(); } }