mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-12-24 11:58:41 +00:00
hw/virtio/vhost: Add simple tests backed by fakes
This slightly advances the use of fakes to test higher level application logic. The fakes are rudimentary at this point, but I wanted to get feedback on the addition of generics in order to facilitate swaping concrete implementations out with fakes in higher level code. BUG=none TEST=./build_test and 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 Change-Id: Ib64581014391f49cff30ada10677bbbcd0088f20 Reviewed-on: https://chromium-review.googlesource.com/689740 Commit-Ready: Jason Clinton <jclinton@chromium.org> Tested-by: Jason Clinton <jclinton@chromium.org> Reviewed-by: Stephen Barber <smbarber@chromium.org>
This commit is contained in:
parent
19e57b9532
commit
865323d0ed
9 changed files with 293 additions and 87 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -24,10 +24,12 @@ dependencies = [
|
|||
"kernel_loader 0.1.0",
|
||||
"kvm 0.1.0",
|
||||
"libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net_util 0.1.0",
|
||||
"plugin_proto 0.5.0",
|
||||
"qcow 0.1.0",
|
||||
"qcow_utils 0.1.0",
|
||||
"sys_util 0.1.0",
|
||||
"vhost 0.1.0",
|
||||
"vm_control 0.1.0",
|
||||
"x86_64 0.1.0",
|
||||
]
|
||||
|
|
|
@ -20,6 +20,8 @@ sys_util = { path = "sys_util" }
|
|||
kernel_loader = { path = "kernel_loader" }
|
||||
libc = "=0.2.34"
|
||||
byteorder = "=1.1.0"
|
||||
net_util = { path = "net_util" }
|
||||
vhost = { path = "vhost" }
|
||||
vm_control = { path = "vm_control" }
|
||||
data_model = { path = "data_model" }
|
||||
qcow = { path = "qcow" }
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
@ -13,8 +12,8 @@ use std::thread;
|
|||
|
||||
use libc::EAGAIN;
|
||||
use net_sys;
|
||||
use net_util::{Tap, Error as TapError};
|
||||
use sys_util::{Error as SysError};
|
||||
use net_util::{Error as TapError, TapT};
|
||||
use sys_util::Error as SysError;
|
||||
use sys_util::{EventFd, GuestMemory, Pollable, Poller};
|
||||
use virtio_sys::{vhost, virtio_net};
|
||||
use virtio_sys::virtio_net::virtio_net_hdr_v1;
|
||||
|
@ -50,11 +49,11 @@ pub enum NetError {
|
|||
PollError(SysError),
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
struct Worker<T: TapT> {
|
||||
mem: GuestMemory,
|
||||
rx_queue: Queue,
|
||||
tx_queue: Queue,
|
||||
tap: Tap,
|
||||
tap: T,
|
||||
interrupt_status: Arc<AtomicUsize>,
|
||||
interrupt_evt: EventFd,
|
||||
rx_buf: [u8; MAX_BUFFER_SIZE],
|
||||
|
@ -66,7 +65,10 @@ struct Worker {
|
|||
acked_features: u64,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
impl<T> Worker<T>
|
||||
where
|
||||
T: TapT,
|
||||
{
|
||||
fn signal_used_queue(&self) {
|
||||
self.interrupt_status
|
||||
.fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst);
|
||||
|
@ -226,7 +228,7 @@ impl Worker {
|
|||
const KILL: u32 = 4;
|
||||
|
||||
'poll: loop {
|
||||
let tokens = match poller.poll(&[(RX_TAP, &self.tap as &Pollable),
|
||||
let tokens = match poller.poll(&[(RX_TAP, &self.tap),
|
||||
(RX_QUEUE, &rx_queue_evt as &Pollable),
|
||||
(TX_QUEUE, &tx_queue_evt as &Pollable),
|
||||
(KILL, &kill_evt as &Pollable)]) {
|
||||
|
@ -274,21 +276,24 @@ impl Worker {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Net {
|
||||
pub struct Net<T: TapT> {
|
||||
workers_kill_evt: Option<EventFd>,
|
||||
kill_evt: EventFd,
|
||||
tap: Option<Tap>,
|
||||
tap: Option<T>,
|
||||
avail_features: u64,
|
||||
acked_features: u64,
|
||||
}
|
||||
|
||||
impl Net {
|
||||
impl<T> Net<T>
|
||||
where
|
||||
T: TapT,
|
||||
{
|
||||
/// Create a new virtio network device with the given IP address and
|
||||
/// netmask.
|
||||
pub fn new(ip_addr: Ipv4Addr, netmask: Ipv4Addr) -> Result<Net, NetError> {
|
||||
pub fn new(ip_addr: Ipv4Addr, netmask: Ipv4Addr) -> Result<Net<T>, NetError> {
|
||||
let kill_evt = EventFd::new().map_err(NetError::CreateKillEventFd)?;
|
||||
|
||||
let tap = Tap::new().map_err(NetError::TapOpen)?;
|
||||
let tap: T = T::new().map_err(NetError::TapOpen)?;
|
||||
tap.set_ip_addr(ip_addr).map_err(NetError::TapSetIp)?;
|
||||
tap.set_netmask(netmask)
|
||||
.map_err(NetError::TapSetNetmask)?;
|
||||
|
@ -306,24 +311,25 @@ impl Net {
|
|||
|
||||
let avail_features =
|
||||
1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM | 1 << virtio_net::VIRTIO_NET_F_CSUM |
|
||||
1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4 |
|
||||
1 << virtio_net::VIRTIO_NET_F_GUEST_UFO |
|
||||
1 << virtio_net::VIRTIO_NET_F_HOST_TSO4 |
|
||||
1 << virtio_net::VIRTIO_NET_F_HOST_UFO | 1 << vhost::VIRTIO_F_VERSION_1;
|
||||
1 << virtio_net::VIRTIO_NET_F_GUEST_TSO4 |
|
||||
1 << virtio_net::VIRTIO_NET_F_GUEST_UFO |
|
||||
1 << virtio_net::VIRTIO_NET_F_HOST_TSO4 |
|
||||
1 << virtio_net::VIRTIO_NET_F_HOST_UFO | 1 << vhost::VIRTIO_F_VERSION_1;
|
||||
|
||||
Ok(Net {
|
||||
workers_kill_evt: Some(kill_evt
|
||||
.try_clone()
|
||||
.map_err(NetError::CloneKillEventFd)?),
|
||||
kill_evt: kill_evt,
|
||||
tap: Some(tap),
|
||||
avail_features: avail_features,
|
||||
acked_features: 0u64,
|
||||
})
|
||||
workers_kill_evt: Some(kill_evt.try_clone().map_err(NetError::CloneKillEventFd)?),
|
||||
kill_evt: kill_evt,
|
||||
tap: Some(tap),
|
||||
avail_features: avail_features,
|
||||
acked_features: 0u64,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
impl<T> Drop for Net<T>
|
||||
where
|
||||
T: TapT,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// Only kill the child if it claimed its eventfd.
|
||||
if self.workers_kill_evt.is_none() {
|
||||
|
@ -333,7 +339,10 @@ impl Drop for Net {
|
|||
}
|
||||
}
|
||||
|
||||
impl VirtioDevice for Net {
|
||||
impl<T> VirtioDevice for Net<T>
|
||||
where
|
||||
T: 'static + TapT,
|
||||
{
|
||||
fn keep_fds(&self) -> Vec<RawFd> {
|
||||
let mut keep_fds = Vec::new();
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ use std::sync::atomic::AtomicUsize;
|
|||
use std::thread;
|
||||
|
||||
use net_sys;
|
||||
use net_util::Tap;
|
||||
use net_util::TapT;
|
||||
|
||||
use sys_util::{EventFd, GuestMemory};
|
||||
use vhost::Net as VhostNetHandle;
|
||||
use vhost::net::NetT;
|
||||
use vhost::NetT as VhostNetT;
|
||||
use virtio_sys::{vhost, virtio_net};
|
||||
|
||||
use super::{Error, Result};
|
||||
|
@ -24,23 +24,27 @@ const QUEUE_SIZE: u16 = 256;
|
|||
const NUM_QUEUES: usize = 2;
|
||||
const QUEUE_SIZES: &'static [u16] = &[QUEUE_SIZE; NUM_QUEUES];
|
||||
|
||||
pub struct Net {
|
||||
pub struct Net<T: TapT, U: VhostNetT<T>> {
|
||||
workers_kill_evt: Option<EventFd>,
|
||||
kill_evt: EventFd,
|
||||
tap: Option<Tap>,
|
||||
vhost_net_handle: Option<VhostNetHandle>,
|
||||
tap: Option<T>,
|
||||
vhost_net_handle: Option<U>,
|
||||
vhost_interrupt: Option<EventFd>,
|
||||
avail_features: u64,
|
||||
acked_features: u64,
|
||||
}
|
||||
|
||||
impl Net {
|
||||
impl<T, U> Net<T, U>
|
||||
where
|
||||
T: TapT,
|
||||
U: VhostNetT<T>,
|
||||
{
|
||||
/// Create a new virtio network device with the given IP address and
|
||||
/// netmask.
|
||||
pub fn new(ip_addr: Ipv4Addr, netmask: Ipv4Addr, mem: &GuestMemory) -> Result<Net> {
|
||||
pub fn new(ip_addr: Ipv4Addr, netmask: Ipv4Addr, mem: &GuestMemory) -> Result<Net<T, U>> {
|
||||
let kill_evt = EventFd::new().map_err(Error::CreateKillEventFd)?;
|
||||
|
||||
let tap = Tap::new().map_err(Error::TapOpen)?;
|
||||
let tap: T = T::new().map_err(Error::TapOpen)?;
|
||||
tap.set_ip_addr(ip_addr).map_err(Error::TapSetIp)?;
|
||||
tap.set_netmask(netmask).map_err(Error::TapSetNetmask)?;
|
||||
|
||||
|
@ -54,7 +58,7 @@ impl Net {
|
|||
tap.set_vnet_hdr_size(vnet_hdr_size).map_err(Error::TapSetVnetHdrSize)?;
|
||||
|
||||
tap.enable().map_err(Error::TapEnable)?;
|
||||
let vhost_net_handle = VhostNetHandle::new(mem).map_err(Error::VhostOpen)?;
|
||||
let vhost_net_handle = U::new(mem).map_err(Error::VhostOpen)?;
|
||||
|
||||
let avail_features =
|
||||
1 << virtio_net::VIRTIO_NET_F_GUEST_CSUM | 1 << virtio_net::VIRTIO_NET_F_CSUM |
|
||||
|
@ -79,7 +83,11 @@ impl Net {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Net {
|
||||
impl<T, U> Drop for Net<T, U>
|
||||
where
|
||||
T: TapT,
|
||||
U: VhostNetT<T>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
// Only kill the child if it claimed its eventfd.
|
||||
if self.workers_kill_evt.is_none() {
|
||||
|
@ -89,7 +97,11 @@ impl Drop for Net {
|
|||
}
|
||||
}
|
||||
|
||||
impl VirtioDevice for Net {
|
||||
impl<T, U> VirtioDevice for Net<T, U>
|
||||
where
|
||||
T: TapT + 'static,
|
||||
U: VhostNetT<T> + 'static,
|
||||
{
|
||||
fn keep_fds(&self) -> Vec<RawFd> {
|
||||
let mut keep_fds = Vec::new();
|
||||
|
||||
|
@ -182,7 +194,7 @@ impl VirtioDevice for Net {
|
|||
status,
|
||||
interrupt_evt,
|
||||
acked_features);
|
||||
let activate_vqs = |handle: &VhostNetHandle| -> Result<()> {
|
||||
let activate_vqs = |handle: &U| -> Result<()> {
|
||||
for idx in 0..NUM_QUEUES {
|
||||
handle
|
||||
.set_backend(idx, &tap)
|
||||
|
@ -207,3 +219,69 @@ impl VirtioDevice for Net {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use std::result;
|
||||
use net_util::fakes::FakeTap;
|
||||
use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
|
||||
use vhost::net::fakes::FakeNet;
|
||||
|
||||
fn create_guest_memory() -> result::Result<GuestMemory, GuestMemoryError> {
|
||||
let start_addr1 = GuestAddress(0x0);
|
||||
let start_addr2 = GuestAddress(0x100);
|
||||
GuestMemory::new(&vec![(start_addr1, 0x100), (start_addr2, 0x400)])
|
||||
}
|
||||
|
||||
fn create_net_common() -> Net<FakeTap, FakeNet<FakeTap>> {
|
||||
let guest_memory = create_guest_memory().unwrap();
|
||||
Net::<FakeTap, FakeNet<FakeTap>>::new(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
Ipv4Addr::new(255, 255, 255, 0),
|
||||
&guest_memory,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_net() {
|
||||
create_net_common();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keep_fds() {
|
||||
let net = create_net_common();
|
||||
let fds = net.keep_fds();
|
||||
assert!(fds.len() >= 1, "We should have gotten at least one fd");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn features() {
|
||||
let net = create_net_common();
|
||||
assert_eq!(net.features(0), 822135939);
|
||||
assert_eq!(net.features(1), 1);
|
||||
assert_eq!(net.features(2), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ack_features() {
|
||||
let mut net = create_net_common();
|
||||
// Just testing that we don't panic, for now
|
||||
net.ack_features(0, 1);
|
||||
net.ack_features(1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn activate() {
|
||||
let mut net = create_net_common();
|
||||
let guest_memory = create_guest_memory().unwrap();
|
||||
// Just testing that we don't panic, for now
|
||||
net.activate(
|
||||
guest_memory,
|
||||
EventFd::new().unwrap(),
|
||||
Arc::new(AtomicUsize::new(0)),
|
||||
vec![Queue::new(1)],
|
||||
vec![EventFd::new().unwrap()],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,9 +67,30 @@ pub struct Tap {
|
|||
if_name: [u8; 16usize],
|
||||
}
|
||||
|
||||
impl Tap {
|
||||
pub trait TapT: Read + Write + AsRawFd + Pollable + Send + Sized {
|
||||
/// Create a new tap interface.
|
||||
pub fn new() -> Result<Tap> {
|
||||
fn new() -> Result<Self>;
|
||||
|
||||
/// Set the host-side IP address for the tap interface.
|
||||
fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Set the netmask for the subnet that the tap interface will exist on.
|
||||
fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Set the offload flags for the tap interface.
|
||||
fn set_offload(&self, flags: c_uint) -> Result<()>;
|
||||
|
||||
/// Enable the tap interface.
|
||||
fn enable(&self) -> Result<()>;
|
||||
|
||||
/// Set the size of the vnet hdr.
|
||||
fn set_vnet_hdr_size(&self, size: c_int) -> Result<()>;
|
||||
|
||||
fn get_ifreq(&self) -> net_sys::ifreq;
|
||||
}
|
||||
|
||||
impl TapT for Tap {
|
||||
fn new() -> Result<Tap> {
|
||||
// Open calls are safe because we give a constant nul-terminated
|
||||
// string and verify the result.
|
||||
let fd = unsafe {
|
||||
|
@ -118,8 +139,7 @@ impl Tap {
|
|||
})
|
||||
}
|
||||
|
||||
/// Set the host-side IP address for the tap interface.
|
||||
pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
|
||||
fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> {
|
||||
let sock = create_socket()?;
|
||||
let addr = create_sockaddr(ip_addr);
|
||||
|
||||
|
@ -141,8 +161,7 @@ impl Tap {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the netmask for the subnet that the tap interface will exist on.
|
||||
pub fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
|
||||
fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> {
|
||||
let sock = create_socket()?;
|
||||
let addr = create_sockaddr(netmask);
|
||||
|
||||
|
@ -164,8 +183,7 @@ impl Tap {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the offload flags for the tap interface.
|
||||
pub fn set_offload(&self, flags: c_uint) -> Result<()> {
|
||||
fn set_offload(&self, flags: c_uint) -> Result<()> {
|
||||
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
let ret =
|
||||
unsafe { ioctl_with_val(&self.tap_file, net_sys::TUNSETOFFLOAD(), flags as c_ulong) };
|
||||
|
@ -176,8 +194,7 @@ impl Tap {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Enable the tap interface.
|
||||
pub fn enable(&self) -> Result<()> {
|
||||
fn enable(&self) -> Result<()> {
|
||||
let sock = create_socket()?;
|
||||
|
||||
let mut ifreq = self.get_ifreq();
|
||||
|
@ -199,8 +216,7 @@ impl Tap {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the size of the vnet hdr.
|
||||
pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> {
|
||||
fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> {
|
||||
// ioctl is safe. Called with a valid tap fd, and we check the return.
|
||||
let ret = unsafe { ioctl_with_ref(&self.tap_file, net_sys::TUNSETVNETHDRSZ(), &size) };
|
||||
if ret < 0 {
|
||||
|
@ -254,6 +270,90 @@ unsafe impl Pollable for Tap {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod fakes {
|
||||
use super::*;
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::remove_file;
|
||||
|
||||
const TMP_FILE: &str = "/tmp/crosvm_tap_test_file";
|
||||
|
||||
pub struct FakeTap {
|
||||
tap_file: File,
|
||||
}
|
||||
|
||||
impl TapT for FakeTap {
|
||||
fn new() -> Result<FakeTap> {
|
||||
Ok(FakeTap {
|
||||
tap_file: OpenOptions::new()
|
||||
.read(true)
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(TMP_FILE)
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn set_ip_addr(&self, _: net::Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_netmask(&self, _: net::Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_offload(&self, _: c_uint) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_vnet_hdr_size(&self, _: c_int) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_ifreq(&self) -> net_sys::ifreq {
|
||||
let ifreq: net_sys::ifreq = Default::default();
|
||||
ifreq
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FakeTap {
|
||||
fn drop(&mut self) {
|
||||
let _ = remove_file(TMP_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for FakeTap {
|
||||
fn read(&mut self, _: &mut [u8]) -> IoResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FakeTap {
|
||||
fn write(&mut self, _: &[u8]) -> IoResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for FakeTap {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tap_file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Pollable for FakeTap {
|
||||
fn pollable_fd(&self) -> RawFd {
|
||||
self.tap_file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -22,9 +22,11 @@ use io_jail::{self, Minijail};
|
|||
use kernel_cmdline;
|
||||
use kernel_loader;
|
||||
use kvm::*;
|
||||
use net_util::Tap;
|
||||
use qcow::{self, QcowFile};
|
||||
use sys_util::*;
|
||||
use sys_util;
|
||||
use vhost;
|
||||
use vm_control::VmRequest;
|
||||
|
||||
use Config;
|
||||
|
@ -352,10 +354,10 @@ fn setup_mmio_bus(cfg: &Config,
|
|||
if let Some(host_ip) = cfg.host_ip {
|
||||
if let Some(netmask) = cfg.netmask {
|
||||
let net_box: Box<devices::virtio::VirtioDevice> = if cfg.vhost_net {
|
||||
Box::new(devices::virtio::vhost::Net::new(host_ip, netmask, &mem)
|
||||
.map_err(Error::VhostNetDeviceNew)?)
|
||||
Box::new(devices::virtio::vhost::Net::<Tap, vhost::Net<Tap>>::new(host_ip, netmask, &mem)
|
||||
.map_err(|e| Error::VhostNetDeviceNew(e))?)
|
||||
} else {
|
||||
Box::new(devices::virtio::Net::new(host_ip, netmask)
|
||||
Box::new(devices::virtio::Net::<Tap>::new(host_ip, netmask)
|
||||
.map_err(|e| Error::NetDeviceNew(e))?)
|
||||
};
|
||||
|
||||
|
|
|
@ -12,9 +12,11 @@ extern crate kvm;
|
|||
extern crate x86_64;
|
||||
extern crate kernel_loader;
|
||||
extern crate byteorder;
|
||||
extern crate net_util;
|
||||
extern crate qcow;
|
||||
#[macro_use]
|
||||
extern crate sys_util;
|
||||
extern crate vhost;
|
||||
extern crate vm_control;
|
||||
extern crate data_model;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ pub mod net;
|
|||
mod vsock;
|
||||
|
||||
pub use net::Net;
|
||||
pub use net::NetT;
|
||||
pub use vsock::Vsock;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
|
@ -333,7 +334,8 @@ pub trait Vhost: AsRawFd + std::marker::Sized {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use net::tests::FakeNet;
|
||||
use net::fakes::FakeNet;
|
||||
use net_util::fakes::FakeTap;
|
||||
use std::result;
|
||||
use sys_util::{GuestAddress, GuestMemory, GuestMemoryError};
|
||||
|
||||
|
@ -352,9 +354,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_fake_vhost_net () -> FakeNet {
|
||||
fn create_fake_vhost_net() -> FakeNet<FakeTap> {
|
||||
let gm = create_guest_memory().unwrap();
|
||||
FakeNet::new(&gm).unwrap()
|
||||
FakeNet::<FakeTap>::new(&gm).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
use libc;
|
||||
use net_util;
|
||||
use net_util::TapT;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::marker::PhantomData;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use virtio_sys;
|
||||
|
@ -19,30 +20,37 @@ static DEVICE: &'static str = "/dev/vhost-net";
|
|||
///
|
||||
/// This provides a simple wrapper around a VHOST_NET file descriptor and
|
||||
/// methods that safely run ioctls on that file descriptor.
|
||||
pub struct Net {
|
||||
pub struct Net<T> {
|
||||
// fd must be dropped first, which will stop and tear down the
|
||||
// vhost-net worker before GuestMemory can potentially be unmapped.
|
||||
fd: File,
|
||||
mem: GuestMemory,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub trait NetT {
|
||||
pub trait NetT<T: TapT>: Vhost + AsRawFd + Send + Sized {
|
||||
/// Create a new NetT instance
|
||||
fn new(mem: &GuestMemory) -> Result<Self>;
|
||||
|
||||
/// 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<()>;
|
||||
fn set_backend(&self, queue_index: usize, fd: &T) -> Result<()>;
|
||||
}
|
||||
|
||||
impl Net {
|
||||
impl<T> NetT<T> for Net<T>
|
||||
where
|
||||
T: TapT,
|
||||
{
|
||||
/// Opens /dev/vhost-net and holds a file descriptor open for it.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `mem` - Guest memory mapping.
|
||||
pub fn new(mem: &GuestMemory) -> Result<Net> {
|
||||
Ok(Net {
|
||||
fn new(mem: &GuestMemory) -> Result<Net<T>> {
|
||||
Ok(Net::<T> {
|
||||
fd: OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
|
@ -50,12 +58,11 @@ impl Net {
|
|||
.open(DEVICE)
|
||||
.map_err(Error::VhostOpen)?,
|
||||
mem: mem.clone(),
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl NetT for Net {
|
||||
fn set_backend(&self, queue_index: usize, fd: &net_util::Tap) -> Result<()> {
|
||||
fn set_backend(&self, queue_index: usize, fd: &T) -> Result<()> {
|
||||
let vring_file = virtio_sys::vhost_vring_file {
|
||||
index: queue_index as u32,
|
||||
fd: fd.as_raw_fd(),
|
||||
|
@ -72,64 +79,66 @@ impl NetT for Net {
|
|||
}
|
||||
}
|
||||
|
||||
impl Vhost for Net {
|
||||
impl<T> Vhost for Net<T> {
|
||||
fn mem(&self) -> &GuestMemory {
|
||||
&self.mem
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Net {
|
||||
impl<T> AsRawFd for Net<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
pub mod fakes {
|
||||
use super::*;
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::remove_file;
|
||||
|
||||
const TMP_FILE: &str = "/tmp/crosvm_vhost_test_file";
|
||||
|
||||
pub struct FakeNet {
|
||||
pub struct FakeNet<T> {
|
||||
fd: File,
|
||||
mem: GuestMemory,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl FakeNet {
|
||||
pub fn new(mem: &GuestMemory) -> Result<FakeNet> {
|
||||
Ok(FakeNet {
|
||||
impl<T> Drop for FakeNet<T> {
|
||||
fn drop(&mut self) {
|
||||
let _ = remove_file(TMP_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> NetT<T> for FakeNet<T>
|
||||
where
|
||||
T: TapT,
|
||||
{
|
||||
fn new(mem: &GuestMemory) -> Result<FakeNet<T>> {
|
||||
Ok(FakeNet::<T> {
|
||||
fd: OpenOptions::new()
|
||||
.read(true)
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(TMP_FILE)
|
||||
.unwrap(),
|
||||
mem: mem.clone()
|
||||
mem: mem.clone(),
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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<()> {
|
||||
fn set_backend(&self, _queue_index: usize, _fd: &T) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Vhost for FakeNet {
|
||||
impl<T> Vhost for FakeNet<T> {
|
||||
fn mem(&self) -> &GuestMemory {
|
||||
&self.mem
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for FakeNet {
|
||||
impl<T> AsRawFd for FakeNet<T> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd.as_raw_fd()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue