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