blob: 89f390ca791acbc2b5464e7b57831b5c6f8a9769 [file] [log] [blame]
// Copyright (C) 2021 Alibaba Cloud Computing. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause
//! Kernel-based vhost-net backend
use std::fs::{File, OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};
use vm_memory::GuestAddressSpace;
use vmm_sys_util::ioctl::ioctl_with_ref;
use super::vhost_binding::*;
use super::{ioctl_result, Error, Result, VhostKernBackend};
use crate::net::*;
const VHOST_NET_PATH: &str = "/dev/vhost-net";
/// Handle for running VHOST_NET ioctls
pub struct Net<AS: GuestAddressSpace> {
fd: File,
mem: AS,
}
impl<AS: GuestAddressSpace> Net<AS> {
/// Open a handle to a new VHOST-NET instance.
pub fn new(mem: AS) -> Result<Self> {
Ok(Net {
fd: OpenOptions::new()
.read(true)
.write(true)
.custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
.open(VHOST_NET_PATH)
.map_err(Error::VhostOpen)?,
mem,
})
}
}
impl<AS: GuestAddressSpace> VhostNet for Net<AS> {
fn set_backend(&self, queue_index: usize, fd: Option<&File>) -> Result<()> {
let vring_file = vhost_vring_file {
index: queue_index as u32,
fd: fd.map_or(-1, |v| v.as_raw_fd()),
};
// SAFETY: Safe because the vhost-net fd is valid and we check the return value
let ret = unsafe { ioctl_with_ref(self, VHOST_NET_SET_BACKEND(), &vring_file) };
ioctl_result(ret, ())
}
}
impl<AS: GuestAddressSpace> VhostKernBackend for Net<AS> {
type AS = AS;
fn mem(&self) -> &Self::AS {
&self.mem
}
}
impl<AS: GuestAddressSpace> AsRawFd for Net<AS> {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
#[cfg(test)]
mod tests {
use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
use vmm_sys_util::eventfd::EventFd;
use super::*;
use crate::{
VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData,
};
use serial_test::serial;
#[test]
#[serial]
fn test_net_new_device() {
let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
let net = Net::new(&m).unwrap();
assert!(net.as_raw_fd() >= 0);
assert!(net.mem().find_region(GuestAddress(0x100)).is_some());
assert!(net.mem().find_region(GuestAddress(0x10_0000)).is_none());
}
#[test]
#[serial]
fn test_net_is_valid() {
let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
let net = Net::new(&m).unwrap();
let mut config = VringConfigData {
queue_max_size: 32,
queue_size: 32,
flags: 0,
desc_table_addr: 0x1000,
used_ring_addr: 0x2000,
avail_ring_addr: 0x3000,
log_addr: None,
};
assert!(net.is_valid(&config));
config.queue_size = 0;
assert!(!net.is_valid(&config));
config.queue_size = 31;
assert!(!net.is_valid(&config));
config.queue_size = 33;
assert!(!net.is_valid(&config));
}
#[test]
#[serial]
fn test_net_ioctls() {
let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
let net = Net::new(&m).unwrap();
let backend = OpenOptions::new()
.read(true)
.write(true)
.open("/dev/null")
.unwrap();
let features = net.get_features().unwrap();
net.set_features(features).unwrap();
net.set_owner().unwrap();
net.set_mem_table(&[]).unwrap_err();
let region = VhostUserMemoryRegionInfo::new(
0x0,
0x10_0000,
m.get_host_address(GuestAddress(0x0)).unwrap() as u64,
0,
-1,
);
net.set_mem_table(&[region]).unwrap();
net.set_log_base(
0x4000,
Some(VhostUserDirtyLogRegion {
mmap_size: 0x1000,
mmap_offset: 0x10,
mmap_handle: 1,
}),
)
.unwrap_err();
net.set_log_base(0x4000, None).unwrap();
let eventfd = EventFd::new(0).unwrap();
net.set_log_fd(eventfd.as_raw_fd()).unwrap();
net.set_vring_num(0, 32).unwrap();
let config = VringConfigData {
queue_max_size: 32,
queue_size: 32,
flags: 0,
desc_table_addr: 0x1000,
used_ring_addr: 0x2000,
avail_ring_addr: 0x3000,
log_addr: None,
};
net.set_vring_addr(0, &config).unwrap();
net.set_vring_base(0, 1).unwrap();
net.set_vring_call(0, &eventfd).unwrap();
net.set_vring_kick(0, &eventfd).unwrap();
net.set_vring_err(0, &eventfd).unwrap();
assert_eq!(net.get_vring_base(0).unwrap(), 1);
net.set_backend(0, Some(&backend)).unwrap_err();
net.set_backend(0, None).unwrap();
}
}