diff --git a/build_test.py b/build_test.py index e500a9a5bf..3a40b01003 100755 --- a/build_test.py +++ b/build_test.py @@ -48,6 +48,7 @@ TEST_MODULES_PARALLEL = [ 'net_sys', 'net_util', 'syscall_defines', + 'vhost', 'virtio_sys', 'x86_64', ] diff --git a/src/hw/virtio/vhost/net.rs b/src/hw/virtio/vhost/net.rs index 8ddb6ded53..6fc978e6d1 100644 --- a/src/hw/virtio/vhost/net.rs +++ b/src/hw/virtio/vhost/net.rs @@ -13,6 +13,7 @@ use net_sys; use net_util::Tap; use sys_util::{EventFd, GuestMemory}; use vhost::Net as VhostNetHandle; +use vhost::net::NetT; use virtio_sys::{vhost, virtio_net}; use super::{Error, Result}; diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs index ebca53aebb..b4282d2471 100644 --- a/vhost/src/lib.rs +++ b/vhost/src/lib.rs @@ -7,22 +7,23 @@ extern crate net_util; extern crate sys_util; extern crate virtio_sys; -mod net; +pub mod net; pub use net::Net; +use std::io::Error as IoError; use std::mem; use std::os::unix::io::AsRawFd; use std::ptr::null; -use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, Error as SysError}; -use sys_util::{ioctl, ioctl_with_ref, ioctl_with_mut_ref, ioctl_with_ptr}; +use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError}; +use sys_util::{ioctl, ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref}; #[derive(Debug)] pub enum Error { /// Error opening vhost device. - VhostOpen(SysError), + VhostOpen(IoError), /// Error while running ioctl. - IoctlError(SysError), + IoctlError(IoError), /// Invalid queue. InvalidQueue, /// Invalid descriptor table address. @@ -37,7 +38,7 @@ pub enum Error { pub type Result = std::result::Result; fn ioctl_result() -> Result { - Err(Error::IoctlError(SysError::last())) + Err(Error::IoctlError(IoError::last_os_error())) } /// An interface for setting up vhost-based virtio devices. Vhost-based devices are different @@ -317,23 +318,19 @@ pub trait Vhost: AsRawFd + std::marker::Sized { // This ioctl is called on a valid vhost_net fd and has its // return value checked. - let ret = unsafe { - ioctl_with_ref(self, - virtio_sys::VHOST_SET_VRING_KICK(), - &vring_file) - }; + let ret = unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_KICK(), &vring_file) }; if ret < 0 { return ioctl_result(); } Ok(()) } - } #[cfg(test)] mod tests { use super::*; + use net::tests::FakeNet; use std::result; use sys_util::{GuestAddress, GuestMemory, GuestMemoryError}; @@ -343,30 +340,93 @@ mod tests { GuestMemory::new(&vec![(start_addr1, 0x100), (start_addr2, 0x400)]) } - #[test] - fn open_vhostnet() { + fn assert_ok_or_known_failure(res: Result) { + match res { + // FakeNet won't respond to ioctl's + Ok(_t) => {} + Err(Error::IoctlError(ref ioe)) if ioe.raw_os_error().unwrap() == 25 => {} + Err(e) => panic!("Unexpected Error:\n{:?}", e), + } + } + + fn create_fake_vhost_net () -> FakeNet { let gm = create_guest_memory().unwrap(); - Net::new(&gm).unwrap(); + FakeNet::new(&gm).unwrap() + } + + #[test] + fn test_create_fake_vhost_net() { + create_fake_vhost_net(); } #[test] fn set_owner() { - let gm = create_guest_memory().unwrap(); - let vhost_net = Net::new(&gm).unwrap(); - vhost_net.set_owner().unwrap(); + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_owner(); + assert_ok_or_known_failure(res); } #[test] fn get_features() { - let gm = create_guest_memory().unwrap(); - let vhost_net = Net::new(&gm).unwrap(); - vhost_net.get_features().unwrap(); + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.get_features(); + assert_ok_or_known_failure(res); } #[test] fn set_features() { - let gm = create_guest_memory().unwrap(); - let vhost_net = Net::new(&gm).unwrap(); - vhost_net.set_features(0).unwrap(); + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_features(0); + assert_ok_or_known_failure(res); + } + + #[test] + fn set_mem_table() { + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_mem_table(); + assert_ok_or_known_failure(res); + } + + #[test] + fn set_vring_num() { + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_vring_num(0, 1); + assert_ok_or_known_failure(res); + } + + #[test] + fn set_vring_addr() { + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_vring_addr( + 1, + 1, + 0, + 0x0, + GuestAddress(0x0), + GuestAddress(0x0), + GuestAddress(0x0), + None); + assert_ok_or_known_failure(res); + } + + #[test] + fn set_vring_base() { + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_vring_base(0, 1); + assert_ok_or_known_failure(res); + } + + #[test] + fn set_vring_call() { + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_vring_call(0, &EventFd::new().unwrap()); + assert_ok_or_known_failure(res); + } + + #[test] + fn set_vring_kick() { + let vhost_net = create_fake_vhost_net(); + let res = vhost_net.set_vring_kick(0, &EventFd::new().unwrap()); + assert_ok_or_known_failure(res); } } diff --git a/vhost/src/net.rs b/vhost/src/net.rs index b20bbfce89..f7c5dfd7a2 100644 --- a/vhost/src/net.rs +++ b/vhost/src/net.rs @@ -6,10 +6,11 @@ use libc; use net_util; use std::ffi::CString; use std::fs::File; +use std::io::Error as IoError; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use virtio_sys; -use sys_util::{ioctl_with_ref, Error as SysError, GuestMemory}; +use sys_util::{ioctl_with_ref, GuestMemory}; use super::{ioctl_result, Error, Result, Vhost}; @@ -26,6 +27,16 @@ pub struct Net { mem: GuestMemory, } +pub trait NetT { + /// Set the tap file descriptor that will serve as the VHOST_NET backend. + /// This will start the vhost worker for the given queue. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to modify. + /// * `fd` - Tap interface that will be used as the backend. + fn set_backend(&self, queue_index: usize, fd: &net_util::Tap) -> Result<()>; +} + impl Net { /// Opens /dev/vhost-net and holds a file descriptor open for it. /// @@ -40,7 +51,7 @@ impl Net { libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC) }; if fd < 0 { - return Err(Error::VhostOpen(SysError::last())); + return Err(Error::VhostOpen(IoError::last_os_error())); } Ok(Net { // There are no other users of this fd, so this is safe. @@ -48,14 +59,10 @@ impl Net { mem: mem.clone(), }) } +} - /// Set the tap file descriptor that will serve as the VHOST_NET backend. - /// This will start the vhost worker for the given queue. - /// - /// # Arguments - /// * `queue_index` - Index of the queue to modify. - /// * `fd` - Tap interface that will be used as the backend. - pub fn set_backend(&self, queue_index: usize, fd: &net_util::Tap) -> Result<()> { +impl NetT for Net { + fn set_backend(&self, queue_index: usize, fd: &net_util::Tap) -> Result<()> { let vring_file = virtio_sys::vhost_vring_file { index: queue_index as u32, fd: fd.as_raw_fd(), @@ -63,11 +70,8 @@ impl Net { // This ioctl is called on a valid vhost_net fd and has its // return value checked. - let ret = unsafe { - ioctl_with_ref(&self.fd, - virtio_sys::VHOST_NET_SET_BACKEND(), - &vring_file) - }; + let ret = + unsafe { ioctl_with_ref(&self.fd, virtio_sys::VHOST_NET_SET_BACKEND(), &vring_file) }; if ret < 0 { return ioctl_result(); } @@ -86,3 +90,55 @@ impl AsRawFd for Net { self.fd.as_raw_fd() } } + +#[cfg(test)] +pub mod tests { + use super::*; + use std::fs::OpenOptions; + use std::fs::remove_file; + + const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file"; + + pub struct FakeNet { + fd: File, + mem: GuestMemory, + } + + impl FakeNet { + pub fn new(mem: &GuestMemory) -> Result { + Ok(FakeNet { + fd: OpenOptions::new() + .read(true) + .append(true) + .create(true) + .open(TMP_FILE) + .unwrap(), + mem: mem.clone() + }) + } + } + + impl Drop for FakeNet { + fn drop(&mut self) { + let _ = remove_file(TMP_FILE); + } + } + + impl NetT for FakeNet { + fn set_backend(&self, _queue_index: usize, _fd: &net_util::Tap) -> Result<()> { + Ok(()) + } + } + + impl Vhost for FakeNet { + fn mem(&self) -> &GuestMemory { + &self.mem + } + } + + impl AsRawFd for FakeNet { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } + } +}