diff --git a/vm-virtio/src/block.rs b/vm-virtio/src/block.rs index 7b0916247..1c84be16c 100755 --- a/vm-virtio/src/block.rs +++ b/vm-virtio/src/block.rs @@ -17,6 +17,7 @@ use std::os::linux::fs::MetadataExt; use std::os::unix::io::AsRawFd; use std::path::PathBuf; use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; @@ -27,6 +28,7 @@ use super::{ }; use crate::VirtioInterrupt; use virtio_bindings::bindings::virtio_blk::*; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap}; use vmm_sys_util::eventfd::EventFd; @@ -43,6 +45,8 @@ const QUEUE_AVAIL_EVENT: DeviceEventT = 0; pub const KILL_EVENT: DeviceEventT = 1; // Number of DeviceEventT events supported by this implementation. pub const BLOCK_EVENTS_COUNT: usize = 2; +// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 3; #[derive(Debug)] pub enum Error { @@ -326,6 +330,8 @@ struct BlockEpollHandler { disk_nsectors: u64, interrupt_cb: Arc, disk_image_id: Vec, + kill_evt: EventFd, + pause_evt: EventFd, } impl BlockEpollHandler { @@ -399,7 +405,11 @@ impl BlockEpollHandler { Ok(()) } - fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) -> result::Result<(), DeviceError> { + fn run( + &mut self, + queue_evt: EventFd, + paused: Arc, + ) -> result::Result<(), DeviceError> { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; @@ -414,10 +424,17 @@ impl BlockEpollHandler { epoll::ctl( epoll_fd, epoll::ControlOptions::EPOLL_CTL_ADD, - kill_evt.as_raw_fd(), + self.kill_evt.as_raw_fd(), epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; const EPOLL_EVENTS_LEN: usize = 100; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; @@ -459,6 +476,15 @@ impl BlockEpollHandler { debug!("KILL_EVENT received, stopping epoll loop"); break 'epoll; } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-block epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } _ => { error!("Unknown event for virtio-block"); } @@ -481,6 +507,9 @@ pub struct Block { config_space: Vec, queue_evt: Option, interrupt_cb: Option>, + epoll_thread: Option>>, + pause_evt: Option, + paused: Arc, } pub fn build_config_space(disk_size: u64) -> Vec { @@ -534,6 +563,9 @@ impl Block { config_space: build_config_space(disk_size), queue_evt: None, interrupt_cb: None, + epoll_thread: None, + pause_evt: None, + paused: Arc::new(AtomicBool::new(false)), }) } } @@ -630,16 +662,23 @@ impl VirtioDevice for Block { return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + if let Some(disk_image) = self.disk_image.clone() { let disk_image_id = build_disk_image_id(&self.disk_path); @@ -663,16 +702,19 @@ impl VirtioDevice for Block { disk_nsectors: self.disk_nsectors, interrupt_cb, disk_image_id, + kill_evt, + pause_evt, }; - let worker_result = thread::Builder::new() + let paused = self.paused.clone(); + thread::Builder::new() .name("virtio_blk".to_string()) - .spawn(move || handler.run(queue_evt, kill_evt)); - - if let Err(e) = worker_result { - error!("failed to spawn virtio_blk worker: {}", e); - return Err(ActivateError::BadActivate); - } + .spawn(move || handler.run(queue_evt, paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone the virtio-blk epoll thread: {}", e); + ActivateError::BadActivate + })?; return Ok(()); } @@ -680,6 +722,11 @@ impl VirtioDevice for Block { } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -692,3 +739,10 @@ impl VirtioDevice for Block { )) } } + +impl Pausable for Block { + virtio_pausable_inner!(); +} + +impl Snapshotable for Block {} +impl Migratable for Block {} diff --git a/vm-virtio/src/console.rs b/vm-virtio/src/console.rs index ad0384b72..7da90fe25 100755 --- a/vm-virtio/src/console.rs +++ b/vm-virtio/src/console.rs @@ -20,7 +20,8 @@ use super::{ VirtioInterruptType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, }; use crate::VirtioInterrupt; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{ByteValued, Bytes, GuestMemoryMmap}; use vmm_sys_util::eventfd::EventFd; @@ -37,6 +38,8 @@ const INPUT_EVENT: DeviceEventT = 2; const KILL_EVENT: DeviceEventT = 3; // Console configuration change event is triggered. const CONFIG_EVENT: DeviceEventT = 4; +// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 5; //Console size feature bit const VIRTIO_CONSOLE_F_SIZE: u64 = 0; @@ -64,6 +67,7 @@ struct ConsoleEpollHandler { input_evt: EventFd, config_evt: EventFd, kill_evt: EventFd, + pause_evt: EventFd, } impl ConsoleEpollHandler { @@ -157,7 +161,7 @@ impl ConsoleEpollHandler { }) } - fn run(&mut self) -> result::Result<(), DeviceError> { + fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; @@ -198,6 +202,13 @@ impl ConsoleEpollHandler { epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; const EPOLL_EVENTS_LEN: usize = 100; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; @@ -264,6 +275,15 @@ impl ConsoleEpollHandler { debug!("KILL_EVENT received, stopping epoll loop"); break 'epoll; } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-console epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } _ => { error!("Unknown event for virtio-console"); } @@ -323,6 +343,7 @@ impl VirtioConsoleConfig { /// Virtio device for exposing console to the guest OS through virtio. pub struct Console { kill_evt: Option, + pause_evt: Option, avail_features: u64, acked_features: u64, config: Arc>, @@ -330,6 +351,8 @@ pub struct Console { out: Arc>>, queue_evts: Option>, interrupt_cb: Option>, + epoll_thread: Option>>, + paused: Arc, } impl Console { @@ -360,6 +383,7 @@ impl Console { Ok(( Console { kill_evt: None, + pause_evt: None, avail_features, acked_features: 0u64, config: console_config, @@ -367,6 +391,8 @@ impl Console { out: Arc::new(Mutex::new(out)), queue_evts: None, interrupt_cb: None, + epoll_thread: None, + paused: Arc::new(AtomicBool::new(false)), }, console_input, )) @@ -461,16 +487,23 @@ impl VirtioDevice for Console { return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. self.interrupt_cb = Some(interrupt_cb.clone()); @@ -507,21 +540,28 @@ impl VirtioDevice for Console { input_evt: self.input.input_evt.try_clone().unwrap(), config_evt: self.input.config_evt.try_clone().unwrap(), kill_evt, + pause_evt, }; - let worker_result = thread::Builder::new() + let paused = self.paused.clone(); + thread::Builder::new() .name("virtio_console".to_string()) - .spawn(move || handler.run()); - - if let Err(e) = worker_result { - error!("failed to spawn virtio_console worker: {}", e); - return Err(ActivateError::BadActivate); - } + .spawn(move || handler.run(paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone the virtio-console epoll thread: {}", e); + ActivateError::BadActivate + })?; Ok(()) } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -534,3 +574,7 @@ impl VirtioDevice for Console { )) } } + +virtio_pausable!(Console); +impl Snapshotable for Console {} +impl Migratable for Console {} diff --git a/vm-virtio/src/device.rs b/vm-virtio/src/device.rs index 552f3cab0..fd3830065 100644 --- a/vm-virtio/src/device.rs +++ b/vm-virtio/src/device.rs @@ -101,3 +101,45 @@ pub trait VirtioDevice: Send { pub trait DmaRemapping: Send + Sync { fn translate(&self, id: u32, addr: u64) -> std::result::Result; } + +#[macro_export] +macro_rules! virtio_pausable_inner { + () => { + fn pause(&mut self) -> result::Result<(), MigratableError> { + debug!( + "Pausing virtio-{}", + VirtioDeviceType::from(self.device_type()) + ); + self.paused.store(true, Ordering::SeqCst); + if let Some(pause_evt) = &self.pause_evt { + pause_evt + .write(1) + .map_err(|e| MigratableError::Pause(e.into()))?; + } + + Ok(()) + } + + fn resume(&mut self) -> result::Result<(), MigratableError> { + debug!( + "Resuming virtio-{}", + VirtioDeviceType::from(self.device_type()) + ); + self.paused.store(false, Ordering::SeqCst); + if let Some(epoll_thread) = &self.epoll_thread { + epoll_thread.thread().unpark(); + } + + Ok(()) + } + } +} + +#[macro_export] +macro_rules! virtio_pausable { + ($name:ident) => { + impl Pausable for $name { + virtio_pausable_inner!(); + } + }; +} diff --git a/vm-virtio/src/iommu.rs b/vm-virtio/src/iommu.rs index 530ae8e6c..fb2ce7bbc 100644 --- a/vm-virtio/src/iommu.rs +++ b/vm-virtio/src/iommu.rs @@ -12,6 +12,7 @@ use std::mem::size_of; use std::ops::Bound::Included; use std::os::unix::io::AsRawFd; use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; @@ -21,7 +22,7 @@ use super::{ VirtioDeviceType, VIRTIO_F_VERSION_1, }; use crate::{DmaRemapping, VirtioInterrupt, VirtioInterruptType}; -use vm_device::ExternalDmaMapping; +use vm_device::{ExternalDmaMapping, Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap}; use vmm_sys_util::eventfd::EventFd; @@ -40,6 +41,8 @@ const REQUEST_Q_EVENT: DeviceEventT = 0; const EVENT_Q_EVENT: DeviceEventT = 1; /// The device has been dropped. const KILL_EVENT: DeviceEventT = 2; +/// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 3; /// Virtio IOMMU features #[allow(unused)] @@ -532,6 +535,7 @@ struct IommuEpollHandler { interrupt_cb: Arc, queue_evts: Vec, kill_evt: EventFd, + pause_evt: EventFd, mapping: Arc, ext_mapping: BTreeMap>, ext_domain_mapping: BTreeMap>, @@ -591,7 +595,7 @@ impl IommuEpollHandler { }) } - fn run(&mut self) -> result::Result<(), DeviceError> { + fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; @@ -617,6 +621,13 @@ impl IommuEpollHandler { epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; const EPOLL_EVENTS_LEN: usize = 100; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; @@ -669,6 +680,15 @@ impl IommuEpollHandler { debug!("kill_evt received, stopping epoll loop"); break 'epoll; } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-iommu epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } _ => { error!("Unknown event for virtio-iommu"); break 'epoll; @@ -723,6 +743,7 @@ impl DmaRemapping for IommuMapping { pub struct Iommu { kill_evt: Option, + pause_evt: Option, avail_features: u64, acked_features: u64, config: VirtioIommuConfig, @@ -730,6 +751,8 @@ pub struct Iommu { ext_mapping: BTreeMap>, queue_evts: Option>, interrupt_cb: Option>, + epoll_thread: Option>>, + paused: Arc, } impl Iommu { @@ -747,6 +770,7 @@ impl Iommu { Ok(( Iommu { kill_evt: None, + pause_evt: None, avail_features: 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP, acked_features: 0u64, config, @@ -754,6 +778,8 @@ impl Iommu { ext_mapping: BTreeMap::new(), queue_evts: None, interrupt_cb: None, + epoll_thread: None, + paused: Arc::new(AtomicBool::new(false)), }, mapping, )) @@ -851,16 +877,22 @@ impl VirtioDevice for Iommu { return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. self.interrupt_cb = Some(interrupt_cb.clone()); @@ -882,24 +914,31 @@ impl VirtioDevice for Iommu { interrupt_cb, queue_evts, kill_evt, + pause_evt, mapping: self.mapping.clone(), ext_mapping: self.ext_mapping.clone(), ext_domain_mapping: BTreeMap::new(), }; - let worker_result = thread::Builder::new() - .name("virtio-iommu".to_string()) - .spawn(move || handler.run()); - - if let Err(e) = worker_result { - error!("failed to spawn virtio-iommu worker: {}", e); - return Err(ActivateError::BadActivate); - } + let paused = self.paused.clone(); + thread::Builder::new() + .name("virtio_iommu".to_string()) + .spawn(move || handler.run(paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone the virtio-iommu epoll thread: {}", e); + ActivateError::BadActivate + })?; Ok(()) } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -912,3 +951,7 @@ impl VirtioDevice for Iommu { )) } } + +virtio_pausable!(Iommu); +impl Snapshotable for Iommu {} +impl Migratable for Iommu {} diff --git a/vm-virtio/src/lib.rs b/vm-virtio/src/lib.rs index bc1666590..be077016e 100755 --- a/vm-virtio/src/lib.rs +++ b/vm-virtio/src/lib.rs @@ -16,14 +16,16 @@ extern crate log; extern crate pci; extern crate vhost_rs; extern crate virtio_bindings; +extern crate vm_device; extern crate vm_memory; use std::fmt; use std::io; +#[macro_use] +mod device; pub mod block; mod console; -mod device; mod iommu; pub mod net; mod pmem; diff --git a/vm-virtio/src/net.rs b/vm-virtio/src/net.rs index beae9b88b..3f523107a 100644 --- a/vm-virtio/src/net.rs +++ b/vm-virtio/src/net.rs @@ -15,6 +15,7 @@ use std::mem; use std::net::Ipv4Addr; use std::os::unix::io::{AsRawFd, RawFd}; use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; use std::vec::Vec; @@ -29,6 +30,7 @@ use super::{ use crate::VirtioInterrupt; use net_util::{MacAddr, Tap, TapError, MAC_ADDR_LEN}; use virtio_bindings::bindings::virtio_net::*; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap}; use vmm_sys_util::eventfd::EventFd; @@ -50,6 +52,8 @@ const TX_QUEUE_EVENT: DeviceEventT = 2; pub const KILL_EVENT: DeviceEventT = 3; // Number of DeviceEventT events supported by this implementation. pub const NET_EVENTS_COUNT: usize = 4; +// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 5; #[derive(Debug)] pub enum Error { @@ -121,6 +125,7 @@ struct NetEpollHandler { tx: TxVirtio, interrupt_cb: Arc, kill_evt: EventFd, + pause_evt: EventFd, epoll_fd: RawFd, rx_tap_listening: bool, } @@ -323,7 +328,7 @@ impl NetEpollHandler { Ok(()) } - fn run(&mut self) -> result::Result<(), DeviceError> { + fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { // Create the epoll file descriptor self.epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; // Add events @@ -351,6 +356,13 @@ impl NetEpollHandler { epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + self.epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; const EPOLL_EVENTS_LEN: usize = 100; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; @@ -420,6 +432,15 @@ impl NetEpollHandler { debug!("KILL_EVENT received, stopping epoll loop"); break 'epoll; } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-net epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } _ => { error!("Unknown event for virtio-net"); } @@ -433,6 +454,7 @@ impl NetEpollHandler { pub struct Net { kill_evt: Option, + pause_evt: Option, tap: Option, avail_features: u64, acked_features: u64, @@ -441,6 +463,8 @@ pub struct Net { config_space: Vec, queue_evts: Option>, interrupt_cb: Option>, + epoll_thread: Option>>, + paused: Arc, } impl Net { @@ -483,12 +507,15 @@ impl Net { Ok(Net { kill_evt: None, + pause_evt: None, tap: Some(tap), avail_features, acked_features: 0u64, config_space, queue_evts: None, interrupt_cb: None, + epoll_thread: None, + paused: Arc::new(AtomicBool::new(false)), }) } @@ -600,16 +627,22 @@ impl VirtioDevice for Net { return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + if let Some(tap) = self.tap.clone() { // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. @@ -637,18 +670,20 @@ impl VirtioDevice for Net { tx: TxVirtio::new(tx_queue, tx_queue_evt), interrupt_cb, kill_evt, + pause_evt, epoll_fd: 0, rx_tap_listening: false, }; - let worker_result = thread::Builder::new() + let paused = self.paused.clone(); + thread::Builder::new() .name("virtio_net".to_string()) - .spawn(move || handler.run()); - - if let Err(e) = worker_result { - error!("failed to spawn virtio_blk worker: {}", e); - return Err(ActivateError::BadActivate); - } + .spawn(move || handler.run(paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone the virtio-net epoll thread: {}", e); + ActivateError::BadActivate + })?; return Ok(()); } @@ -656,6 +691,11 @@ impl VirtioDevice for Net { } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -668,3 +708,7 @@ impl VirtioDevice for Net { )) } } + +virtio_pausable!(Net); +impl Snapshotable for Net {} +impl Migratable for Net {} diff --git a/vm-virtio/src/pmem.rs b/vm-virtio/src/pmem.rs index 940d89626..6c734faf9 100644 --- a/vm-virtio/src/pmem.rs +++ b/vm-virtio/src/pmem.rs @@ -15,6 +15,7 @@ use std::io::{self, Write}; use std::mem::size_of; use std::os::unix::io::AsRawFd; use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; @@ -24,6 +25,7 @@ use super::{ VirtioDeviceType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, }; use crate::{VirtioInterrupt, VirtioInterruptType}; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{ Address, ByteValued, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap, GuestUsize, }; @@ -41,6 +43,8 @@ const VIRTIO_PMEM_RESP_TYPE_EIO: u32 = 1; const QUEUE_AVAIL_EVENT: DeviceEventT = 0; // The device has been dropped. const KILL_EVENT: DeviceEventT = 1; +// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 2; #[derive(Copy, Clone, Debug, Default)] #[repr(C)] @@ -159,6 +163,7 @@ struct PmemEpollHandler { interrupt_cb: Arc, queue_evt: EventFd, kill_evt: EventFd, + pause_evt: EventFd, } impl PmemEpollHandler { @@ -214,7 +219,7 @@ impl PmemEpollHandler { }) } - fn run(&mut self) -> result::Result<(), DeviceError> { + fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; @@ -234,6 +239,14 @@ impl PmemEpollHandler { ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; + const EPOLL_EVENTS_LEN: usize = 100; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; @@ -274,6 +287,15 @@ impl PmemEpollHandler { debug!("kill_evt received, stopping epoll loop"); break 'epoll; } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-pmem epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } _ => { error!("Unknown event for virtio-block"); } @@ -287,12 +309,15 @@ impl PmemEpollHandler { pub struct Pmem { kill_evt: Option, + pause_evt: Option, disk: Option, avail_features: u64, acked_features: u64, config: VirtioPmemConfig, queue_evts: Option>, interrupt_cb: Option>, + epoll_thread: Option>>, + paused: Arc, } impl Pmem { @@ -310,12 +335,15 @@ impl Pmem { Ok(Pmem { kill_evt: None, + pause_evt: None, disk: Some(disk), avail_features, acked_features: 0u64, config, queue_evts: None, interrupt_cb: None, + epoll_thread: None, + paused: Arc::new(AtomicBool::new(false)), }) } } @@ -407,16 +435,22 @@ impl VirtioDevice for Pmem { return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. self.interrupt_cb = Some(interrupt_cb.clone()); @@ -444,16 +478,18 @@ impl VirtioDevice for Pmem { interrupt_cb, queue_evt: queue_evts.remove(0), kill_evt, + pause_evt, }; - let worker_result = thread::Builder::new() + let paused = self.paused.clone(); + thread::Builder::new() .name("virtio_pmem".to_string()) - .spawn(move || handler.run()); - - if let Err(e) = worker_result { - error!("failed to spawn virtio_pmem worker: {}", e); - return Err(ActivateError::BadActivate); - } + .spawn(move || handler.run(paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone virtio-pmem epoll thread: {}", e); + ActivateError::BadActivate + })?; return Ok(()); } @@ -461,6 +497,11 @@ impl VirtioDevice for Pmem { } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -473,3 +514,7 @@ impl VirtioDevice for Pmem { )) } } + +virtio_pausable!(Pmem); +impl Snapshotable for Pmem {} +impl Migratable for Pmem {} diff --git a/vm-virtio/src/rng.rs b/vm-virtio/src/rng.rs index aa0d8ab10..b10c1d8c4 100755 --- a/vm-virtio/src/rng.rs +++ b/vm-virtio/src/rng.rs @@ -9,6 +9,7 @@ use std::fs::File; use std::io; use std::os::unix::io::AsRawFd; use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; @@ -18,6 +19,7 @@ use super::{ VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, }; use crate::{VirtioInterrupt, VirtioInterruptType}; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{Bytes, GuestMemoryMmap}; use vmm_sys_util::eventfd::EventFd; @@ -29,6 +31,8 @@ const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE]; const QUEUE_AVAIL_EVENT: DeviceEventT = 0; // The device has been dropped. const KILL_EVENT: DeviceEventT = 1; +// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 2; struct RngEpollHandler { queues: Vec, @@ -37,6 +41,7 @@ struct RngEpollHandler { interrupt_cb: Arc, queue_evt: EventFd, kill_evt: EventFd, + pause_evt: EventFd, } impl RngEpollHandler { @@ -81,7 +86,7 @@ impl RngEpollHandler { }) } - fn run(&mut self) -> result::Result<(), DeviceError> { + fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; @@ -100,6 +105,13 @@ impl RngEpollHandler { epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; const EPOLL_EVENTS_LEN: usize = 100; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; @@ -141,6 +153,15 @@ impl RngEpollHandler { debug!("KILL_EVENT received, stopping epoll loop"); break 'epoll; } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-rng epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } _ => { error!("Unknown event for virtio-block"); } @@ -155,11 +176,14 @@ impl RngEpollHandler { /// Virtio device for exposing entropy to the guest OS through virtio. pub struct Rng { kill_evt: Option, + pause_evt: Option, random_file: Option, avail_features: u64, acked_features: u64, queue_evts: Option>, interrupt_cb: Option>, + epoll_thread: Option>>, + paused: Arc, } impl Rng { @@ -174,11 +198,14 @@ impl Rng { Ok(Rng { kill_evt: None, + pause_evt: None, random_file: Some(random_file), avail_features, acked_features: 0u64, queue_evts: None, interrupt_cb: None, + epoll_thread: None, + paused: Arc::new(AtomicBool::new(false)), }) } } @@ -259,16 +286,22 @@ impl VirtioDevice for Rng { return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. self.interrupt_cb = Some(interrupt_cb.clone()); @@ -296,16 +329,18 @@ impl VirtioDevice for Rng { interrupt_cb, queue_evt: queue_evts.remove(0), kill_evt, + pause_evt, }; - let worker_result = thread::Builder::new() + let paused = self.paused.clone(); + thread::Builder::new() .name("virtio_rng".to_string()) - .spawn(move || handler.run()); - - if let Err(e) = worker_result { - error!("failed to spawn virtio_rng worker: {}", e); - return Err(ActivateError::BadActivate); - } + .spawn(move || handler.run(paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone the virtio-rng epoll thread: {}", e); + ActivateError::BadActivate + })?; return Ok(()); } @@ -313,6 +348,12 @@ impl VirtioDevice for Rng { } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + + // Then kill it. if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -325,3 +366,7 @@ impl VirtioDevice for Rng { )) } } + +virtio_pausable!(Rng); +impl Snapshotable for Rng {} +impl Migratable for Rng {} diff --git a/vm-virtio/src/transport/mmio.rs b/vm-virtio/src/transport/mmio.rs index 694769200..0c3901837 100644 --- a/vm-virtio/src/transport/mmio.rs +++ b/vm-virtio/src/transport/mmio.rs @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; @@ -15,6 +16,7 @@ use crate::{ INTERRUPT_STATUS_CONFIG_CHANGED, INTERRUPT_STATUS_USED_RING, }; use devices::{BusDevice, Interrupt}; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{GuestAddress, GuestMemoryMmap}; use vmm_sys_util::{errno::Result, eventfd::EventFd}; @@ -275,3 +277,16 @@ impl BusDevice for MmioDevice { } } } + +impl Pausable for MmioDevice { + fn pause(&mut self) -> result::Result<(), MigratableError> { + Ok(()) + } + + fn resume(&mut self) -> result::Result<(), MigratableError> { + Ok(()) + } +} + +impl Snapshotable for MmioDevice {} +impl Migratable for MmioDevice {} diff --git a/vm-virtio/src/transport/pci_device.rs b/vm-virtio/src/transport/pci_device.rs index f40634394..d5e555de8 100755 --- a/vm-virtio/src/transport/pci_device.rs +++ b/vm-virtio/src/transport/pci_device.rs @@ -15,6 +15,7 @@ extern crate vmm_sys_util; use libc::EFD_NONBLOCK; use std::any::Any; +use std::result; use std::sync::atomic::{AtomicU16, AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; @@ -26,6 +27,7 @@ use pci::{ PciMassStorageSubclass, PciNetworkControllerSubclass, PciSubclass, }; use vm_allocator::SystemAllocator; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryMmap, GuestUsize, Le32}; use vmm_sys_util::{errno::Result, eventfd::EventFd}; @@ -762,3 +764,16 @@ impl BusDevice for VirtioPciDevice { self.write_bar(base, offset, data) } } + +impl Pausable for VirtioPciDevice { + fn pause(&mut self) -> result::Result<(), MigratableError> { + Ok(()) + } + + fn resume(&mut self) -> result::Result<(), MigratableError> { + Ok(()) + } +} + +impl Snapshotable for VirtioPciDevice {} +impl Migratable for VirtioPciDevice {} diff --git a/vm-virtio/src/vsock/device.rs b/vm-virtio/src/vsock/device.rs index 0609d7a23..b0061616d 100644 --- a/vm-virtio/src/vsock/device.rs +++ b/vm-virtio/src/vsock/device.rs @@ -33,6 +33,7 @@ use std; use std::io; use std::os::unix::io::AsRawFd; use std::result; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, RwLock}; use std::thread; @@ -44,6 +45,7 @@ use crate::{ VirtioInterruptType, VIRTIO_F_IN_ORDER, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, }; use byteorder::{ByteOrder, LittleEndian}; +use vm_device::{Migratable, MigratableError, Pausable, Snapshotable}; use vm_memory::GuestMemoryMmap; use vmm_sys_util::eventfd::EventFd; @@ -61,7 +63,9 @@ pub const EVT_QUEUE_EVENT: DeviceEventT = 2; pub const BACKEND_EVENT: DeviceEventT = 3; // The device has been dropped. pub const KILL_EVENT: DeviceEventT = 4; -pub const EVENTS_LEN: usize = 5; +// The device should be paused. +const PAUSE_EVENT: DeviceEventT = 5; +pub const EVENTS_LEN: usize = 6; /// The `VsockEpollHandler` implements the runtime logic of our vsock device: /// 1. Respond to TX queue events by wrapping virtio buffers into `VsockPacket`s, then sending those @@ -86,6 +90,7 @@ pub struct VsockEpollHandler { pub queues: Vec, pub queue_evts: Vec, pub kill_evt: EventFd, + pub pause_evt: EventFd, pub interrupt_cb: Arc, pub backend: Arc>, } @@ -188,7 +193,7 @@ where } } - fn run(&mut self) -> result::Result<(), DeviceError> { + fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { // Create the epoll file descriptor let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; @@ -231,6 +236,13 @@ where epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), ) .map_err(DeviceError::EpollCtl)?; + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + self.pause_evt.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, u64::from(PAUSE_EVENT)), + ) + .map_err(DeviceError::EpollCtl)?; let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EVENTS_LEN]; @@ -264,7 +276,7 @@ where let ev_type = event.data as DeviceEventT; - if self.handle_event(ev_type, evset)? { + if self.handle_event(ev_type, evset, paused.clone())? { break 'epoll; } } @@ -277,6 +289,7 @@ where &mut self, device_event: DeviceEventT, evset: epoll::Events, + paused: Arc, ) -> Result { match device_event { RX_QUEUE_EVENT => { @@ -336,6 +349,15 @@ where debug!("KILL_EVENT received, stopping epoll loop"); return Ok(true); } + PAUSE_EVENT => { + debug!("PAUSE_EVENT received, pausing virtio-vsock epoll loop"); + // We loop here to handle spurious park() returns. + // Until we have not resumed, the paused boolean will + // be true. + while paused.load(Ordering::SeqCst) { + thread::park(); + } + } other => { error!("Unknown event for virtio-vsock"); return Err(DeviceError::UnknownEvent { @@ -354,10 +376,13 @@ pub struct Vsock { cid: u64, backend: Arc>, kill_evt: Option, + pause_evt: Option, avail_features: u64, acked_features: u64, queue_evts: Option>, interrupt_cb: Option>, + epoll_thread: Option>>, + paused: Arc, } impl Vsock @@ -377,10 +402,13 @@ where cid, backend: Arc::new(RwLock::new(backend)), kill_evt: None, + pause_evt: None, avail_features, acked_features: 0u64, queue_evts: None, interrupt_cb: None, + epoll_thread: None, + paused: Arc::new(AtomicBool::new(false)), }) } } @@ -482,16 +510,22 @@ where return Err(ActivateError::BadActivate); } - let (self_kill_evt, kill_evt) = - match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) { - Ok(v) => v, - Err(e) => { - error!("failed creating kill EventFd pair: {}", e); - return Err(ActivateError::BadActivate); - } - }; + let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating kill EventFd pair: {}", e); + ActivateError::BadActivate + })?; self.kill_evt = Some(self_kill_evt); + let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) + .and_then(|e| Ok((e.try_clone()?, e))) + .map_err(|e| { + error!("failed creating pause EventFd pair: {}", e); + ActivateError::BadActivate + })?; + self.pause_evt = Some(self_pause_evt); + // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. self.interrupt_cb = Some(interrupt_cb.clone()); @@ -512,23 +546,30 @@ where queues, queue_evts, kill_evt, + pause_evt, interrupt_cb, backend: self.backend.clone(), }; - let worker_result = thread::Builder::new() + let paused = self.paused.clone(); + thread::Builder::new() .name("virtio_vsock".to_string()) - .spawn(move || handler.run()); - - if let Err(e) = worker_result { - error!("failed to spawn virtio_vsock worker: {}", e); - return Err(ActivateError::BadActivate); - } + .spawn(move || handler.run(paused)) + .map(|thread| self.epoll_thread = Some(thread)) + .map_err(|e| { + error!("failed to clone the vsock epoll thread: {}", e); + ActivateError::BadActivate + })?; Ok(()) } fn reset(&mut self) -> Option<(Arc, Vec)> { + // We first must resume the virtio thread if it was paused. + if self.pause_evt.take().is_some() { + self.resume().ok()?; + } + if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); @@ -542,6 +583,16 @@ where } } +impl Pausable for Vsock +where + B: VsockBackend + Sync + 'static, +{ + virtio_pausable_inner!(); +} + +impl Snapshotable for Vsock where B: VsockBackend + Sync + 'static {} +impl Migratable for Vsock where B: VsockBackend + Sync + 'static {} + #[cfg(test)] mod tests { use super::super::tests::TestContext; @@ -730,10 +781,11 @@ mod tests { let test_ctx = TestContext::new(); let mut ctx = test_ctx.create_epoll_handler_context(); - match ctx - .handler - .handle_event(TX_QUEUE_EVENT, epoll::Events::EPOLLIN) - { + match ctx.handler.handle_event( + TX_QUEUE_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) { Err(DeviceError::FailedReadingQueue { .. }) => (), other => panic!("{:?}", other), } @@ -796,10 +848,11 @@ mod tests { let test_ctx = TestContext::new(); let mut ctx = test_ctx.create_epoll_handler_context(); ctx.handler.backend.write().unwrap().set_pending_rx(false); - match ctx - .handler - .handle_event(RX_QUEUE_EVENT, epoll::Events::EPOLLIN) - { + match ctx.handler.handle_event( + RX_QUEUE_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) { Err(DeviceError::FailedReadingQueue { .. }) => (), other => panic!("{:?}", other), } @@ -813,10 +866,11 @@ mod tests { let test_ctx = TestContext::new(); let mut ctx = test_ctx.create_epoll_handler_context(); ctx.handler.backend.write().unwrap().set_pending_rx(false); - match ctx - .handler - .handle_event(EVT_QUEUE_EVENT, epoll::Events::EPOLLIN) - { + match ctx.handler.handle_event( + EVT_QUEUE_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) { Err(DeviceError::FailedReadingQueue { .. }) => (), other => panic!("{:?}", other), } @@ -834,7 +888,11 @@ mod tests { ctx.handler.backend.write().unwrap().set_pending_rx(true); ctx.handler - .handle_event(BACKEND_EVENT, epoll::Events::EPOLLIN) + .handle_event( + BACKEND_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) .unwrap(); // The backend should've received this event. @@ -857,7 +915,11 @@ mod tests { ctx.handler.backend.write().unwrap().set_pending_rx(false); ctx.handler - .handle_event(BACKEND_EVENT, epoll::Events::EPOLLIN) + .handle_event( + BACKEND_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) .unwrap(); // The backend should've received this event. @@ -877,7 +939,11 @@ mod tests { let test_ctx = TestContext::new(); let mut ctx = test_ctx.create_epoll_handler_context(); - match ctx.handler.handle_event(0xff, epoll::Events::EPOLLIN) { + match ctx.handler.handle_event( + 0xff, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) { Err(DeviceError::UnknownEvent { .. }) => (), other => panic!("{:?}", other), } diff --git a/vm-virtio/src/vsock/mod.rs b/vm-virtio/src/vsock/mod.rs index c5c343479..9b7c5c562 100644 --- a/vm-virtio/src/vsock/mod.rs +++ b/vm-virtio/src/vsock/mod.rs @@ -165,6 +165,7 @@ mod tests { use super::*; use std::os::unix::io::AsRawFd; + use std::sync::atomic::AtomicBool; use std::sync::{Arc, RwLock}; use vmm_sys_util::eventfd::EventFd; @@ -306,6 +307,7 @@ mod tests { queues, queue_evts, kill_evt: EventFd::new(EFD_NONBLOCK).unwrap(), + pause_evt: EventFd::new(EFD_NONBLOCK).unwrap(), interrupt_cb, backend: Arc::new(RwLock::new(TestBackend::new())), }, @@ -324,13 +326,21 @@ mod tests { pub fn signal_txq_event(&mut self) { self.handler.queue_evts[1].write(1).unwrap(); self.handler - .handle_event(TX_QUEUE_EVENT, epoll::Events::EPOLLIN) + .handle_event( + TX_QUEUE_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) .unwrap(); } pub fn signal_rxq_event(&mut self) { self.handler.queue_evts[0].write(1).unwrap(); self.handler - .handle_event(RX_QUEUE_EVENT, epoll::Events::EPOLLIN) + .handle_event( + RX_QUEUE_EVENT, + epoll::Events::EPOLLIN, + Arc::new(AtomicBool::new(false)), + ) .unwrap(); } }