1 // Copyright 2019 Intel Corporation. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 use crate::{ 5 ActivateError, EpollHelper, EpollHelperError, EpollHelperHandler, GuestMemoryMmap, 6 GuestRegionMmap, VirtioInterrupt, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IN_ORDER, 7 VIRTIO_F_NOTIFICATION_DATA, VIRTIO_F_ORDER_PLATFORM, VIRTIO_F_RING_EVENT_IDX, 8 VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, 9 }; 10 use anyhow::anyhow; 11 use std::io; 12 use std::ops::Deref; 13 use std::os::unix::io::AsRawFd; 14 use std::sync::{atomic::AtomicBool, Arc, Barrier, Mutex}; 15 use versionize::Versionize; 16 use vhost::vhost_user::message::{ 17 VhostUserInflight, VhostUserProtocolFeatures, VhostUserVirtioFeatures, 18 }; 19 use vhost::vhost_user::{MasterReqHandler, VhostUserMasterReqHandler}; 20 use vhost::Error as VhostError; 21 use virtio_queue::Error as QueueError; 22 use virtio_queue::Queue; 23 use vm_memory::{ 24 mmap::MmapRegionError, Address, Error as MmapError, GuestAddressSpace, GuestMemory, 25 GuestMemoryAtomic, 26 }; 27 use vm_migration::{protocol::MemoryRangeTable, MigratableError, Snapshot, VersionMapped}; 28 use vmm_sys_util::eventfd::EventFd; 29 use vu_common_ctrl::VhostUserHandle; 30 31 pub mod blk; 32 pub mod fs; 33 pub mod net; 34 pub mod vu_common_ctrl; 35 36 pub use self::blk::Blk; 37 pub use self::fs::*; 38 pub use self::net::Net; 39 pub use self::vu_common_ctrl::VhostUserConfig; 40 41 #[derive(Debug)] 42 pub enum Error { 43 /// Failed accepting connection. 44 AcceptConnection(io::Error), 45 /// Invalid available address. 46 AvailAddress, 47 /// Queue number is not correct 48 BadQueueNum, 49 /// Failed binding vhost-user socket. 50 BindSocket(io::Error), 51 /// Creating kill eventfd failed. 52 CreateKillEventFd(io::Error), 53 /// Cloning kill eventfd failed. 54 CloneKillEventFd(io::Error), 55 /// Invalid descriptor table address. 56 DescriptorTableAddress, 57 /// Signal used queue failed. 58 FailedSignalingUsedQueue(io::Error), 59 /// Failed to read vhost eventfd. 60 MemoryRegions(MmapError), 61 /// Failed removing socket path 62 RemoveSocketPath(io::Error), 63 /// Failed to create master. 64 VhostUserCreateMaster(VhostError), 65 /// Failed to open vhost device. 66 VhostUserOpen(VhostError), 67 /// Connection to socket failed. 68 VhostUserConnect, 69 /// Get features failed. 70 VhostUserGetFeatures(VhostError), 71 /// Get queue max number failed. 72 VhostUserGetQueueMaxNum(VhostError), 73 /// Get protocol features failed. 74 VhostUserGetProtocolFeatures(VhostError), 75 /// Get vring base failed. 76 VhostUserGetVringBase(VhostError), 77 /// Vhost-user Backend not support vhost-user protocol. 78 VhostUserProtocolNotSupport, 79 /// Set owner failed. 80 VhostUserSetOwner(VhostError), 81 /// Reset owner failed. 82 VhostUserResetOwner(VhostError), 83 /// Set features failed. 84 VhostUserSetFeatures(VhostError), 85 /// Set protocol features failed. 86 VhostUserSetProtocolFeatures(VhostError), 87 /// Set mem table failed. 88 VhostUserSetMemTable(VhostError), 89 /// Set vring num failed. 90 VhostUserSetVringNum(VhostError), 91 /// Set vring addr failed. 92 VhostUserSetVringAddr(VhostError), 93 /// Set vring base failed. 94 VhostUserSetVringBase(VhostError), 95 /// Set vring call failed. 96 VhostUserSetVringCall(VhostError), 97 /// Set vring kick failed. 98 VhostUserSetVringKick(VhostError), 99 /// Set vring enable failed. 100 VhostUserSetVringEnable(VhostError), 101 /// Failed to create vhost eventfd. 102 VhostIrqCreate(io::Error), 103 /// Failed to read vhost eventfd. 104 VhostIrqRead(io::Error), 105 /// Failed to read vhost eventfd. 106 VhostUserMemoryRegion(MmapError), 107 /// Failed to create the master request handler from slave. 108 MasterReqHandlerCreation(vhost::vhost_user::Error), 109 /// Set slave request fd failed. 110 VhostUserSetSlaveRequestFd(vhost::Error), 111 /// Add memory region failed. 112 VhostUserAddMemReg(VhostError), 113 /// Failed getting the configuration. 114 VhostUserGetConfig(VhostError), 115 /// Failed setting the configuration. 116 VhostUserSetConfig(VhostError), 117 /// Failed getting inflight shm log. 118 VhostUserGetInflight(VhostError), 119 /// Failed setting inflight shm log. 120 VhostUserSetInflight(VhostError), 121 /// Failed setting the log base. 122 VhostUserSetLogBase(VhostError), 123 /// Invalid used address. 124 UsedAddress, 125 /// Invalid features provided from vhost-user backend 126 InvalidFeatures, 127 /// Missing file descriptor for the region. 128 MissingRegionFd, 129 /// Missing IrqFd 130 MissingIrqFd, 131 /// Failed getting the available index. 132 GetAvailableIndex(QueueError), 133 /// Migration is not supported by this vhost-user device. 134 MigrationNotSupported, 135 /// Failed creating memfd. 136 MemfdCreate(io::Error), 137 /// Failed truncating the file size to the expected size. 138 SetFileSize(io::Error), 139 /// Failed to set the seals on the file. 140 SetSeals(io::Error), 141 /// Failed creating new mmap region 142 NewMmapRegion(MmapRegionError), 143 /// Could not find the shm log region 144 MissingShmLogRegion, 145 } 146 type Result<T> = std::result::Result<T, Error>; 147 148 pub const DEFAULT_VIRTIO_FEATURES: u64 = 1 << VIRTIO_F_RING_INDIRECT_DESC 149 | 1 << VIRTIO_F_RING_EVENT_IDX 150 | 1 << VIRTIO_F_VERSION_1 151 | 1 << VIRTIO_F_IN_ORDER 152 | 1 << VIRTIO_F_ORDER_PLATFORM 153 | 1 << VIRTIO_F_NOTIFICATION_DATA 154 | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits(); 155 156 const HUP_CONNECTION_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; 157 const SLAVE_REQ_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2; 158 159 #[derive(Default)] 160 pub struct Inflight { 161 pub info: VhostUserInflight, 162 pub fd: Option<std::fs::File>, 163 } 164 165 pub struct VhostUserEpollHandler<S: VhostUserMasterReqHandler> { 166 pub vu: Arc<Mutex<VhostUserHandle>>, 167 pub mem: GuestMemoryAtomic<GuestMemoryMmap>, 168 pub kill_evt: EventFd, 169 pub pause_evt: EventFd, 170 pub queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>, 171 pub queue_evts: Vec<EventFd>, 172 pub virtio_interrupt: Arc<dyn VirtioInterrupt>, 173 pub acked_features: u64, 174 pub acked_protocol_features: u64, 175 pub socket_path: String, 176 pub server: bool, 177 pub slave_req_handler: Option<MasterReqHandler<S>>, 178 pub inflight: Option<Inflight>, 179 } 180 181 impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> { 182 pub fn run( 183 &mut self, 184 paused: Arc<AtomicBool>, 185 paused_sync: Arc<Barrier>, 186 ) -> std::result::Result<(), EpollHelperError> { 187 let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; 188 helper.add_event_custom( 189 self.vu.lock().unwrap().socket_handle().as_raw_fd(), 190 HUP_CONNECTION_EVENT, 191 epoll::Events::EPOLLHUP, 192 )?; 193 194 if let Some(slave_req_handler) = &self.slave_req_handler { 195 helper.add_event(slave_req_handler.as_raw_fd(), SLAVE_REQ_EVENT)?; 196 } 197 198 helper.run(paused, paused_sync, self)?; 199 200 Ok(()) 201 } 202 203 fn reconnect(&mut self, helper: &mut EpollHelper) -> std::result::Result<(), EpollHelperError> { 204 helper.del_event_custom( 205 self.vu.lock().unwrap().socket_handle().as_raw_fd(), 206 HUP_CONNECTION_EVENT, 207 epoll::Events::EPOLLHUP, 208 )?; 209 210 let mut vhost_user = VhostUserHandle::connect_vhost_user( 211 self.server, 212 &self.socket_path, 213 self.queues.len() as u64, 214 true, 215 ) 216 .map_err(|e| { 217 EpollHelperError::IoError(std::io::Error::new( 218 std::io::ErrorKind::Other, 219 format!("failed connecting vhost-user backend{:?}", e), 220 )) 221 })?; 222 223 // Initialize the backend 224 vhost_user 225 .reinitialize_vhost_user( 226 self.mem.memory().deref(), 227 self.queues.clone(), 228 self.queue_evts 229 .iter() 230 .map(|q| q.try_clone().unwrap()) 231 .collect(), 232 &self.virtio_interrupt, 233 self.acked_features, 234 self.acked_protocol_features, 235 &self.slave_req_handler, 236 self.inflight.as_mut(), 237 ) 238 .map_err(|e| { 239 EpollHelperError::IoError(std::io::Error::new( 240 std::io::ErrorKind::Other, 241 format!("failed reconnecting vhost-user backend{:?}", e), 242 )) 243 })?; 244 245 helper.add_event_custom( 246 vhost_user.socket_handle().as_raw_fd(), 247 HUP_CONNECTION_EVENT, 248 epoll::Events::EPOLLHUP, 249 )?; 250 251 // Update vhost-user reference 252 let mut vu = self.vu.lock().unwrap(); 253 *vu = vhost_user; 254 255 Ok(()) 256 } 257 } 258 259 impl<S: VhostUserMasterReqHandler> EpollHelperHandler for VhostUserEpollHandler<S> { 260 fn handle_event(&mut self, helper: &mut EpollHelper, event: &epoll::Event) -> bool { 261 let ev_type = event.data as u16; 262 match ev_type { 263 HUP_CONNECTION_EVENT => { 264 if let Err(e) = self.reconnect(helper) { 265 error!("failed to reconnect vhost-user backend: {:?}", e); 266 return true; 267 } 268 } 269 SLAVE_REQ_EVENT => { 270 if let Some(slave_req_handler) = self.slave_req_handler.as_mut() { 271 if let Err(e) = slave_req_handler.handle_request() { 272 error!("Failed to handle request from vhost-user backend: {:?}", e); 273 return true; 274 } 275 } 276 } 277 _ => { 278 error!("Unknown event for vhost-user thread"); 279 return true; 280 } 281 } 282 283 false 284 } 285 } 286 287 #[derive(Default)] 288 pub struct VhostUserCommon { 289 pub vu: Option<Arc<Mutex<VhostUserHandle>>>, 290 pub acked_protocol_features: u64, 291 pub socket_path: String, 292 pub vu_num_queues: usize, 293 pub migration_started: bool, 294 pub server: bool, 295 } 296 297 impl VhostUserCommon { 298 #[allow(clippy::too_many_arguments)] 299 pub fn activate<T: VhostUserMasterReqHandler>( 300 &mut self, 301 mem: GuestMemoryAtomic<GuestMemoryMmap>, 302 queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>, 303 queue_evts: Vec<EventFd>, 304 interrupt_cb: Arc<dyn VirtioInterrupt>, 305 acked_features: u64, 306 slave_req_handler: Option<MasterReqHandler<T>>, 307 kill_evt: EventFd, 308 pause_evt: EventFd, 309 ) -> std::result::Result<VhostUserEpollHandler<T>, ActivateError> { 310 let mut inflight: Option<Inflight> = 311 if self.acked_protocol_features & VhostUserProtocolFeatures::INFLIGHT_SHMFD.bits() != 0 312 { 313 Some(Inflight::default()) 314 } else { 315 None 316 }; 317 318 if self.vu.is_none() { 319 error!("Missing vhost-user handle"); 320 return Err(ActivateError::BadActivate); 321 } 322 let vu = self.vu.as_ref().unwrap(); 323 vu.lock() 324 .unwrap() 325 .setup_vhost_user( 326 &mem.memory(), 327 queues.clone(), 328 queue_evts.iter().map(|q| q.try_clone().unwrap()).collect(), 329 &interrupt_cb, 330 acked_features, 331 &slave_req_handler, 332 inflight.as_mut(), 333 ) 334 .map_err(ActivateError::VhostUserBlkSetup)?; 335 336 Ok(VhostUserEpollHandler { 337 vu: vu.clone(), 338 mem, 339 kill_evt, 340 pause_evt, 341 queues, 342 queue_evts, 343 virtio_interrupt: interrupt_cb, 344 acked_features, 345 acked_protocol_features: self.acked_protocol_features, 346 socket_path: self.socket_path.clone(), 347 server: self.server, 348 slave_req_handler, 349 inflight, 350 }) 351 } 352 353 pub fn restore_backend_connection(&mut self, acked_features: u64) -> Result<()> { 354 let mut vu = VhostUserHandle::connect_vhost_user( 355 self.server, 356 &self.socket_path, 357 self.vu_num_queues as u64, 358 false, 359 )?; 360 361 vu.set_protocol_features_vhost_user(acked_features, self.acked_protocol_features)?; 362 363 self.vu = Some(Arc::new(Mutex::new(vu))); 364 365 Ok(()) 366 } 367 368 pub fn shutdown(&mut self) { 369 if let Some(vu) = &self.vu { 370 let _ = unsafe { libc::close(vu.lock().unwrap().socket_handle().as_raw_fd()) }; 371 } 372 373 // Remove socket path if needed 374 if self.server { 375 let _ = std::fs::remove_file(&self.socket_path); 376 } 377 } 378 379 pub fn add_memory_region( 380 &mut self, 381 guest_memory: &Option<GuestMemoryAtomic<GuestMemoryMmap>>, 382 region: &Arc<GuestRegionMmap>, 383 ) -> std::result::Result<(), crate::Error> { 384 if let Some(vu) = &self.vu { 385 if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIGURE_MEM_SLOTS.bits() 386 != 0 387 { 388 return vu 389 .lock() 390 .unwrap() 391 .add_memory_region(region) 392 .map_err(crate::Error::VhostUserAddMemoryRegion); 393 } else if let Some(guest_memory) = guest_memory { 394 return vu 395 .lock() 396 .unwrap() 397 .update_mem_table(guest_memory.memory().deref()) 398 .map_err(crate::Error::VhostUserUpdateMemory); 399 } 400 } 401 Ok(()) 402 } 403 404 pub fn pause(&mut self) -> std::result::Result<(), MigratableError> { 405 if let Some(vu) = &self.vu { 406 vu.lock() 407 .unwrap() 408 .pause_vhost_user(self.vu_num_queues) 409 .map_err(|e| { 410 MigratableError::Pause(anyhow!("Error pausing vhost-user-blk backend: {:?}", e)) 411 }) 412 } else { 413 Ok(()) 414 } 415 } 416 417 pub fn resume(&mut self) -> std::result::Result<(), MigratableError> { 418 if let Some(vu) = &self.vu { 419 vu.lock() 420 .unwrap() 421 .resume_vhost_user(self.vu_num_queues) 422 .map_err(|e| { 423 MigratableError::Resume(anyhow!( 424 "Error resuming vhost-user-blk backend: {:?}", 425 e 426 )) 427 }) 428 } else { 429 Ok(()) 430 } 431 } 432 433 pub fn snapshot<T>( 434 &mut self, 435 id: &str, 436 state: &T, 437 ) -> std::result::Result<Snapshot, MigratableError> 438 where 439 T: Versionize + VersionMapped, 440 { 441 let snapshot = Snapshot::new_from_versioned_state(id, state)?; 442 443 if self.migration_started { 444 self.shutdown(); 445 } 446 447 Ok(snapshot) 448 } 449 450 pub fn start_dirty_log( 451 &mut self, 452 guest_memory: &Option<GuestMemoryAtomic<GuestMemoryMmap>>, 453 ) -> std::result::Result<(), MigratableError> { 454 if let Some(vu) = &self.vu { 455 if let Some(guest_memory) = guest_memory { 456 let last_ram_addr = guest_memory.memory().last_addr().raw_value(); 457 vu.lock() 458 .unwrap() 459 .start_dirty_log(last_ram_addr) 460 .map_err(|e| { 461 MigratableError::StartDirtyLog(anyhow!( 462 "Error starting migration for vhost-user backend: {:?}", 463 e 464 )) 465 }) 466 } else { 467 Err(MigratableError::StartDirtyLog(anyhow!( 468 "Missing guest memory" 469 ))) 470 } 471 } else { 472 Ok(()) 473 } 474 } 475 476 pub fn stop_dirty_log(&mut self) -> std::result::Result<(), MigratableError> { 477 if let Some(vu) = &self.vu { 478 vu.lock().unwrap().stop_dirty_log().map_err(|e| { 479 MigratableError::StopDirtyLog(anyhow!( 480 "Error stopping migration for vhost-user backend: {:?}", 481 e 482 )) 483 }) 484 } else { 485 Ok(()) 486 } 487 } 488 489 pub fn dirty_log( 490 &mut self, 491 guest_memory: &Option<GuestMemoryAtomic<GuestMemoryMmap>>, 492 ) -> std::result::Result<MemoryRangeTable, MigratableError> { 493 if let Some(vu) = &self.vu { 494 if let Some(guest_memory) = guest_memory { 495 let last_ram_addr = guest_memory.memory().last_addr().raw_value(); 496 vu.lock().unwrap().dirty_log(last_ram_addr).map_err(|e| { 497 MigratableError::DirtyLog(anyhow!( 498 "Error retrieving dirty ranges from vhost-user backend: {:?}", 499 e 500 )) 501 }) 502 } else { 503 Err(MigratableError::DirtyLog(anyhow!("Missing guest memory"))) 504 } 505 } else { 506 Ok(MemoryRangeTable::default()) 507 } 508 } 509 510 pub fn start_migration(&mut self) -> std::result::Result<(), MigratableError> { 511 self.migration_started = true; 512 Ok(()) 513 } 514 515 pub fn complete_migration( 516 &mut self, 517 kill_evt: Option<EventFd>, 518 ) -> std::result::Result<(), MigratableError> { 519 self.migration_started = false; 520 521 // Make sure the device thread is killed in order to prevent from 522 // reconnections to the socket. 523 if let Some(kill_evt) = kill_evt { 524 kill_evt.write(1).map_err(|e| { 525 MigratableError::CompleteMigration(anyhow!( 526 "Error killing vhost-user thread: {:?}", 527 e 528 )) 529 })?; 530 } 531 532 // Drop the vhost-user handler to avoid further calls to fail because 533 // the connection with the backend has been closed. 534 self.vu = None; 535 536 Ok(()) 537 } 538 } 539