1 // Copyright (C) 2019 Alibaba Cloud. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause 3 4 //! Virtio Vhost Backend Drivers 5 //! 6 //! Virtio devices use virtqueues to transport data efficiently. The first generation of virtqueue 7 //! is a set of three different single-producer, single-consumer ring structures designed to store 8 //! generic scatter-gather I/O. The virtio specification 1.1 introduces an alternative compact 9 //! virtqueue layout named "Packed Virtqueue", which is more friendly to memory cache system and 10 //! hardware implemented virtio devices. The packed virtqueue uses read-write memory, that means 11 //! the memory will be both read and written by both host and guest. The new Packed Virtqueue is 12 //! preferred for performance. 13 //! 14 //! Vhost is a mechanism to improve performance of Virtio devices by delegate data plane operations 15 //! to dedicated IO service processes. Only the configuration, I/O submission notification, and I/O 16 //! completion interruption are piped through the hypervisor. 17 //! It uses the same virtqueue layout as Virtio to allow Vhost devices to be mapped directly to 18 //! Virtio devices. This allows a Vhost device to be accessed directly by a guest OS inside a 19 //! hypervisor process with an existing Virtio (PCI) driver. 20 //! 21 //! The initial vhost implementation is a part of the Linux kernel and uses ioctl interface to 22 //! communicate with userspace applications. Dedicated kernel worker threads are created to handle 23 //! IO requests from the guest. 24 //! 25 //! Later Vhost-user protocol is introduced to complement the ioctl interface used to control the 26 //! vhost implementation in the Linux kernel. It implements the control plane needed to establish 27 //! virtqueues sharing with a user space process on the same host. It uses communication over a 28 //! Unix domain socket to share file descriptors in the ancillary data of the message. 29 //! The protocol defines 2 sides of the communication, master and slave. Master is the application 30 //! that shares its virtqueues. Slave is the consumer of the virtqueues. Master and slave can be 31 //! either a client (i.e. connecting) or server (listening) in the socket communication. 32 33 #![deny(missing_docs)] 34 35 #[cfg_attr(feature = "vhost-user", macro_use)] 36 extern crate bitflags; 37 #[cfg_attr(feature = "vhost-kern", macro_use)] 38 extern crate vmm_sys_util; 39 40 mod backend; 41 pub use backend::*; 42 43 #[cfg(feature = "vhost-net")] 44 pub mod net; 45 #[cfg(feature = "vhost-vdpa")] 46 pub mod vdpa; 47 #[cfg(feature = "vhost-kern")] 48 pub mod vhost_kern; 49 #[cfg(feature = "vhost-user")] 50 pub mod vhost_user; 51 #[cfg(feature = "vhost-vsock")] 52 pub mod vsock; 53 54 /// Error codes for vhost operations 55 #[derive(Debug)] 56 pub enum Error { 57 /// Invalid operations. 58 InvalidOperation, 59 /// Invalid guest memory. 60 InvalidGuestMemory, 61 /// Invalid guest memory region. 62 InvalidGuestMemoryRegion, 63 /// Invalid IOTLB message. 64 InvalidIotlbMsg, 65 /// Invalid queue. 66 InvalidQueue, 67 /// Invalid descriptor table address. 68 DescriptorTableAddress, 69 /// Invalid used address. 70 UsedAddress, 71 /// Invalid available address. 72 AvailAddress, 73 /// Invalid log address. 74 LogAddress, 75 #[cfg(feature = "vhost-kern")] 76 /// Error opening the vhost backend driver. 77 VhostOpen(std::io::Error), 78 #[cfg(feature = "vhost-kern")] 79 /// Error while running ioctl. 80 IoctlError(std::io::Error), 81 /// Error from IO subsystem. 82 IOError(std::io::Error), 83 #[cfg(feature = "vhost-user")] 84 /// Error from the vhost-user subsystem. 85 VhostUserProtocol(vhost_user::Error), 86 } 87 88 impl std::fmt::Display for Error { fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result89 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 90 match self { 91 Error::InvalidOperation => write!(f, "invalid vhost operations"), 92 Error::InvalidGuestMemory => write!(f, "invalid guest memory object"), 93 Error::InvalidGuestMemoryRegion => write!(f, "invalid guest memory region"), 94 Error::InvalidIotlbMsg => write!(f, "invalid IOTLB message"), 95 Error::InvalidQueue => write!(f, "invalid virtqueue"), 96 Error::DescriptorTableAddress => { 97 write!(f, "invalid virtqueue descriptor table address") 98 } 99 Error::UsedAddress => write!(f, "invalid virtqueue used table address"), 100 Error::AvailAddress => write!(f, "invalid virtqueue available table address"), 101 Error::LogAddress => write!(f, "invalid virtqueue log address"), 102 Error::IOError(e) => write!(f, "IO error: {}", e), 103 #[cfg(feature = "vhost-kern")] 104 Error::VhostOpen(e) => write!(f, "failure in opening vhost file: {}", e), 105 #[cfg(feature = "vhost-kern")] 106 Error::IoctlError(e) => write!(f, "failure in vhost ioctl: {}", e), 107 #[cfg(feature = "vhost-user")] 108 Error::VhostUserProtocol(e) => write!(f, "vhost-user: {}", e), 109 } 110 } 111 } 112 113 impl std::error::Error for Error {} 114 115 #[cfg(feature = "vhost-user")] 116 impl std::convert::From<vhost_user::Error> for Error { from(err: vhost_user::Error) -> Self117 fn from(err: vhost_user::Error) -> Self { 118 Error::VhostUserProtocol(err) 119 } 120 } 121 122 /// Result of vhost operations 123 pub type Result<T> = std::result::Result<T, Error>; 124 125 #[cfg(test)] 126 mod tests { 127 use super::*; 128 129 #[test] test_error()130 fn test_error() { 131 assert_eq!( 132 format!("{}", Error::AvailAddress), 133 "invalid virtqueue available table address" 134 ); 135 assert_eq!( 136 format!("{}", Error::InvalidOperation), 137 "invalid vhost operations" 138 ); 139 assert_eq!( 140 format!("{}", Error::InvalidGuestMemory), 141 "invalid guest memory object" 142 ); 143 assert_eq!( 144 format!("{}", Error::InvalidGuestMemoryRegion), 145 "invalid guest memory region" 146 ); 147 assert_eq!( 148 format!("{}", Error::InvalidIotlbMsg), 149 "invalid IOTLB message" 150 ); 151 assert_eq!(format!("{}", Error::InvalidQueue), "invalid virtqueue"); 152 assert_eq!( 153 format!("{}", Error::DescriptorTableAddress), 154 "invalid virtqueue descriptor table address" 155 ); 156 assert_eq!( 157 format!("{}", Error::UsedAddress), 158 "invalid virtqueue used table address" 159 ); 160 assert_eq!( 161 format!("{}", Error::LogAddress), 162 "invalid virtqueue log address" 163 ); 164 165 assert_eq!(format!("{:?}", Error::AvailAddress), "AvailAddress"); 166 } 167 168 #[cfg(feature = "vhost-user")] 169 #[test] test_convert_from_vhost_user_error()170 fn test_convert_from_vhost_user_error() { 171 let e: Error = vhost_user::Error::OversizedMsg.into(); 172 173 assert_eq!(format!("{}", e), "vhost-user: oversized message"); 174 } 175 } 176