use std::fs::File;
use std::io::Result;
use std::ops::Deref;
use std::sync::{Arc, Mutex, RwLock};
use vhost::vhost_user::message::{
VhostTransferStateDirection, VhostTransferStatePhase, VhostUserProtocolFeatures,
VhostUserShMemConfig, VhostUserSharedMsg,
};
use vhost::vhost_user::Backend;
use vm_memory::bitmap::Bitmap;
use vmm_sys_util::epoll::EventSet;
use vmm_sys_util::event::{EventConsumer, EventNotifier};
use vhost::vhost_user::GpuBackend;
use super::vring::VringT;
use super::GM;
pub trait VhostUserBackend: Send + Sync {
type Bitmap: Bitmap + 'static;
type Vring: VringT<GM<Self::Bitmap>>;
fn num_queues(&self) -> usize;
fn max_queue_size(&self) -> usize;
fn features(&self) -> u64;
fn acked_features(&self, _features: u64) {}
fn protocol_features(&self) -> VhostUserProtocolFeatures;
fn reset_device(&self) {}
fn set_event_idx(&self, enabled: bool);
fn get_config(&self, _offset: u32, _size: u32) -> Vec<u8> {
Vec::new()
}
fn set_config(&self, _offset: u32, _buf: &[u8]) -> Result<()> {
Ok(())
}
fn update_memory(&self, mem: GM<Self::Bitmap>) -> Result<()>;
fn set_backend_req_fd(&self, _backend: Backend) {}
fn get_shared_object(&self, _uuid: VhostUserSharedMsg) -> Result<File> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support get shared object",
))
}
fn set_gpu_socket(&self, _gpu_backend: GpuBackend) -> Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"backend does not support set_gpu_socket() / VHOST_USER_GPU_SET_SOCKET",
))
}
fn queues_per_thread(&self) -> Vec<u64> {
vec![0xffff_ffff]
}
fn exit_event(&self, _thread_index: usize) -> Option<(EventConsumer, EventNotifier)> {
None
}
fn handle_event(
&self,
device_event: u16,
evset: EventSet,
vrings: &[Self::Vring],
thread_id: usize,
) -> Result<()>;
fn set_device_state_fd(
&self,
_direction: VhostTransferStateDirection,
_phase: VhostTransferStatePhase,
_file: File,
) -> Result<Option<File>> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support state transfer",
))
}
fn check_device_state(&self) -> Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support state transfer",
))
}
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support shared memory regions",
))
}
}
pub trait VhostUserBackendMut: Send + Sync {
type Bitmap: Bitmap + 'static;
type Vring: VringT<GM<Self::Bitmap>>;
fn num_queues(&self) -> usize;
fn max_queue_size(&self) -> usize;
fn features(&self) -> u64;
fn acked_features(&mut self, _features: u64) {}
fn protocol_features(&self) -> VhostUserProtocolFeatures;
fn reset_device(&mut self) {}
fn set_event_idx(&mut self, enabled: bool);
fn get_config(&self, _offset: u32, _size: u32) -> Vec<u8> {
Vec::new()
}
fn set_config(&mut self, _offset: u32, _buf: &[u8]) -> Result<()> {
Ok(())
}
fn update_memory(&mut self, mem: GM<Self::Bitmap>) -> Result<()>;
fn set_backend_req_fd(&mut self, _backend: Backend) {}
fn get_shared_object(&mut self, _uuid: VhostUserSharedMsg) -> Result<File> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support get shared object",
))
}
fn set_gpu_socket(&mut self, _gpu_backend: GpuBackend) -> Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"backend does not support set_gpu_socket() / VHOST_USER_GPU_SET_SOCKET",
))
}
fn queues_per_thread(&self) -> Vec<u64> {
vec![0xffff_ffff]
}
fn exit_event(&self, _thread_index: usize) -> Option<(EventConsumer, EventNotifier)> {
None
}
fn handle_event(
&mut self,
device_event: u16,
evset: EventSet,
vrings: &[Self::Vring],
thread_id: usize,
) -> Result<()>;
fn set_device_state_fd(
&mut self,
_direction: VhostTransferStateDirection,
_phase: VhostTransferStatePhase,
_file: File,
) -> Result<Option<File>> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support state transfer",
))
}
fn check_device_state(&self) -> Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support state transfer",
))
}
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"back end does not support shared memory regions",
))
}
}
impl<T: VhostUserBackend> VhostUserBackend for Arc<T> {
type Bitmap = T::Bitmap;
type Vring = T::Vring;
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 reset_device(&self) {
self.deref().reset_device()
}
fn set_event_idx(&self, enabled: bool) {
self.deref().set_event_idx(enabled)
}
fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
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<Self::Bitmap>) -> Result<()> {
self.deref().update_memory(mem)
}
fn set_backend_req_fd(&self, backend: Backend) {
self.deref().set_backend_req_fd(backend)
}
fn get_shared_object(&self, uuid: VhostUserSharedMsg) -> Result<File> {
self.deref().get_shared_object(uuid)
}
fn set_gpu_socket(&self, gpu_backend: GpuBackend) -> Result<()> {
self.deref().set_gpu_socket(gpu_backend)
}
fn queues_per_thread(&self) -> Vec<u64> {
self.deref().queues_per_thread()
}
fn exit_event(&self, thread_index: usize) -> Option<(EventConsumer, EventNotifier)> {
self.deref().exit_event(thread_index)
}
fn handle_event(
&self,
device_event: u16,
evset: EventSet,
vrings: &[Self::Vring],
thread_id: usize,
) -> Result<()> {
self.deref()
.handle_event(device_event, evset, vrings, thread_id)
}
fn set_device_state_fd(
&self,
direction: VhostTransferStateDirection,
phase: VhostTransferStatePhase,
file: File,
) -> Result<Option<File>> {
self.deref().set_device_state_fd(direction, phase, file)
}
fn check_device_state(&self) -> Result<()> {
self.deref().check_device_state()
}
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
self.deref().get_shmem_config()
}
}
impl<T: VhostUserBackendMut> VhostUserBackend for Mutex<T> {
type Bitmap = T::Bitmap;
type Vring = T::Vring;
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 reset_device(&self) {
self.lock().unwrap().reset_device()
}
fn set_event_idx(&self, enabled: bool) {
self.lock().unwrap().set_event_idx(enabled)
}
fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
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<Self::Bitmap>) -> Result<()> {
self.lock().unwrap().update_memory(mem)
}
fn set_backend_req_fd(&self, backend: Backend) {
self.lock().unwrap().set_backend_req_fd(backend)
}
fn get_shared_object(&self, uuid: VhostUserSharedMsg) -> Result<File> {
self.lock().unwrap().get_shared_object(uuid)
}
fn set_gpu_socket(&self, gpu_backend: GpuBackend) -> Result<()> {
self.lock().unwrap().set_gpu_socket(gpu_backend)
}
fn queues_per_thread(&self) -> Vec<u64> {
self.lock().unwrap().queues_per_thread()
}
fn exit_event(&self, thread_index: usize) -> Option<(EventConsumer, EventNotifier)> {
self.lock().unwrap().exit_event(thread_index)
}
fn handle_event(
&self,
device_event: u16,
evset: EventSet,
vrings: &[Self::Vring],
thread_id: usize,
) -> Result<()> {
self.lock()
.unwrap()
.handle_event(device_event, evset, vrings, thread_id)
}
fn set_device_state_fd(
&self,
direction: VhostTransferStateDirection,
phase: VhostTransferStatePhase,
file: File,
) -> Result<Option<File>> {
self.lock()
.unwrap()
.set_device_state_fd(direction, phase, file)
}
fn check_device_state(&self) -> Result<()> {
self.lock().unwrap().check_device_state()
}
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
self.lock().unwrap().get_shmem_config()
}
}
impl<T: VhostUserBackendMut> VhostUserBackend for RwLock<T> {
type Bitmap = T::Bitmap;
type Vring = T::Vring;
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 reset_device(&self) {
self.write().unwrap().reset_device()
}
fn set_event_idx(&self, enabled: bool) {
self.write().unwrap().set_event_idx(enabled)
}
fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
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<Self::Bitmap>) -> Result<()> {
self.write().unwrap().update_memory(mem)
}
fn set_backend_req_fd(&self, backend: Backend) {
self.write().unwrap().set_backend_req_fd(backend)
}
fn get_shared_object(&self, uuid: VhostUserSharedMsg) -> Result<File> {
self.write().unwrap().get_shared_object(uuid)
}
fn set_gpu_socket(&self, gpu_backend: GpuBackend) -> Result<()> {
self.write().unwrap().set_gpu_socket(gpu_backend)
}
fn queues_per_thread(&self) -> Vec<u64> {
self.read().unwrap().queues_per_thread()
}
fn exit_event(&self, thread_index: usize) -> Option<(EventConsumer, EventNotifier)> {
self.read().unwrap().exit_event(thread_index)
}
fn handle_event(
&self,
device_event: u16,
evset: EventSet,
vrings: &[Self::Vring],
thread_id: usize,
) -> Result<()> {
self.write()
.unwrap()
.handle_event(device_event, evset, vrings, thread_id)
}
fn set_device_state_fd(
&self,
direction: VhostTransferStateDirection,
phase: VhostTransferStatePhase,
file: File,
) -> Result<Option<File>> {
self.write()
.unwrap()
.set_device_state_fd(direction, phase, file)
}
fn check_device_state(&self) -> Result<()> {
self.read().unwrap().check_device_state()
}
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
self.read().unwrap().get_shmem_config()
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use crate::VringRwLock;
use std::sync::Mutex;
use uuid::Uuid;
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
use vmm_sys_util::event::{new_event_consumer_and_notifier, EventFlag};
pub struct MockVhostBackend {
events: u64,
event_idx: bool,
acked_features: u64,
exit_event_fds: Vec<(EventConsumer, EventNotifier)>,
}
impl MockVhostBackend {
pub fn new() -> Self {
let mut backend = MockVhostBackend {
events: 0,
event_idx: false,
acked_features: 0,
exit_event_fds: vec![],
};
backend.exit_event_fds = (0..backend.queues_per_thread().len())
.map(|_| {
new_event_consumer_and_notifier(EventFlag::NONBLOCK)
.expect("Failed to new EventNotifier and EventConsumer")
})
.collect();
backend
}
pub fn events(&self) -> u64 {
self.events
}
}
impl VhostUserBackendMut for MockVhostBackend {
type Bitmap = ();
type Vring = VringRwLock;
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 reset_device(&mut self) {
self.event_idx = false;
self.events = 0;
self.acked_features = 0;
}
fn set_event_idx(&mut self, enabled: bool) {
self.event_idx = enabled;
}
fn get_config(&self, offset: u32, size: u32) -> Vec<u8> {
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<GuestMemoryMmap>) -> Result<()> {
Ok(())
}
fn set_backend_req_fd(&mut self, _backend: Backend) {}
fn get_shared_object(&mut self, _uuid: VhostUserSharedMsg) -> Result<File> {
let file = tempfile::tempfile().unwrap();
Ok(file)
}
fn queues_per_thread(&self) -> Vec<u64> {
vec![1, 1]
}
fn exit_event(&self, thread_index: usize) -> Option<(EventConsumer, EventNotifier)> {
self.exit_event_fds.get(thread_index).map(|(s, r)| {
(
s.try_clone().expect("Failed to clone EventConsumer"),
r.try_clone().expect("Failed to clone EventNotifier"),
)
})
}
fn handle_event(
&mut self,
_device_event: u16,
_evset: EventSet,
_vrings: &[VringRwLock],
_thread_id: usize,
) -> Result<()> {
self.events += 1;
Ok(())
}
}
#[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 uuid = VhostUserSharedMsg {
uuid: Uuid::new_v4(),
};
backend.get_shared_object(uuid).unwrap();
let mem = GuestMemoryAtomic::new(
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x100000), 0x10000)]).unwrap(),
);
backend.update_memory(mem).unwrap();
backend.reset_device();
assert!(backend.lock().unwrap().events == 0);
assert!(!backend.lock().unwrap().event_idx);
assert!(backend.lock().unwrap().acked_features == 0);
}
#[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 uuid = VhostUserSharedMsg {
uuid: Uuid::new_v4(),
};
backend.get_shared_object(uuid).unwrap();
let vring = VringRwLock::new(mem, 0x1000).unwrap();
backend
.handle_event(0x1, EventSet::IN, &[vring], 0)
.unwrap();
backend.reset_device();
assert!(backend.read().unwrap().events == 0);
assert!(!backend.read().unwrap().event_idx);
assert!(backend.read().unwrap().acked_features == 0);
}
}