• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! VirtioDevice implementation for the VMM side of a vhost-user connection.
6 
7 use std::cell::RefCell;
8 
9 use anyhow::Context;
10 use base::error;
11 use base::Event;
12 use base::RawDescriptor;
13 use base::WorkerThread;
14 use vm_memory::GuestMemory;
15 use vmm_vhost::message::VhostUserProtocolFeatures;
16 use vmm_vhost::message::VhostUserVirtioFeatures;
17 
18 use crate::virtio::copy_config;
19 use crate::virtio::vhost::user::vmm::Connection;
20 use crate::virtio::vhost::user::vmm::Result;
21 use crate::virtio::vhost::user::vmm::VhostUserHandler;
22 use crate::virtio::DeviceType;
23 use crate::virtio::Interrupt;
24 use crate::virtio::Queue;
25 use crate::virtio::SharedMemoryMapper;
26 use crate::virtio::SharedMemoryRegion;
27 use crate::virtio::VirtioDevice;
28 use crate::Suspendable;
29 
30 pub struct VhostUserVirtioDevice {
31     device_type: DeviceType,
32     worker_thread: Option<WorkerThread<()>>,
33     handler: RefCell<VhostUserHandler>,
34     queue_sizes: Vec<u16>,
35     cfg: Option<Vec<u8>>,
36     expose_shmem_descriptors_with_viommu: bool,
37 }
38 
39 /// Method for determining the number of queues and the size of each queue.
40 ///
41 /// For device types that have a fixed number of queues defined in the specification, use
42 /// `QueueSizes::Fixed` to specify a vector containing queue sizes with one element per queue.
43 ///
44 /// Otherwise, use `QueueSizes::AskDevice`, which will query the backend using the vhost-user
45 /// `VHOST_USER_GET_QUEUE_NUM` message if `VHOST_USER_PROTOCOL_F_MQ` has been negotiated. If the
46 /// MQ feature is supported, the `queue_size` field will be used as the size of each queue up to
47 /// the number indicated by the backend's `GET_QUEUE_NUM` response. If the MQ feature is not
48 /// supported, `default_queues` will be used as the number of queues instead, and again
49 /// `queue_size` will be used as the size of each of these queues.
50 pub enum QueueSizes {
51     /// Use a fixed number of queues. Each element in the `Vec` represents the size of the
52     /// corresponding queue with the same index. The number of queues is determined by the length
53     /// of the `Vec`.
54     Fixed(Vec<u16>),
55     /// Query the backend device to determine how many queues it supports.
56     AskDevice {
57         /// Size of each queue (number of elements in each ring).
58         queue_size: u16,
59         /// Default number of queues to use if the backend does not support the
60         /// `VHOST_USER_PROTOCOL_F_MQ` feature.
61         default_queues: usize,
62     },
63 }
64 
65 impl VhostUserVirtioDevice {
66     /// Create a new VirtioDevice for a vhost-user device frontend.
67     ///
68     /// # Arguments
69     ///
70     /// - `connection`: connection to the device backend
71     /// - `device_type`: virtio device type
72     /// - `queue_sizes`: per-device queue size configuration
73     /// - `max_queues`: maximum number of queues supported by this implementation
74     /// - `allow_features`: allowed virtio device features
75     /// - `allow_protocol_features`: allowed vhost-user protocol features
76     /// - `base_features`: base virtio device features (e.g. `VIRTIO_F_VERSION_1`)
77     /// - `cfg`: bytes to return for the virtio configuration space (queried from device if not
78     ///   specified)
new( connection: Connection, device_type: DeviceType, queue_sizes: QueueSizes, max_queues: usize, allow_features: u64, allow_protocol_features: VhostUserProtocolFeatures, base_features: u64, cfg: Option<&[u8]>, expose_shmem_descriptors_with_viommu: bool, ) -> Result<VhostUserVirtioDevice>79     pub fn new(
80         connection: Connection,
81         device_type: DeviceType,
82         queue_sizes: QueueSizes,
83         max_queues: usize,
84         allow_features: u64,
85         allow_protocol_features: VhostUserProtocolFeatures,
86         base_features: u64,
87         cfg: Option<&[u8]>,
88         expose_shmem_descriptors_with_viommu: bool,
89     ) -> Result<VhostUserVirtioDevice> {
90         let allow_features =
91             allow_features | base_features | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
92         let init_features = base_features | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
93 
94         let mut handler = VhostUserHandler::new_from_connection(
95             connection,
96             max_queues as u64,
97             allow_features,
98             init_features,
99             allow_protocol_features,
100         )?;
101 
102         let queue_sizes = match queue_sizes {
103             QueueSizes::Fixed(v) => v,
104             QueueSizes::AskDevice {
105                 queue_size,
106                 default_queues,
107             } => handler.queue_sizes(queue_size, default_queues)?,
108         };
109 
110         Ok(VhostUserVirtioDevice {
111             device_type,
112             worker_thread: None,
113             handler: RefCell::new(handler),
114             queue_sizes,
115             cfg: cfg.map(|cfg| cfg.to_vec()),
116             expose_shmem_descriptors_with_viommu,
117         })
118     }
119 }
120 
121 impl VirtioDevice for VhostUserVirtioDevice {
keep_rds(&self) -> Vec<RawDescriptor>122     fn keep_rds(&self) -> Vec<RawDescriptor> {
123         Vec::new()
124     }
125 
device_type(&self) -> DeviceType126     fn device_type(&self) -> DeviceType {
127         self.device_type
128     }
129 
queue_max_sizes(&self) -> &[u16]130     fn queue_max_sizes(&self) -> &[u16] {
131         &self.queue_sizes
132     }
133 
features(&self) -> u64134     fn features(&self) -> u64 {
135         self.handler.borrow().avail_features
136     }
137 
ack_features(&mut self, features: u64)138     fn ack_features(&mut self, features: u64) {
139         if let Err(e) = self.handler.borrow_mut().ack_features(features) {
140             error!("failed to enable features 0x{:x}: {}", features, e);
141         }
142     }
143 
read_config(&self, offset: u64, data: &mut [u8])144     fn read_config(&self, offset: u64, data: &mut [u8]) {
145         if let Some(cfg) = &self.cfg {
146             copy_config(data, 0, cfg, offset);
147         } else if let Err(e) = self.handler.borrow_mut().read_config(offset, data) {
148             error!("failed to read config: {}", e);
149         }
150     }
151 
write_config(&mut self, offset: u64, data: &[u8])152     fn write_config(&mut self, offset: u64, data: &[u8]) {
153         if let Err(e) = self.handler.borrow_mut().write_config(offset, data) {
154             error!("failed to write config: {}", e);
155         }
156     }
157 
activate( &mut self, mem: GuestMemory, interrupt: Interrupt, queues: Vec<(Queue, Event)>, ) -> anyhow::Result<()>158     fn activate(
159         &mut self,
160         mem: GuestMemory,
161         interrupt: Interrupt,
162         queues: Vec<(Queue, Event)>,
163     ) -> anyhow::Result<()> {
164         let worker_thread = self
165             .handler
166             .borrow_mut()
167             .activate(mem, interrupt, queues, &format!("{}", self.device_type))
168             .context("failed to activate queues")?;
169         self.worker_thread = Some(worker_thread);
170         Ok(())
171     }
172 
reset(&mut self) -> bool173     fn reset(&mut self) -> bool {
174         if let Err(e) = self.handler.borrow_mut().reset(self.queue_sizes.len()) {
175             error!("Failed to reset device: {}", e);
176             false
177         } else {
178             true
179         }
180     }
181 
get_shared_memory_region(&self) -> Option<SharedMemoryRegion>182     fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
183         match self.handler.borrow_mut().get_shared_memory_region() {
184             Ok(r) => r,
185             Err(e) => {
186                 error!("Failed to get shared memory regions {}", e);
187                 None
188             }
189         }
190     }
191 
set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>)192     fn set_shared_memory_mapper(&mut self, mapper: Box<dyn SharedMemoryMapper>) {
193         if let Err(e) = self.handler.borrow_mut().set_shared_memory_mapper(mapper) {
194             error!("Error setting shared memory mapper {}", e);
195         }
196     }
197 
expose_shmem_descriptors_with_viommu(&self) -> bool198     fn expose_shmem_descriptors_with_viommu(&self) -> bool {
199         self.expose_shmem_descriptors_with_viommu
200     }
201 }
202 
203 impl Suspendable for VhostUserVirtioDevice {}
204