mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-01-28 19:29:20 +00:00
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>
This commit is contained in:
parent
2bcf05b2af
commit
6f366b5460
4 changed files with 156 additions and 38 deletions
|
@ -48,6 +48,7 @@ TEST_MODULES_PARALLEL = [
|
|||
'net_sys',
|
||||
'net_util',
|
||||
'syscall_defines',
|
||||
'vhost',
|
||||
'virtio_sys',
|
||||
'x86_64',
|
||||
]
|
||||
|
|
|
@ -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};
|
||||
|
|
108
vhost/src/lib.rs
108
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue