diff options
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | src/backend.rs | 135 | ||||
-rw-r--r-- | src/lib.rs | 110 | ||||
-rw-r--r-- | src/vsock.rs | 30 |
4 files changed, 276 insertions, 5 deletions
@@ -5,4 +5,10 @@ authors = ["Liu Jiang <gerry@linux.alibaba.com>"] repository = "https://github.com/rust-vmm/vhost" license = "Apache-2.0 or BSD-3-Clause" +[features] +default = [] +vhost-vsock = [] + [dependencies] +vmm-sys-util = ">=0.3.1" +vm-memory = { version = "0.2.0", optional = true } diff --git a/src/backend.rs b/src/backend.rs new file mode 100644 index 0000000..e5dc4b7 --- /dev/null +++ b/src/backend.rs @@ -0,0 +1,135 @@ +// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +// +// Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Portions 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-BSD-Google file. + +//! Common traits and structs for vhost-kern and vhost-user backend drivers. + +use super::Result; +use std::os::unix::io::RawFd; +use vmm_sys_util::eventfd::EventFd; + +/// Maximum number of memory regions supported. +pub const VHOST_MAX_MEMORY_REGIONS: usize = 255; + +/// Vring/virtque configuration data. +pub struct VringConfigData { + /// Maximum queue size supported by the driver. + pub queue_max_size: u16, + /// Actual queue size negotiated by the driver. + pub queue_size: u16, + /// Bitmask of vring flags. + pub flags: u32, + /// Descriptor table address. + pub desc_table_addr: u64, + /// Used ring buffer address. + pub used_ring_addr: u64, + /// Available ring buffer address. + pub avail_ring_addr: u64, + /// Optional address for logging. + pub log_addr: Option<u64>, +} + +/// Memory region configuration data. +#[derive(Default, Clone, Copy)] +pub struct VhostUserMemoryRegionInfo { + /// Guest physical address of the memory region. + pub guest_phys_addr: u64, + /// Size of the memory region. + pub memory_size: u64, + /// Virtual address in the current process. + pub userspace_addr: u64, + /// Optional offset where region starts in the mapped memory. + pub mmap_offset: u64, + /// Optional file diescriptor for mmap + pub mmap_handle: RawFd, +} + +/// An interface for setting up vhost-based backend drivers. +/// +/// Vhost devices are subset of virtio devices, which improve virtio device's performance by +/// delegating data plane operations to dedicated IO service processes. Vhost devices use the +/// same virtqueue layout as virtio devices to allow vhost devices to be mapped directly to +/// virtio devices. +/// The purpose of vhost is to implement a subset of a virtio device's functionality outside the +/// VMM process. Typically fast paths for IO operations are delegated to the dedicated IO service +/// processes, and slow path for device configuration are still handled by the VMM process. It may +/// also be used to control access permissions of virtio backend devices. +pub trait VhostBackend: std::marker::Sized { + /// Get a bitmask of supported virtio/vhost features. + fn get_features(&mut self) -> Result<u64>; + + /// Inform the vhost subsystem which features to enable. + /// This should be a subset of supported features from get_features(). + /// + /// # Arguments + /// * `features` - Bitmask of features to set. + fn set_features(&mut self, features: u64) -> Result<()>; + + /// Set the current process as the owner of the vhost backend. + /// This must be run before any other vhost commands. + fn set_owner(&mut self) -> Result<()>; + + /// Used to be sent to request disabling all rings + /// This is no longer used. + fn reset_owner(&mut self) -> Result<()>; + + /// Set the guest memory mappings for vhost to use. + fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegionInfo]) -> Result<()>; + + /// Set base address for page modification logging. + fn set_log_base(&mut self, base: u64, fd: Option<RawFd>) -> Result<()>; + + /// Specify an eventfd file descriptor to signal on log write. + fn set_log_fd(&mut self, fd: RawFd) -> Result<()>; + + /// Set the number of descriptors in the vring. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to set descriptor count for. + /// * `num` - Number of descriptors in the queue. + fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>; + + /// Set the addresses for a given vring. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to set addresses for. + /// * `config_data` - Configuration data for a vring. + fn set_vring_addr(&mut self, queue_index: usize, config_data: &VringConfigData) -> Result<()>; + + /// Set the first index to look for available descriptors. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to modify. + /// * `num` - Index where available descriptors start. + fn set_vring_base(&mut self, queue_index: usize, base: u16) -> Result<()>; + + /// Get the available vring base offset. + fn get_vring_base(&mut self, queue_index: usize) -> Result<u32>; + + /// Set the eventfd to trigger when buffers have been used by the host. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to modify. + /// * `fd` - EventFd to trigger. + fn set_vring_call(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>; + + /// Set the eventfd that will be signaled by the guest when buffers are + /// available for the host to process. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to modify. + /// * `fd` - EventFd that will be signaled from guest. + fn set_vring_kick(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>; + + /// Set the eventfd that will be signaled by the guest when error happens. + /// + /// # Arguments + /// * `queue_index` - Index of the queue to modify. + /// * `fd` - EventFd that will be signaled from guest. + fn set_vring_err(&mut self, queue_index: usize, fd: &EventFd) -> Result<()>; +} @@ -1,7 +1,107 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +//! Virtio Vhost Backend Drivers +//! +//! Virtio devices use virtqueues to transport data efficiently. The first generation of virtqueue +//! is a set of three different single-producer, single-consumer ring structures designed to store +//! generic scatter-gather I/O. The virtio specification 1.1 introduces an alternative compact +//! virtqueue layout named "Packed Virtqueue", which is more friendly to memory cache system and +//! hardware implemented virtio devices. The packed virtqueue uses read-write memory, that means +//! the memory will be both read and written by both host and guest. The new Packed Virtqueue is +//! preferred for performance. +//! +//! Vhost is a mechanism to improve performance of Virtio devices by delegate data plane operations +//! to dedicated IO service processes. Only the configuration, I/O submission notification, and I/O +//! completion interruption are piped through the hypervisor. +//! It uses the same virtqueue layout as Virtio to allow Vhost devices to be mapped directly to +//! Virtio devices. This allows a Vhost device to be accessed directly by a guest OS inside a +//! hypervisor process with an existing Virtio (PCI) driver. +//! +//! The initial vhost implementation is a part of the Linux kernel and uses ioctl interface to +//! communicate with userspace applications. Dedicated kernel worker threads are created to handle +//! IO requests from the guest. +//! +//! Later Vhost-user protocol is introduced to complement the ioctl interface used to control the +//! vhost implementation in the Linux kernel. It implements the control plane needed to establish +//! virtqueues sharing with a user space process on the same host. It uses communication over a +//! Unix domain socket to share file descriptors in the ancillary data of the message. +//! The protocol defines 2 sides of the communication, master and slave. Master is the application +//! that shares its virtqueues. Slave is the consumer of the virtqueues. Master and slave can be +//! either a client (i.e. connecting) or server (listening) in the socket communication. + +#![deny(missing_docs)] + +#[cfg(feature = "vhost-kern")] +extern crate vm_memory; +extern crate vmm_sys_util; + +mod backend; +pub use backend::*; + +#[cfg(feature = "vhost-vsock")] +pub mod vsock; + +/// Error codes for vhost operations +#[derive(Debug)] +pub enum Error { + /// Invalid operations. + InvalidOperation, + /// Invalid guest memory. + InvalidGuestMemory, + /// Invalid guest memory region. + InvalidGuestMemoryRegion, + /// Invalid queue. + InvalidQueue, + /// Invalid descriptor table address. + DescriptorTableAddress, + /// Invalid used address. + UsedAddress, + /// Invalid available address. + AvailAddress, + /// Invalid log address. + LogAddress, + #[cfg(feature = "vhost-kern")] + /// Error opening the vhost backend driver. + VhostOpen(std::io::Error), + #[cfg(feature = "vhost-kern")] + /// Error while running ioctl. + IoctlError(std::io::Error), + /// Error from IO subsystem. + IOError(std::io::Error), + #[cfg(any(feature = "vhost-user-master", feature = "vhost-user-slave"))] + /// Error from the vhost-user subsystem. + VhostUserProtocol(crate::vhost_user::Error), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::InvalidOperation => write!(f, "invalid vhost operations"), + Error::InvalidGuestMemory => write!(f, "invalid guest memory object"), + Error::InvalidGuestMemoryRegion => write!(f, "invalid guest memory region"), + Error::InvalidQueue => write!(f, "invalid virtque"), + Error::DescriptorTableAddress => write!(f, "invalid virtque descriptor talbe address"), + Error::UsedAddress => write!(f, "invalid virtque used talbe address"), + Error::AvailAddress => write!(f, "invalid virtque available talbe address"), + Error::LogAddress => write!(f, "invalid virtque log address"), + Error::IOError(e) => write!(f, "IO error: {}", e), + #[cfg(feature = "vhost-kern")] + Error::VhostOpen(e) => write!(f, "failure in opening vhost file: {}", e), + #[cfg(feature = "vhost-kern")] + Error::IoctlError(e) => write!(f, "failure in vhost ioctl: {}", e), + #[cfg(any(feature = "vhost-user-master", feature = "vhost-user-slave"))] + Error::VhostUserProtocol(e) => write!(f, "vhost-user error: {}", e), + } + } +} + +#[cfg(any(feature = "vhost-user-master", feature = "vhost-user-slave"))] +impl std::convert::From<crate::vhost_user::Error> for Error { + fn from(err: crate::vhost_user::Error) -> Self { + Error::VhostUserProtocol(err) } } + +/// Result of vhost operations +pub type Result<T> = std::result::Result<T, Error>; diff --git a/src/vsock.rs b/src/vsock.rs new file mode 100644 index 0000000..4fb75f5 --- /dev/null +++ b/src/vsock.rs @@ -0,0 +1,30 @@ +// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +// +// Portions Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Portions 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-BSD-Google file. + +//! Trait to control vhost-vsock backend drivers. + +use crate::backend::VhostBackend; +use crate::Result; + +/// Trait to control vhost-vsock backend drivers. +pub trait VhostVsock: VhostBackend { + /// Set the CID for the guest. + /// This number is used for routing all data destined for running in the guest. + /// Each guest on a hypervisor must have an unique CID. + /// + /// # Arguments + /// * `cid` - CID to assign to the guest + fn set_guest_cid(&mut self, cid: u64) -> Result<()>; + + /// Tell the VHOST driver to start performing data transfer. + fn start(&mut self) -> Result<()>; + + /// Tell the VHOST driver to stop performing data transfer. + fn stop(&mut self) -> Result<()>; +} |