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