summary refs log tree commit diff
path: root/vhost
diff options
context:
space:
mode:
authorJason D. Clinton <jclinton@chromium.org>2017-09-07 20:51:07 -0600
committerchrome-bot <chrome-bot@chromium.org>2017-09-09 13:48:49 -0700
commit6f366b54604e4012b43822d5dc2afe7d1616287d (patch)
tree0df22ac05bb2f2ed17ad2d24652f54c110db5cbc /vhost
parent2bcf05b2afbcbe1287583a229dbb3e5b6c78aa8c (diff)
downloadcrosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.tar
crosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.tar.gz
crosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.tar.bz2
crosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.tar.lz
crosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.tar.xz
crosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.tar.zst
crosvm-6f366b54604e4012b43822d5dc2afe7d1616287d.zip
vhost: Fix-up failing tests and add a little more coverage
This fakes out the underlying Net implementation with FakeNet to try
and get some of the code a little further along before it
explodes. Then, we test for known failures when running without a real
vhost file descriptors.

This allows us to pass without running as root as we would expect
running on Paladins.

This is also the final module that was failing at ToT.

Also adds vhost to the build_test test targets.

BUG=none
TEST=Run unit tests:
cargo test -p crosvm -p data_model -p syscall_defines -p kernel_loader -p net_util -p x86_64 -p virtio_sys -p kvm_sys -p vhost -p io_jail -p net_sys -p sys_util -p kvm
Also ran ./build_test

Change-Id: Ie12d05c044634a660a234483532cf783e2a7fe84
Reviewed-on: https://chromium-review.googlesource.com/656278
Commit-Ready: Jason Clinton <jclinton@chromium.org>
Tested-by: Jason Clinton <jclinton@chromium.org>
Reviewed-by: Jason Clinton <jclinton@chromium.org>
Diffstat (limited to 'vhost')
-rw-r--r--vhost/src/lib.rs108
-rw-r--r--vhost/src/net.rs84
2 files changed, 154 insertions, 38 deletions
diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs
index ebca53a..b4282d2 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<T> = std::result::Result<T, Error>;
 
 fn ioctl_result<T>() -> Result<T> {
-    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<T>(res: Result<T>) {
+        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 b20bbfc..f7c5dfd 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<FakeNet> {
+            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()
+        }
+    }
+}