diff options
author | Chirantan Ekbote <chirantan@chromium.org> | 2017-08-23 15:02:37 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-08-28 21:15:36 -0700 |
commit | 195457e48494e5a3082a35e7b3d570dc7c292de1 (patch) | |
tree | ace6461ca76ecaf781df3ade6dae82c059777cdb /vhost/src | |
parent | 56158c873a446d8026b32a719616be2911865335 (diff) | |
download | crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.tar crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.tar.gz crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.tar.bz2 crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.tar.lz crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.tar.xz crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.tar.zst crosvm-195457e48494e5a3082a35e7b3d570dc7c292de1.zip |
Refactor VhostNet into separate vhost and net pieces
A large portion of the VhostNet implementation is common to all vhost devices. Create a new Vhost trait that encapsulates this behavior and split the network specific bits into a new Net type and implement the Vhost trait for it. BUG=chromium:708267 TEST=build and run with a VHOST_NET enabled kernel and see that everything still works fine Change-Id: Ia6b7591f9428c1fba1e13b11791fe40e1bd3942b Signed-off-by: Chirantan Ekbote <chirantan@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/630060 Reviewed-by: Jason Clinton <jclinton@chromium.org> Reviewed-by: Stephen Barber <smbarber@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'vhost/src')
-rw-r--r-- | vhost/src/lib.rs | 159 | ||||
-rw-r--r-- | vhost/src/net.rs | 88 |
2 files changed, 141 insertions, 106 deletions
diff --git a/vhost/src/lib.rs b/vhost/src/lib.rs index 912dfcb..ebca53a 100644 --- a/vhost/src/lib.rs +++ b/vhost/src/lib.rs @@ -7,9 +7,11 @@ extern crate net_util; extern crate sys_util; extern crate virtio_sys; -use std::fs::File; +mod net; +pub use net::Net; + use std::mem; -use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::os::unix::io::AsRawFd; use std::ptr::null; use sys_util::{EventFd, GuestAddress, GuestMemory, GuestMemoryError, Error as SysError}; @@ -17,7 +19,7 @@ use sys_util::{ioctl, ioctl_with_ref, ioctl_with_mut_ref, ioctl_with_ptr}; #[derive(Debug)] pub enum Error { - /// Error opening /dev/host-net. + /// Error opening vhost device. VhostOpen(SysError), /// Error while running ioctl. IoctlError(SysError), @@ -38,45 +40,20 @@ fn ioctl_result<T>() -> Result<T> { Err(Error::IoctlError(SysError::last())) } -/// Handle to run VHOST_NET ioctls. -/// -/// This provides a simple wrapper around a VHOST_NET file descriptor and -/// methods that safely run ioctls on that file descriptor. -pub struct VhostNet { - // vhost_net must be dropped first, which will stop and tear down the - // vhost_net worker before GuestMemory can potentially be unmapped. - vhost_net: File, - mem: GuestMemory, -} - -impl VhostNet { - /// Opens /dev/vhost-net and holds a file descriptor open for it. - /// - /// # Arguments - /// * `mem` - Guest memory mapping. - pub fn new(mem: &GuestMemory) -> Result<VhostNet> { - // Open calls are safe because we give a constant nul-terminated - // string and verify the result. - let fd = unsafe { - libc::open(b"/dev/vhost-net\0".as_ptr() as *const i8, - libc::O_RDONLY | libc::O_WRONLY | libc::O_NONBLOCK | libc::O_CLOEXEC) - }; - if fd < 0 { - return Err(Error::VhostOpen(SysError::last())); - } - Ok(VhostNet { - // There are no other users of this fd, so this is safe. - vhost_net: unsafe { File::from_raw_fd(fd) }, - mem: mem.clone(), - }) - } +/// An interface for setting up vhost-based virtio devices. Vhost-based devices are different +/// from regular virtio devices because the host kernel takes care of handling all the data +/// transfer. The device itself only needs to deal with setting up the kernel driver and +/// managing the control channel. +pub trait Vhost: AsRawFd + std::marker::Sized { + /// Get the guest memory mapping. + fn mem(&self) -> &GuestMemory; /// Set the current process as the owner of this file descriptor. /// This must be run before any other vhost ioctls. - pub fn set_owner(&self) -> Result<()> { + fn set_owner(&self) -> Result<()> { // This ioctl is called on a valid vhost_net fd and has its // return value checked. - let ret = unsafe { ioctl(&self.vhost_net, virtio_sys::VHOST_SET_OWNER()) }; + let ret = unsafe { ioctl(self, virtio_sys::VHOST_SET_OWNER()) }; if ret < 0 { return ioctl_result(); } @@ -84,12 +61,12 @@ impl VhostNet { } /// Get a bitmask of supported virtio/vhost features. - pub fn get_features(&self) -> Result<u64> { + fn get_features(&self) -> Result<u64> { let mut avail_features: u64 = 0; // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = unsafe { - ioctl_with_mut_ref(&self.vhost_net, + ioctl_with_mut_ref(self, virtio_sys::VHOST_GET_FEATURES(), &mut avail_features) }; @@ -99,16 +76,16 @@ impl VhostNet { Ok(avail_features) } - /// Inform VHOST_NET which features to enable. This should be a subset of + /// Inform the vhost subsystem which features to enable. This should be a subset of /// supported features from VHOST_GET_FEATURES. /// /// # Arguments /// * `features` - Bitmask of features to set. - pub fn set_features(&self, features: u64) -> Result<()> { + fn set_features(&self, features: u64) -> Result<()> { // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = - unsafe { ioctl_with_ref(&self.vhost_net, virtio_sys::VHOST_SET_FEATURES(), &features) }; + unsafe { ioctl_with_ref(self, virtio_sys::VHOST_SET_FEATURES(), &features) }; if ret < 0 { return ioctl_result(); } @@ -116,8 +93,8 @@ impl VhostNet { } /// Set the guest memory mappings for vhost to use. - pub fn set_mem_table(&self) -> Result<()> { - let num_regions = self.mem.num_regions(); + fn set_mem_table(&self) -> Result<()> { + let num_regions = self.mem().num_regions(); let vec_size_bytes = mem::size_of::<virtio_sys::vhost_memory>() + (num_regions * mem::size_of::<virtio_sys::vhost_memory_region>()); let mut bytes: Vec<u8> = vec![0; vec_size_bytes]; @@ -130,7 +107,7 @@ impl VhostNet { // we correctly specify the size to match the amount of backing memory. let vhost_regions = unsafe { vhost_memory.regions.as_mut_slice(num_regions as usize) }; - let _ = self.mem + let _ = self.mem() .with_regions_mut::<_, ()>(|index, guest_addr, size, host_addr| { vhost_regions[index] = virtio_sys::vhost_memory_region { guest_phys_addr: guest_addr.offset() as u64, @@ -146,7 +123,7 @@ impl VhostNet { // of this function. The kernel will make its own copy of the memory // tables. As always, check the return value. let ret = unsafe { - ioctl_with_ptr(&self.vhost_net, + ioctl_with_ptr(self, virtio_sys::VHOST_SET_MEM_TABLE(), bytes.as_ptr()) }; @@ -161,7 +138,7 @@ impl VhostNet { /// # Arguments /// * `queue_index` - Index of the queue to set descriptor count for. /// * `num` - Number of descriptors in the queue. - pub fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> { + fn set_vring_num(&self, queue_index: usize, num: u16) -> Result<()> { let vring_state = virtio_sys::vhost_vring_state { index: queue_index as u32, num: num as u32, @@ -170,7 +147,7 @@ impl VhostNet { // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = unsafe { - ioctl_with_ref(&self.vhost_net, + ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_NUM(), &vring_state) }; @@ -195,15 +172,15 @@ impl VhostNet { false } else if desc_addr .checked_add(desc_table_size) - .map_or(true, |v| !self.mem.address_in_range(v)) { + .map_or(true, |v| !self.mem().address_in_range(v)) { false } else if avail_addr .checked_add(avail_ring_size) - .map_or(true, |v| !self.mem.address_in_range(v)) { + .map_or(true, |v| !self.mem().address_in_range(v)) { false } else if used_addr .checked_add(used_ring_size) - .map_or(true, |v| !self.mem.address_in_range(v)) { + .map_or(true, |v| !self.mem().address_in_range(v)) { false } else { true @@ -222,35 +199,35 @@ impl VhostNet { /// * `used_addr` - Used ring buffer address. /// * `avail_addr` - Available ring buffer address. /// * `log_addr` - Optional address for logging. - pub fn set_vring_addr(&self, - queue_max_size: u16, - queue_size: u16, - queue_index: usize, - flags: u32, - desc_addr: GuestAddress, - used_addr: GuestAddress, - avail_addr: GuestAddress, - log_addr: Option<GuestAddress>) - -> Result<()> { + fn set_vring_addr(&self, + queue_max_size: u16, + queue_size: u16, + queue_index: usize, + flags: u32, + desc_addr: GuestAddress, + used_addr: GuestAddress, + avail_addr: GuestAddress, + log_addr: Option<GuestAddress>) + -> Result<()> { // TODO(smbarber): Refactor out virtio from crosvm so we can // validate a Queue struct directly. if !self.is_valid(queue_max_size, queue_size, desc_addr, used_addr, avail_addr) { return Err(Error::InvalidQueue); } - let desc_addr = self.mem + let desc_addr = self.mem() .get_host_address(desc_addr) .map_err(Error::DescriptorTableAddress)?; - let used_addr = self.mem + let used_addr = self.mem() .get_host_address(used_addr) .map_err(Error::UsedAddress)?; - let avail_addr = self.mem + let avail_addr = self.mem() .get_host_address(avail_addr) .map_err(Error::AvailAddress)?; let log_addr = match log_addr { None => null(), Some(a) => { - self.mem + self.mem() .get_host_address(a) .map_err(Error::LogAddress)? }, @@ -268,7 +245,7 @@ impl VhostNet { // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = unsafe { - ioctl_with_ref(&self.vhost_net, + ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_ADDR(), &vring_addr) }; @@ -283,7 +260,7 @@ impl VhostNet { /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `num` - Index where available descriptors start. - pub fn set_vring_base(&self, queue_index: usize, num: u16) -> Result<()> { + fn set_vring_base(&self, queue_index: usize, num: u16) -> Result<()> { let vring_state = virtio_sys::vhost_vring_state { index: queue_index as u32, num: num as u32, @@ -292,7 +269,7 @@ impl VhostNet { // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = unsafe { - ioctl_with_ref(&self.vhost_net, + ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_BASE(), &vring_state) }; @@ -307,7 +284,7 @@ impl VhostNet { /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd to trigger. - pub fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> { + fn set_vring_call(&self, queue_index: usize, fd: &EventFd) -> Result<()> { let vring_file = virtio_sys::vhost_vring_file { index: queue_index as u32, fd: fd.as_raw_fd(), @@ -316,7 +293,7 @@ impl VhostNet { // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = unsafe { - ioctl_with_ref(&self.vhost_net, + ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_CALL(), &vring_file) }; @@ -332,7 +309,7 @@ impl VhostNet { /// # Arguments /// * `queue_index` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. - pub fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> { + fn set_vring_kick(&self, queue_index: usize, fd: &EventFd) -> Result<()> { let vring_file = virtio_sys::vhost_vring_file { index: queue_index as u32, fd: fd.as_raw_fd(), @@ -341,7 +318,7 @@ impl VhostNet { // This ioctl is called on a valid vhost_net fd and has its // return value checked. let ret = unsafe { - ioctl_with_ref(&self.vhost_net, + ioctl_with_ref(self, virtio_sys::VHOST_SET_VRING_KICK(), &vring_file) }; @@ -351,36 +328,6 @@ impl VhostNet { Ok(()) } - /// 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 net_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(), - }; - - // This ioctl is called on a valid vhost_net fd and has its - // return value checked. - let ret = unsafe { - ioctl_with_ref(&self.vhost_net, - virtio_sys::VHOST_NET_SET_BACKEND(), - &vring_file) - }; - if ret < 0 { - return ioctl_result(); - } - Ok(()) - } -} - -impl AsRawFd for VhostNet { - fn as_raw_fd(&self) -> RawFd { - self.vhost_net.as_raw_fd() - } } #[cfg(test)] @@ -399,27 +346,27 @@ mod tests { #[test] fn open_vhostnet() { let gm = create_guest_memory().unwrap(); - VhostNet::new(&gm).unwrap(); + Net::new(&gm).unwrap(); } #[test] fn set_owner() { let gm = create_guest_memory().unwrap(); - let vhost_net = VhostNet::new(&gm).unwrap(); + let vhost_net = Net::new(&gm).unwrap(); vhost_net.set_owner().unwrap(); } #[test] fn get_features() { let gm = create_guest_memory().unwrap(); - let vhost_net = VhostNet::new(&gm).unwrap(); + let vhost_net = Net::new(&gm).unwrap(); vhost_net.get_features().unwrap(); } #[test] fn set_features() { let gm = create_guest_memory().unwrap(); - let vhost_net = VhostNet::new(&gm).unwrap(); + let vhost_net = Net::new(&gm).unwrap(); vhost_net.set_features(0).unwrap(); } } diff --git a/vhost/src/net.rs b/vhost/src/net.rs new file mode 100644 index 0000000..b20bbfc --- /dev/null +++ b/vhost/src/net.rs @@ -0,0 +1,88 @@ +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use libc; +use net_util; +use std::ffi::CString; +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use virtio_sys; + +use sys_util::{ioctl_with_ref, Error as SysError, GuestMemory}; + +use super::{ioctl_result, Error, Result, Vhost}; + +static DEVICE: &'static str = "/dev/vhost-net"; + +/// Handle to run VHOST_NET ioctls. +/// +/// This provides a simple wrapper around a VHOST_NET file descriptor and +/// methods that safely run ioctls on that file descriptor. +pub struct Net { + // 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, +} + +impl Net { + /// Opens /dev/vhost-net and holds a file descriptor open for it. + /// + /// # Arguments + /// * `mem` - Guest memory mapping. + pub fn new(mem: &GuestMemory) -> Result<Net> { + // Open calls are safe because we give a constant nul-terminated + // string and verify the result. The CString unwrap is safe because + // DEVICE does not have any embedded '\0' characters. + let fd = unsafe { + libc::open(CString::new(DEVICE).unwrap().as_ptr(), + libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC) + }; + if fd < 0 { + return Err(Error::VhostOpen(SysError::last())); + } + Ok(Net { + // There are no other users of this fd, so this is safe. + fd: unsafe { File::from_raw_fd(fd) }, + 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<()> { + let vring_file = virtio_sys::vhost_vring_file { + index: queue_index as u32, + fd: fd.as_raw_fd(), + }; + + // 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) + }; + if ret < 0 { + return ioctl_result(); + } + Ok(()) + } +} + +impl Vhost for Net { + fn mem(&self) -> &GuestMemory { + &self.mem + } +} + +impl AsRawFd for Net { + fn as_raw_fd(&self) -> RawFd { + self.fd.as_raw_fd() + } +} |