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