| 402 | } |
| 403 | |
| 404 | fn resume(&mut self) -> std::result::Result<(), MigratableError> { |
| 405 | info!( |
| 406 | "Resuming virtio-{}", |
| 407 | VirtioDeviceType::from(self.device_type) |
| 408 | ); |
| 409 | self.paused.store(false, Ordering::SeqCst); |
| 410 | if let Some(epoll_threads) = &self.epoll_threads { |
| 411 | for t in epoll_threads.iter() { |
| 412 | t.thread().unpark(); |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | // Signal each activated queue eventfd so workers process restored queues |
| 417 | // that may already contain pending requests. |
| 418 | for queue_evt in &self.queue_evts { |
| 419 | queue_evt.write(1).map_err(|e| { |
| 420 | MigratableError::Resume(anyhow!( |
| 421 | "Could not notify restored virtio worker on resume: {e}" |
| 422 | )) |
| 423 | })?; |
| 424 | } |
| 425 | |
| 426 | // Also trigger interrupts into the guest to wake up the driver to avoid a "livelock" |
| 427 | for i in 0..self.queue_evts.len() { |
| 428 | self.trigger_interrupt(crate::VirtioInterruptType::Queue(i as u16)) |
| 429 | .ok(); |
| 430 | } |
| 431 | |
| 432 | Ok(()) |
| 433 | } |
| 434 | } |