1 // Copyright 2019 Intel Corporation. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 use super::vu_common_ctrl::VhostUserHandle; 5 use super::{Error, Result, DEFAULT_VIRTIO_FEATURES}; 6 use crate::seccomp_filters::Thread; 7 use crate::thread_helper::spawn_virtio_thread; 8 use crate::vhost_user::VhostUserCommon; 9 use crate::{ 10 ActivateResult, UserspaceMapping, VirtioCommon, VirtioDevice, VirtioDeviceType, 11 VirtioInterrupt, VirtioSharedMemoryList, VIRTIO_F_IOMMU_PLATFORM, 12 }; 13 use crate::{GuestMemoryMmap, GuestRegionMmap, MmapRegion}; 14 use seccompiler::SeccompAction; 15 use serde::{Deserialize, Serialize}; 16 use serde_with::{serde_as, Bytes}; 17 use std::result; 18 use std::sync::atomic::AtomicBool; 19 use std::sync::{Arc, Barrier, Mutex}; 20 use std::thread; 21 use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; 22 use vhost::vhost_user::{FrontendReqHandler, VhostUserFrontend, VhostUserFrontendReqHandler}; 23 use virtio_queue::Queue; 24 use vm_memory::{ByteValued, GuestMemoryAtomic}; 25 use vm_migration::{ 26 protocol::MemoryRangeTable, Migratable, MigratableError, Pausable, Snapshot, Snapshottable, 27 Transportable, 28 }; 29 use vmm_sys_util::eventfd::EventFd; 30 31 const NUM_QUEUE_OFFSET: usize = 1; 32 const DEFAULT_QUEUE_NUMBER: usize = 2; 33 34 #[derive(Serialize, Deserialize)] 35 pub struct State { 36 pub avail_features: u64, 37 pub acked_features: u64, 38 pub config: VirtioFsConfig, 39 pub acked_protocol_features: u64, 40 pub vu_num_queues: usize, 41 pub backend_req_support: bool, 42 } 43 44 struct BackendReqHandler {} 45 impl VhostUserFrontendReqHandler for BackendReqHandler {} 46 47 pub const VIRTIO_FS_TAG_LEN: usize = 36; 48 #[serde_as] 49 #[derive(Copy, Clone, Serialize, Deserialize)] 50 #[repr(C, packed)] 51 pub struct VirtioFsConfig { 52 #[serde_as(as = "Bytes")] 53 pub tag: [u8; VIRTIO_FS_TAG_LEN], 54 pub num_request_queues: u32, 55 } 56 57 impl Default for VirtioFsConfig { 58 fn default() -> Self { 59 VirtioFsConfig { 60 tag: [0; VIRTIO_FS_TAG_LEN], 61 num_request_queues: 0, 62 } 63 } 64 } 65 66 // SAFETY: only a series of integers 67 unsafe impl ByteValued for VirtioFsConfig {} 68 69 pub struct Fs { 70 common: VirtioCommon, 71 vu_common: VhostUserCommon, 72 id: String, 73 config: VirtioFsConfig, 74 // Hold ownership of the memory that is allocated for the device 75 // which will be automatically dropped when the device is dropped 76 cache: Option<(VirtioSharedMemoryList, MmapRegion)>, 77 seccomp_action: SeccompAction, 78 guest_memory: Option<GuestMemoryAtomic<GuestMemoryMmap>>, 79 epoll_thread: Option<thread::JoinHandle<()>>, 80 exit_evt: EventFd, 81 iommu: bool, 82 } 83 84 impl Fs { 85 /// Create a new virtio-fs device. 86 #[allow(clippy::too_many_arguments)] 87 pub fn new( 88 id: String, 89 path: &str, 90 tag: &str, 91 req_num_queues: usize, 92 queue_size: u16, 93 cache: Option<(VirtioSharedMemoryList, MmapRegion)>, 94 seccomp_action: SeccompAction, 95 exit_evt: EventFd, 96 iommu: bool, 97 state: Option<State>, 98 ) -> Result<Fs> { 99 // Calculate the actual number of queues needed. 100 let num_queues = NUM_QUEUE_OFFSET + req_num_queues; 101 102 // Connect to the vhost-user socket. 103 let mut vu = VhostUserHandle::connect_vhost_user(false, path, num_queues as u64, false)?; 104 105 let ( 106 avail_features, 107 acked_features, 108 acked_protocol_features, 109 vu_num_queues, 110 config, 111 paused, 112 ) = if let Some(state) = state { 113 info!("Restoring vhost-user-fs {}", id); 114 115 vu.set_protocol_features_vhost_user( 116 state.acked_features, 117 state.acked_protocol_features, 118 )?; 119 120 ( 121 state.avail_features, 122 state.acked_features, 123 state.acked_protocol_features, 124 state.vu_num_queues, 125 state.config, 126 true, 127 ) 128 } else { 129 // Filling device and vring features VMM supports. 130 let avail_features = DEFAULT_VIRTIO_FEATURES; 131 132 let avail_protocol_features = VhostUserProtocolFeatures::MQ 133 | VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS 134 | VhostUserProtocolFeatures::REPLY_ACK 135 | VhostUserProtocolFeatures::INFLIGHT_SHMFD 136 | VhostUserProtocolFeatures::LOG_SHMFD; 137 138 let (acked_features, acked_protocol_features) = 139 vu.negotiate_features_vhost_user(avail_features, avail_protocol_features)?; 140 141 let backend_num_queues = 142 if acked_protocol_features & VhostUserProtocolFeatures::MQ.bits() != 0 { 143 vu.socket_handle() 144 .get_queue_num() 145 .map_err(Error::VhostUserGetQueueMaxNum)? as usize 146 } else { 147 DEFAULT_QUEUE_NUMBER 148 }; 149 150 if num_queues > backend_num_queues { 151 error!( 152 "vhost-user-fs requested too many queues ({}) since the backend only supports {}\n", 153 num_queues, backend_num_queues 154 ); 155 return Err(Error::BadQueueNum); 156 } 157 158 // Create virtio-fs device configuration. 159 let mut config = VirtioFsConfig::default(); 160 let tag_bytes_slice = tag.as_bytes(); 161 let len = if tag_bytes_slice.len() < config.tag.len() { 162 tag_bytes_slice.len() 163 } else { 164 config.tag.len() 165 }; 166 config.tag[..len].copy_from_slice(tag_bytes_slice[..len].as_ref()); 167 config.num_request_queues = req_num_queues as u32; 168 169 ( 170 acked_features, 171 // If part of the available features that have been acked, the 172 // PROTOCOL_FEATURES bit must be already set through the VIRTIO 173 // acked features as we know the guest would never ack it, thus 174 // the feature would be lost. 175 acked_features & VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(), 176 acked_protocol_features, 177 num_queues, 178 config, 179 false, 180 ) 181 }; 182 183 Ok(Fs { 184 common: VirtioCommon { 185 device_type: VirtioDeviceType::Fs as u32, 186 avail_features, 187 acked_features, 188 queue_sizes: vec![queue_size; num_queues], 189 paused_sync: Some(Arc::new(Barrier::new(2))), 190 min_queues: 1, 191 paused: Arc::new(AtomicBool::new(paused)), 192 ..Default::default() 193 }, 194 vu_common: VhostUserCommon { 195 vu: Some(Arc::new(Mutex::new(vu))), 196 acked_protocol_features, 197 socket_path: path.to_string(), 198 vu_num_queues, 199 ..Default::default() 200 }, 201 id, 202 config, 203 cache, 204 seccomp_action, 205 guest_memory: None, 206 epoll_thread: None, 207 exit_evt, 208 iommu, 209 }) 210 } 211 212 fn state(&self) -> State { 213 State { 214 avail_features: self.common.avail_features, 215 acked_features: self.common.acked_features, 216 config: self.config, 217 acked_protocol_features: self.vu_common.acked_protocol_features, 218 vu_num_queues: self.vu_common.vu_num_queues, 219 backend_req_support: false, 220 } 221 } 222 } 223 224 impl Drop for Fs { 225 fn drop(&mut self) { 226 if let Some(kill_evt) = self.common.kill_evt.take() { 227 // Ignore the result because there is nothing we can do about it. 228 let _ = kill_evt.write(1); 229 } 230 self.common.wait_for_epoll_threads(); 231 if let Some(thread) = self.epoll_thread.take() { 232 if let Err(e) = thread.join() { 233 error!("Error joining thread: {:?}", e); 234 } 235 } 236 } 237 } 238 239 impl VirtioDevice for Fs { 240 fn device_type(&self) -> u32 { 241 self.common.device_type 242 } 243 244 fn queue_max_sizes(&self) -> &[u16] { 245 &self.common.queue_sizes 246 } 247 248 fn features(&self) -> u64 { 249 let mut features = self.common.avail_features; 250 if self.iommu { 251 features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; 252 } 253 features 254 } 255 256 fn ack_features(&mut self, value: u64) { 257 self.common.ack_features(value) 258 } 259 260 fn read_config(&self, offset: u64, data: &mut [u8]) { 261 self.read_config_from_slice(self.config.as_slice(), offset, data); 262 } 263 264 fn activate( 265 &mut self, 266 mem: GuestMemoryAtomic<GuestMemoryMmap>, 267 interrupt_cb: Arc<dyn VirtioInterrupt>, 268 queues: Vec<(usize, Queue, EventFd)>, 269 ) -> ActivateResult { 270 self.common.activate(&queues, &interrupt_cb)?; 271 self.guest_memory = Some(mem.clone()); 272 273 let backend_req_handler: Option<FrontendReqHandler<BackendReqHandler>> = None; 274 // Run a dedicated thread for handling potential reconnections with 275 // the backend. 276 let (kill_evt, pause_evt) = self.common.dup_eventfds(); 277 278 let mut handler = self.vu_common.activate( 279 mem, 280 queues, 281 interrupt_cb, 282 self.common.acked_features, 283 backend_req_handler, 284 kill_evt, 285 pause_evt, 286 )?; 287 288 let paused = self.common.paused.clone(); 289 let paused_sync = self.common.paused_sync.clone(); 290 291 let mut epoll_threads = Vec::new(); 292 spawn_virtio_thread( 293 &self.id, 294 &self.seccomp_action, 295 Thread::VirtioVhostFs, 296 &mut epoll_threads, 297 &self.exit_evt, 298 move || handler.run(paused, paused_sync.unwrap()), 299 )?; 300 self.epoll_thread = Some(epoll_threads.remove(0)); 301 302 event!("virtio-device", "activated", "id", &self.id); 303 Ok(()) 304 } 305 306 fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> { 307 // We first must resume the virtio thread if it was paused. 308 if self.common.pause_evt.take().is_some() { 309 self.common.resume().ok()?; 310 } 311 312 if let Some(vu) = &self.vu_common.vu { 313 if let Err(e) = vu.lock().unwrap().reset_vhost_user() { 314 error!("Failed to reset vhost-user daemon: {:?}", e); 315 return None; 316 } 317 } 318 319 if let Some(kill_evt) = self.common.kill_evt.take() { 320 // Ignore the result because there is nothing we can do about it. 321 let _ = kill_evt.write(1); 322 } 323 324 event!("virtio-device", "reset", "id", &self.id); 325 326 // Return the interrupt 327 Some(self.common.interrupt_cb.take().unwrap()) 328 } 329 330 fn shutdown(&mut self) { 331 self.vu_common.shutdown() 332 } 333 334 fn get_shm_regions(&self) -> Option<VirtioSharedMemoryList> { 335 self.cache.as_ref().map(|cache| cache.0.clone()) 336 } 337 338 fn set_shm_regions( 339 &mut self, 340 shm_regions: VirtioSharedMemoryList, 341 ) -> std::result::Result<(), crate::Error> { 342 if let Some(cache) = self.cache.as_mut() { 343 cache.0 = shm_regions; 344 Ok(()) 345 } else { 346 Err(crate::Error::SetShmRegionsNotSupported) 347 } 348 } 349 350 fn add_memory_region( 351 &mut self, 352 region: &Arc<GuestRegionMmap>, 353 ) -> std::result::Result<(), crate::Error> { 354 self.vu_common.add_memory_region(&self.guest_memory, region) 355 } 356 357 fn userspace_mappings(&self) -> Vec<UserspaceMapping> { 358 let mut mappings = Vec::new(); 359 if let Some(cache) = self.cache.as_ref() { 360 mappings.push(UserspaceMapping { 361 host_addr: cache.0.host_addr, 362 mem_slot: cache.0.mem_slot, 363 addr: cache.0.addr, 364 len: cache.0.len, 365 mergeable: false, 366 }) 367 } 368 369 mappings 370 } 371 } 372 373 impl Pausable for Fs { 374 fn pause(&mut self) -> result::Result<(), MigratableError> { 375 self.vu_common.pause()?; 376 self.common.pause() 377 } 378 379 fn resume(&mut self) -> result::Result<(), MigratableError> { 380 self.common.resume()?; 381 382 if let Some(epoll_thread) = &self.epoll_thread { 383 epoll_thread.thread().unpark(); 384 } 385 386 self.vu_common.resume() 387 } 388 } 389 390 impl Snapshottable for Fs { 391 fn id(&self) -> String { 392 self.id.clone() 393 } 394 395 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 396 self.vu_common.snapshot(&self.state()) 397 } 398 } 399 impl Transportable for Fs {} 400 401 impl Migratable for Fs { 402 fn start_dirty_log(&mut self) -> std::result::Result<(), MigratableError> { 403 self.vu_common.start_dirty_log(&self.guest_memory) 404 } 405 406 fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> { 407 self.vu_common.stop_dirty_log() 408 } 409 410 fn dirty_log(&mut self) -> std::result::Result<MemoryRangeTable, MigratableError> { 411 self.vu_common.dirty_log(&self.guest_memory) 412 } 413 414 fn start_migration(&mut self) -> std::result::Result<(), MigratableError> { 415 self.vu_common.start_migration() 416 } 417 418 fn complete_migration(&mut self) -> std::result::Result<(), MigratableError> { 419 self.vu_common 420 .complete_migration(self.common.kill_evt.take()) 421 } 422 } 423