diff options
author | Liu Jiang <gerry@linux.alibaba.com> | 2021-02-21 22:56:35 +0800 |
---|---|---|
committer | Sergio Lopez <slp@sinrega.org> | 2021-03-01 12:50:56 +0100 |
commit | ec6eae722ef7c6fcecbb8acddf1d9905083dc3de (patch) | |
tree | de3776314e023cb30b9b5e631f49f84fad57fbdf /src | |
parent | 56b823482b1f47392aeec9051ed3bcf526926864 (diff) | |
download | vmm_vhost-ec6eae722ef7c6fcecbb8acddf1d9905083dc3de.tar.gz |
vhost_user: add more unit test cases
Add more unit test cases for vhost-user protocol.
Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/vhost_user/connection.rs | 34 | ||||
-rw-r--r-- | src/vhost_user/dummy_slave.rs | 48 | ||||
-rw-r--r-- | src/vhost_user/master.rs | 62 | ||||
-rw-r--r-- | src/vhost_user/master_req_handler.rs | 99 | ||||
-rw-r--r-- | src/vhost_user/mod.rs | 180 | ||||
-rw-r--r-- | src/vhost_user/slave.rs | 40 | ||||
-rw-r--r-- | src/vhost_user/slave_req_handler.rs | 21 |
8 files changed, 416 insertions, 80 deletions
@@ -74,7 +74,7 @@ pub enum Error { IoctlError(std::io::Error), /// Error from IO subsystem. IOError(std::io::Error), - #[cfg(feature = "vhost-user-master")] + #[cfg(feature = "vhost-user")] /// Error from the vhost-user subsystem. VhostUserProtocol(vhost_user::Error), } @@ -95,7 +95,7 @@ impl std::fmt::Display for Error { Error::VhostOpen(e) => write!(f, "failure in opening vhost file: {}", e), #[cfg(feature = "vhost-kern")] Error::IoctlError(e) => write!(f, "failure in vhost ioctl: {}", e), - #[cfg(feature = "vhost-user-master")] + #[cfg(feature = "vhost-user")] Error::VhostUserProtocol(e) => write!(f, "vhost-user: {}", e), } } @@ -151,4 +151,12 @@ mod tests { assert_eq!(format!("{:?}", Error::AvailAddress), "AvailAddress"); } + + #[cfg(feature = "vhost-user")] + #[test] + fn test_convert_from_vhost_user_error() { + let e: Error = vhost_user::Error::OversizedMsg.into(); + + assert_eq!(format!("{}", e), "vhost-user: oversized message"); + } } diff --git a/src/vhost_user/connection.rs b/src/vhost_user/connection.rs index d89f9c7..01bf124 100644 --- a/src/vhost_user/connection.rs +++ b/src/vhost_user/connection.rs @@ -606,29 +606,32 @@ fn get_sub_iovs_offset(iov_lens: &[usize], skip_size: usize) -> (usize, usize) { #[cfg(test)] mod tests { - use super::*; use std::fs::File; use std::io::{Read, Seek, SeekFrom, Write}; use std::os::unix::io::FromRawFd; + use vmm_sys_util::rand::rand_alphanumerics; use vmm_sys_util::tempfile::TempFile; - const UNIX_SOCKET_LISTENER: &'static str = "/tmp/vhost_user_test_rust_listener"; - const UNIX_SOCKET_CONNECTION: &'static str = "/tmp/vhost_user_test_rust_connection"; - const UNIX_SOCKET_DATA: &'static str = "/tmp/vhost_user_test_rust_data"; - const UNIX_SOCKET_FD: &'static str = "/tmp/vhost_user_test_rust_fd"; - const UNIX_SOCKET_SEND: &'static str = "/tmp/vhost_user_test_rust_send"; + fn temp_path() -> String { + format!( + "/tmp/vhost_test_{}", + rand_alphanumerics(8).to_str().unwrap() + ) + } #[test] fn create_listener() { - let listener = Listener::new(UNIX_SOCKET_LISTENER, true).unwrap(); + let path = temp_path(); + let listener = Listener::new(&path, true).unwrap(); assert!(listener.as_raw_fd() > 0); } #[test] fn accept_connection() { - let listener = Listener::new(UNIX_SOCKET_CONNECTION, true).unwrap(); + let path = temp_path(); + let listener = Listener::new(&path, true).unwrap(); listener.set_nonblocking(true).unwrap(); // accept on a fd without incoming connection @@ -638,9 +641,10 @@ mod tests { #[test] fn send_data() { - let listener = Listener::new(UNIX_SOCKET_DATA, true).unwrap(); + let path = temp_path(); + let listener = Listener::new(&path, true).unwrap(); listener.set_nonblocking(true).unwrap(); - let mut master = Endpoint::<MasterReq>::connect(UNIX_SOCKET_DATA).unwrap(); + let mut master = Endpoint::<MasterReq>::connect(&path).unwrap(); let sock = listener.accept().unwrap().unwrap(); let mut slave = Endpoint::<MasterReq>::from_stream(sock); @@ -663,9 +667,10 @@ mod tests { #[test] fn send_fd() { - let listener = Listener::new(UNIX_SOCKET_FD, true).unwrap(); + let path = temp_path(); + let listener = Listener::new(&path, true).unwrap(); listener.set_nonblocking(true).unwrap(); - let mut master = Endpoint::<MasterReq>::connect(UNIX_SOCKET_FD).unwrap(); + let mut master = Endpoint::<MasterReq>::connect(&path).unwrap(); let sock = listener.accept().unwrap().unwrap(); let mut slave = Endpoint::<MasterReq>::from_stream(sock); @@ -816,9 +821,10 @@ mod tests { #[test] fn send_recv() { - let listener = Listener::new(UNIX_SOCKET_SEND, true).unwrap(); + let path = temp_path(); + let listener = Listener::new(&path, true).unwrap(); listener.set_nonblocking(true).unwrap(); - let mut master = Endpoint::<MasterReq>::connect(UNIX_SOCKET_SEND).unwrap(); + let mut master = Endpoint::<MasterReq>::connect(&path).unwrap(); let sock = listener.accept().unwrap().unwrap(); let mut slave = Endpoint::<MasterReq>::from_stream(sock); diff --git a/src/vhost_user/dummy_slave.rs b/src/vhost_user/dummy_slave.rs index 53887e2..99f08e7 100644 --- a/src/vhost_user/dummy_slave.rs +++ b/src/vhost_user/dummy_slave.rs @@ -1,9 +1,10 @@ // Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +use std::os::unix::io::RawFd; + use super::message::*; use super::*; -use std::os::unix::io::RawFd; pub const MAX_QUEUE_NUM: usize = 2; pub const MAX_VRING_NUM: usize = 256; @@ -34,7 +35,7 @@ impl DummySlaveReqHandler { } } -impl VhostUserSlaveReqHandler for DummySlaveReqHandler { +impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler { fn set_owner(&mut self) -> Result<()> { if self.owned { return Err(Error::InvalidOperation); @@ -83,30 +84,10 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler { Ok(()) } - fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> { - Ok(VhostUserProtocolFeatures::all()) - } - - fn set_protocol_features(&mut self, features: u64) -> Result<()> { - // Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must - // support this message even before VHOST_USER_SET_FEATURES was - // called. - // What happens if the master calls set_features() with - // VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this - // interface? - self.acked_protocol_features = features; - Ok(()) - } - fn set_mem_table(&mut self, _ctx: &[VhostUserMemoryRegion], _fds: &[RawFd]) -> Result<()> { - // TODO Ok(()) } - fn get_queue_num(&mut self) -> Result<u64> { - Ok(MAX_QUEUE_NUM as u64) - } - fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> { if index as usize >= self.queue_num || num == 0 || num as usize > MAX_VRING_NUM { return Err(Error::InvalidParam); @@ -199,6 +180,25 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler { Ok(()) } + fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> { + Ok(VhostUserProtocolFeatures::all()) + } + + fn set_protocol_features(&mut self, features: u64) -> Result<()> { + // Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must + // support this message even before VHOST_USER_SET_FEATURES was + // called. + // What happens if the master calls set_features() with + // VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this + // interface? + self.acked_protocol_features = features; + Ok(()) + } + + fn get_queue_num(&mut self) -> Result<u64> { + Ok(MAX_QUEUE_NUM as u64) + } + fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> { // This request should be handled only when VHOST_USER_F_PROTOCOL_FEATURES // has been negotiated. @@ -222,7 +222,7 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler { size: u32, _flags: VhostUserConfigFlags, ) -> Result<Vec<u8>> { - if self.acked_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 { + if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 { return Err(Error::InvalidOperation); } else if offset < VHOST_USER_CONFIG_OFFSET || offset >= VHOST_USER_CONFIG_SIZE @@ -236,7 +236,7 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler { fn set_config(&mut self, offset: u32, buf: &[u8], _flags: VhostUserConfigFlags) -> Result<()> { let size = buf.len() as u32; - if self.acked_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 { + if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 { return Err(Error::InvalidOperation); } else if offset < VHOST_USER_CONFIG_OFFSET || offset >= VHOST_USER_CONFIG_SIZE diff --git a/src/vhost_user/master.rs b/src/vhost_user/master.rs index 65c7960..cc754b5 100644 --- a/src/vhost_user/master.rs +++ b/src/vhost_user/master.rs @@ -393,7 +393,10 @@ impl VhostUserMaster for Master { return error_code(VhostUserError::SlaveInternalError); } else if body_reply.size != body.size || body_reply.size as usize != buf.len() { return error_code(VhostUserError::InvalidMessage); + } else if body_reply.offset != body.offset { + return error_code(VhostUserError::InvalidMessage); } + Ok((body_reply, buf_reply)) } @@ -571,6 +574,7 @@ impl MasterInternal { ) -> VhostUserResult<(T, Vec<u8>, Option<Vec<RawFd>>)> { if mem::size_of::<T>() > MAX_MSG_SIZE || hdr.get_size() as usize <= mem::size_of::<T>() + || hdr.get_size() as usize > MAX_MSG_SIZE || hdr.is_reply() { return Err(VhostUserError::InvalidParam); @@ -586,11 +590,8 @@ impl MasterInternal { { Endpoint::<MasterReq>::close_rfds(rfds); return Err(VhostUserError::InvalidMessage); - } else if bytes > MAX_MSG_SIZE - mem::size_of::<T>() { + } else if bytes != buf.len() { return Err(VhostUserError::InvalidMessage); - } else if bytes < buf.len() { - // It's safe because we have checked the buffer size - unsafe { buf.set_len(bytes) }; } Ok((body, buf, rfds)) } @@ -638,11 +639,14 @@ impl MasterInternal { mod tests { use super::super::connection::Listener; use super::*; + use vmm_sys_util::rand::rand_alphanumerics; - const UNIX_SOCKET_MASTER: &'static str = "/tmp/vhost_user_test_rust_master"; - const UNIX_SOCKET_MASTER2: &'static str = "/tmp/vhost_user_test_rust_master2"; - const UNIX_SOCKET_MASTER3: &'static str = "/tmp/vhost_user_test_rust_master3"; - const UNIX_SOCKET_MASTER4: &'static str = "/tmp/vhost_user_test_rust_master4"; + fn temp_path() -> String { + format!( + "/tmp/vhost_test_{}", + rand_alphanumerics(8).to_str().unwrap() + ) + } fn create_pair(path: &str) -> (Master, Endpoint<MasterReq>) { let listener = Listener::new(path, true).unwrap(); @@ -653,14 +657,15 @@ mod tests { } #[test] - #[ignore] fn create_master() { - let listener = Listener::new(UNIX_SOCKET_MASTER, true).unwrap(); + let path = temp_path(); + let listener = Listener::new(&path, true).unwrap(); listener.set_nonblocking(true).unwrap(); - let master = Master::connect(UNIX_SOCKET_MASTER, 1).unwrap(); + let master = Master::connect(&path, 1).unwrap(); let mut slave = Endpoint::<MasterReq>::from_stream(listener.accept().unwrap().unwrap()); + assert!(master.as_raw_fd() > 0); // Send two messages continuously master.set_owner().unwrap(); master.reset_owner().unwrap(); @@ -679,24 +684,24 @@ mod tests { } #[test] - #[ignore] fn test_create_failure() { - let _ = Listener::new(UNIX_SOCKET_MASTER2, true).unwrap(); - let _ = Listener::new(UNIX_SOCKET_MASTER2, false).is_err(); - assert!(Master::connect(UNIX_SOCKET_MASTER2, 1).is_err()); + let path = temp_path(); + let _ = Listener::new(&path, true).unwrap(); + let _ = Listener::new(&path, false).is_err(); + assert!(Master::connect(&path, 1).is_err()); - let listener = Listener::new(UNIX_SOCKET_MASTER2, true).unwrap(); - assert!(Listener::new(UNIX_SOCKET_MASTER2, false).is_err()); + let listener = Listener::new(&path, true).unwrap(); + assert!(Listener::new(&path, false).is_err()); listener.set_nonblocking(true).unwrap(); - let _master = Master::connect(UNIX_SOCKET_MASTER2, 1).unwrap(); + let _master = Master::connect(&path, 1).unwrap(); let _slave = listener.accept().unwrap().unwrap(); } #[test] - #[ignore] fn test_features() { - let (master, mut peer) = create_pair(UNIX_SOCKET_MASTER3); + let path = temp_path(); + let (master, mut peer) = create_pair(&path); master.set_owner().unwrap(); let (hdr, rfds) = peer.recv_header().unwrap(); @@ -713,6 +718,9 @@ mod tests { let (_hdr, rfds) = peer.recv_header().unwrap(); assert!(rfds.is_none()); + let hdr = VhostUserMsgHeader::new(MasterReq::SET_FEATURES, 0x4, 8); + let msg = VhostUserU64::new(0x15); + peer.send_message(&hdr, &msg, None).unwrap(); master.set_features(0x15).unwrap(); let (_hdr, msg, rfds) = peer.recv_body::<VhostUserU64>().unwrap(); assert!(rfds.is_none()); @@ -726,9 +734,9 @@ mod tests { } #[test] - #[ignore] fn test_protocol_features() { - let (mut master, mut peer) = create_pair(UNIX_SOCKET_MASTER4); + let path = temp_path(); + let (mut master, mut peer) = create_pair(&path); master.set_owner().unwrap(); let (hdr, rfds) = peer.recv_header().unwrap(); @@ -775,14 +783,4 @@ mod tests { peer.send_message(&hdr, &msg, None).unwrap(); assert!(master.get_protocol_features().is_err()); } - - #[test] - fn test_set_mem_table() { - // TODO - } - - #[test] - fn test_get_ring_num() { - // TODO - } } diff --git a/src/vhost_user/master_req_handler.rs b/src/vhost_user/master_req_handler.rs index 02c2bb7..fb33f15 100644 --- a/src/vhost_user/master_req_handler.rs +++ b/src/vhost_user/master_req_handler.rs @@ -377,3 +377,102 @@ impl<S: VhostUserMasterReqHandler> AsRawFd for MasterReqHandler<S> { self.sub_sock.as_raw_fd() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "vhost-user-slave")] + use crate::vhost_user::SlaveFsCacheReq; + #[cfg(feature = "vhost-user-slave")] + use std::os::unix::io::FromRawFd; + + struct MockMasterReqHandler {} + + impl VhostUserMasterReqHandlerMut for MockMasterReqHandler { + /// Handle virtio-fs map file requests from the slave. + fn fs_slave_map(&mut self, _fs: &VhostUserFSSlaveMsg, fd: RawFd) -> HandlerResult<u64> { + // Safe because we have just received the rawfd from kernel. + unsafe { libc::close(fd) }; + Ok(0) + } + + /// Handle virtio-fs unmap file requests from the slave. + fn fs_slave_unmap(&mut self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) + } + } + + #[test] + fn test_new_master_req_handler() { + let backend = Arc::new(Mutex::new(MockMasterReqHandler {})); + let mut handler = MasterReqHandler::new(backend).unwrap(); + + assert!(handler.get_tx_raw_fd() >= 0); + assert!(handler.as_raw_fd() >= 0); + handler.check_state().unwrap(); + + assert_eq!(handler.error, None); + handler.set_failed(libc::EAGAIN); + assert_eq!(handler.error, Some(libc::EAGAIN)); + handler.check_state().unwrap_err(); + } + + #[cfg(feature = "vhost-user-slave")] + #[test] + fn test_master_slave_req_handler() { + let backend = Arc::new(Mutex::new(MockMasterReqHandler {})); + let mut handler = MasterReqHandler::new(backend).unwrap(); + + let fd = unsafe { libc::dup(handler.get_tx_raw_fd()) }; + if fd < 0 { + panic!("failed to duplicated tx fd!"); + } + let stream = unsafe { UnixStream::from_raw_fd(fd) }; + let fs_cache = SlaveFsCacheReq::from_stream(stream); + + std::thread::spawn(move || { + let res = handler.handle_request().unwrap(); + assert_eq!(res, 0); + handler.handle_request().unwrap_err(); + }); + + fs_cache + .fs_slave_map(&VhostUserFSSlaveMsg::default(), fd) + .unwrap(); + // When REPLY_ACK has not been negotiated, the master has no way to detect failure from + // slave side. + fs_cache + .fs_slave_unmap(&VhostUserFSSlaveMsg::default()) + .unwrap(); + } + + #[cfg(feature = "vhost-user-slave")] + #[test] + fn test_master_slave_req_handler_with_ack() { + let backend = Arc::new(Mutex::new(MockMasterReqHandler {})); + let mut handler = MasterReqHandler::new(backend).unwrap(); + handler.set_reply_ack_flag(true); + + let fd = unsafe { libc::dup(handler.get_tx_raw_fd()) }; + if fd < 0 { + panic!("failed to duplicated tx fd!"); + } + let stream = unsafe { UnixStream::from_raw_fd(fd) }; + let fs_cache = SlaveFsCacheReq::from_stream(stream); + + std::thread::spawn(move || { + let res = handler.handle_request().unwrap(); + assert_eq!(res, 0); + handler.handle_request().unwrap_err(); + }); + + fs_cache.set_reply_ack_flag(true); + fs_cache + .fs_slave_map(&VhostUserFSSlaveMsg::default(), fd) + .unwrap(); + fs_cache + .fs_slave_unmap(&VhostUserFSSlaveMsg::default()) + .unwrap_err(); + } +} diff --git a/src/vhost_user/mod.rs b/src/vhost_user/mod.rs index 91e4203..bf0a261 100644 --- a/src/vhost_user/mod.rs +++ b/src/vhost_user/mod.rs @@ -175,21 +175,32 @@ pub type Result<T> = std::result::Result<T, Error>; /// Result of request handler. pub type HandlerResult<T> = std::result::Result<T, IOError>; -#[cfg(all(test, feature = "vhost-user-master", feature = "vhost-user-slave"))] +#[cfg(all(test, feature = "vhost-user-slave"))] mod dummy_slave; #[cfg(all(test, feature = "vhost-user-master", feature = "vhost-user-slave"))] mod tests { + use std::os::unix::io::AsRawFd; + use std::sync::{Arc, Barrier, Mutex}; + use std::thread; + use vmm_sys_util::rand::rand_alphanumerics; + use super::dummy_slave::{DummySlaveReqHandler, VIRTIO_FEATURES}; use super::message::*; use super::*; use crate::backend::VhostBackend; - use std::sync::{Arc, Barrier, Mutex}; - use std::thread; + use crate::{VhostUserMemoryRegionInfo, VringConfigData}; + + fn temp_path() -> String { + format!( + "/tmp/vhost_test_{}", + rand_alphanumerics(8).to_str().unwrap() + ) + } fn create_slave<S: VhostUserSlaveReqHandler>( path: &str, - backend: Arc<Mutex<S>>, + backend: Arc<S>, ) -> (Master, SlaveReqHandler<S>) { let listener = Listener::new(path, true).unwrap(); let mut slave_listener = SlaveListener::new(listener, backend).unwrap(); @@ -208,8 +219,8 @@ mod tests { #[test] fn test_set_owner() { let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new())); - let (master, mut slave) = - create_slave("/tmp/vhost_user_lib_unit_test_owner", slave_be.clone()); + let path = temp_path(); + let (master, mut slave) = create_slave(&path, slave_be.clone()); assert_eq!(slave_be.lock().unwrap().owned, false); master.set_owner().unwrap(); @@ -224,14 +235,60 @@ mod tests { fn test_set_features() { let mbar = Arc::new(Barrier::new(2)); let sbar = mbar.clone(); + let path = temp_path(); + let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new())); + let (mut master, mut slave) = create_slave(&path, slave_be.clone()); + + thread::spawn(move || { + slave.handle_request().unwrap(); + assert_eq!(slave_be.lock().unwrap().owned, true); + + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + assert_eq!( + slave_be.lock().unwrap().acked_features, + VIRTIO_FEATURES & !0x1 + ); + + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + assert_eq!( + slave_be.lock().unwrap().acked_protocol_features, + VhostUserProtocolFeatures::all().bits() + ); + + sbar.wait(); + }); + + master.set_owner().unwrap(); + + // set virtio features + let features = master.get_features().unwrap(); + assert_eq!(features, VIRTIO_FEATURES); + master.set_features(VIRTIO_FEATURES & !0x1).unwrap(); + + // set vhost protocol features + let features = master.get_protocol_features().unwrap(); + assert_eq!(features.bits(), VhostUserProtocolFeatures::all().bits()); + master.set_protocol_features(features).unwrap(); + + mbar.wait(); + } + + #[test] + fn test_master_slave_process() { + let mbar = Arc::new(Barrier::new(2)); + let sbar = mbar.clone(); + let path = temp_path(); let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new())); - let (mut master, mut slave) = - create_slave("/tmp/vhost_user_lib_unit_test_feature", slave_be.clone()); + let (mut master, mut slave) = create_slave(&path, slave_be.clone()); thread::spawn(move || { + // set_own() slave.handle_request().unwrap(); assert_eq!(slave_be.lock().unwrap().owned, true); + // get/set_features() slave.handle_request().unwrap(); slave.handle_request().unwrap(); assert_eq!( @@ -246,6 +303,36 @@ mod tests { VhostUserProtocolFeatures::all().bits() ); + // get_queue_num() + slave.handle_request().unwrap(); + + // set_mem_table() + slave.handle_request().unwrap(); + + // get/set_config() + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + + // set_slave_request_fd + slave.handle_request().unwrap(); + + // set_vring_enable + slave.handle_request().unwrap(); + + /* + // set_log_base,set_log_fd() + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + */ + + // set_vring_xxx + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + slave.handle_request().unwrap(); + sbar.wait(); }); @@ -261,6 +348,83 @@ mod tests { assert_eq!(features.bits(), VhostUserProtocolFeatures::all().bits()); master.set_protocol_features(features).unwrap(); + let num = master.get_queue_num().unwrap(); + assert_eq!(num, 2); + + let eventfd = vmm_sys_util::eventfd::EventFd::new(0).unwrap(); + let mem = [VhostUserMemoryRegionInfo { + guest_phys_addr: 0, + memory_size: 0x10_0000, + userspace_addr: 0, + mmap_offset: 0, + mmap_handle: eventfd.as_raw_fd(), + }]; + master.set_mem_table(&mem).unwrap(); + + master + .set_config(0x100, VhostUserConfigFlags::WRITABLE, &[0xa5u8]) + .unwrap(); + let buf = [0x0u8; 4]; + let (reply_body, reply_payload) = master + .get_config(0x100, 4, VhostUserConfigFlags::empty(), &buf) + .unwrap(); + let offset = reply_body.offset; + assert_eq!(offset, 0x100); + assert_eq!(reply_payload[0], 0xa5); + + master.set_slave_request_fd(eventfd.as_raw_fd()).unwrap(); + master.set_vring_enable(0, true).unwrap(); + + /* + master.set_log_base(0, Some(eventfd.as_raw_fd())).unwrap(); + master.set_log_fd(eventfd.as_raw_fd()).unwrap(); + */ + + master.set_vring_num(0, 256).unwrap(); + master.set_vring_base(0, 0).unwrap(); + let config = VringConfigData { + queue_max_size: 256, + queue_size: 128, + flags: VhostUserVringAddrFlags::VHOST_VRING_F_LOG.bits(), + desc_table_addr: 0x1000, + used_ring_addr: 0x2000, + avail_ring_addr: 0x3000, + log_addr: Some(0x4000), + }; + master.set_vring_addr(0, &config).unwrap(); + master.set_vring_call(0, &eventfd).unwrap(); + master.set_vring_kick(0, &eventfd).unwrap(); + master.set_vring_err(0, &eventfd).unwrap(); + mbar.wait(); } + + #[test] + fn test_error_display() { + assert_eq!(format!("{}", Error::InvalidParam), "invalid parameters"); + assert_eq!(format!("{}", Error::InvalidOperation), "invalid operation"); + } + + #[test] + fn test_should_reconnect() { + assert_eq!(Error::PartialMessage.should_reconnect(), true); + assert_eq!(Error::SlaveInternalError.should_reconnect(), true); + assert_eq!(Error::MasterInternalError.should_reconnect(), true); + assert_eq!(Error::InvalidParam.should_reconnect(), false); + assert_eq!(Error::InvalidOperation.should_reconnect(), false); + assert_eq!(Error::InvalidMessage.should_reconnect(), false); + assert_eq!(Error::IncorrectFds.should_reconnect(), false); + assert_eq!(Error::OversizedMsg.should_reconnect(), false); + assert_eq!(Error::FeatureMismatch.should_reconnect(), false); + } + + #[test] + fn test_error_from_sys_util_error() { + let e: Error = vmm_sys_util::errno::Error::new(libc::EAGAIN.into()).into(); + if let Error::SocketRetry(e1) = e { + assert_eq!(e1.raw_os_error().unwrap(), libc::EAGAIN); + } else { + panic!("invalid error code conversion!"); + } + } } diff --git a/src/vhost_user/slave.rs b/src/vhost_user/slave.rs index c167dce..fb65c41 100644 --- a/src/vhost_user/slave.rs +++ b/src/vhost_user/slave.rs @@ -44,3 +44,43 @@ impl<S: VhostUserSlaveReqHandler> SlaveListener<S> { self.listener.set_nonblocking(block) } } + +#[cfg(test)] +mod tests { + use std::sync::Mutex; + + use super::*; + use crate::vhost_user::dummy_slave::DummySlaveReqHandler; + + #[test] + fn test_slave_listener_set_nonblocking() { + let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new())); + let listener = + Listener::new("/tmp/vhost_user_lib_unit_test_slave_nonblocking", true).unwrap(); + let slave_listener = SlaveListener::new(listener, backend).unwrap(); + + slave_listener.set_nonblocking(true).unwrap(); + slave_listener.set_nonblocking(false).unwrap(); + slave_listener.set_nonblocking(false).unwrap(); + slave_listener.set_nonblocking(true).unwrap(); + slave_listener.set_nonblocking(true).unwrap(); + } + + #[cfg(feature = "vhost-user-master")] + #[test] + fn test_slave_listener_accept() { + use super::super::Master; + + let path = "/tmp/vhost_user_lib_unit_test_slave_accept"; + let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new())); + let listener = Listener::new(path, true).unwrap(); + let mut slave_listener = SlaveListener::new(listener, backend).unwrap(); + + slave_listener.set_nonblocking(true).unwrap(); + assert!(slave_listener.accept().unwrap().is_none()); + assert!(slave_listener.accept().unwrap().is_none()); + + let _master = Master::connect(path, 1).unwrap(); + let _slave = slave_listener.accept().unwrap().unwrap(); + } +} diff --git a/src/vhost_user/slave_req_handler.rs b/src/vhost_user/slave_req_handler.rs index 344afff..ff07304 100644 --- a/src/vhost_user/slave_req_handler.rs +++ b/src/vhost_user/slave_req_handler.rs @@ -743,3 +743,24 @@ impl<S: VhostUserSlaveReqHandler> AsRawFd for SlaveReqHandler<S> { self.main_sock.as_raw_fd() } } + +#[cfg(test)] +mod tests { + use std::os::unix::io::AsRawFd; + + use super::*; + use crate::vhost_user::dummy_slave::DummySlaveReqHandler; + + #[test] + fn test_slave_req_handler_new() { + let (p1, _p2) = UnixStream::pair().unwrap(); + let endpoint = Endpoint::<MasterReq>::from_stream(p1); + let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new())); + let mut handler = SlaveReqHandler::new(endpoint, backend); + + handler.check_state().unwrap(); + handler.set_failed(libc::EAGAIN); + handler.check_state().unwrap_err(); + assert!(handler.as_raw_fd() >= 0); + } +} |