mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vm-virtio: Implement the Pausable trait for all virtio devices
Due to the amount of code currently duplicated across virtio devices, the stats for this commit is on the large side but it's mostly more duplicated code, unfortunately. Migratable and Snapshotable placeholder implementations are provided as well, making all virtio devices Migratable. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
+71
-17
@@ -17,6 +17,7 @@ use std::os::linux::fs::MetadataExt;
|
|||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::VirtioInterrupt;
|
use crate::VirtioInterrupt;
|
||||||
use virtio_bindings::bindings::virtio_blk::*;
|
use virtio_bindings::bindings::virtio_blk::*;
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
|
use vm_memory::{Bytes, GuestAddress, GuestMemory, GuestMemoryError, GuestMemoryMmap};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -43,6 +45,8 @@ const QUEUE_AVAIL_EVENT: DeviceEventT = 0;
|
|||||||
pub const KILL_EVENT: DeviceEventT = 1;
|
pub const KILL_EVENT: DeviceEventT = 1;
|
||||||
// Number of DeviceEventT events supported by this implementation.
|
// Number of DeviceEventT events supported by this implementation.
|
||||||
pub const BLOCK_EVENTS_COUNT: usize = 2;
|
pub const BLOCK_EVENTS_COUNT: usize = 2;
|
||||||
|
// The device should be paused.
|
||||||
|
const PAUSE_EVENT: DeviceEventT = 3;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -326,6 +330,8 @@ struct BlockEpollHandler<T: DiskFile> {
|
|||||||
disk_nsectors: u64,
|
disk_nsectors: u64,
|
||||||
interrupt_cb: Arc<VirtioInterrupt>,
|
interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
disk_image_id: Vec<u8>,
|
disk_image_id: Vec<u8>,
|
||||||
|
kill_evt: EventFd,
|
||||||
|
pause_evt: EventFd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DiskFile> BlockEpollHandler<T> {
|
impl<T: DiskFile> BlockEpollHandler<T> {
|
||||||
@@ -399,7 +405,11 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self, queue_evt: EventFd, kill_evt: EventFd) -> result::Result<(), DeviceError> {
|
fn run(
|
||||||
|
&mut self,
|
||||||
|
queue_evt: EventFd,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
|
) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
||||||
|
|
||||||
@@ -414,10 +424,17 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
|||||||
epoll::ctl(
|
epoll::ctl(
|
||||||
epoll_fd,
|
epoll_fd,
|
||||||
epoll::ControlOptions::EPOLL_CTL_ADD,
|
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)),
|
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
||||||
@@ -459,6 +476,15 @@ impl<T: DiskFile> BlockEpollHandler<T> {
|
|||||||
debug!("KILL_EVENT received, stopping epoll loop");
|
debug!("KILL_EVENT received, stopping epoll loop");
|
||||||
break 'epoll;
|
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");
|
error!("Unknown event for virtio-block");
|
||||||
}
|
}
|
||||||
@@ -481,6 +507,9 @@ pub struct Block<T: DiskFile> {
|
|||||||
config_space: Vec<u8>,
|
config_space: Vec<u8>,
|
||||||
queue_evt: Option<EventFd>,
|
queue_evt: Option<EventFd>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_config_space(disk_size: u64) -> Vec<u8> {
|
pub fn build_config_space(disk_size: u64) -> Vec<u8> {
|
||||||
@@ -534,6 +563,9 @@ impl<T: DiskFile> Block<T> {
|
|||||||
config_space: build_config_space(disk_size),
|
config_space: build_config_space(disk_size),
|
||||||
queue_evt: None,
|
queue_evt: None,
|
||||||
interrupt_cb: None,
|
interrupt_cb: None,
|
||||||
|
epoll_thread: None,
|
||||||
|
pause_evt: None,
|
||||||
|
paused: Arc::new(AtomicBool::new(false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -630,16 +662,23 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
|||||||
return Err(ActivateError::BadActivate);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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() {
|
if let Some(disk_image) = self.disk_image.clone() {
|
||||||
let disk_image_id = build_disk_image_id(&self.disk_path);
|
let disk_image_id = build_disk_image_id(&self.disk_path);
|
||||||
|
|
||||||
@@ -663,16 +702,19 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
|||||||
disk_nsectors: self.disk_nsectors,
|
disk_nsectors: self.disk_nsectors,
|
||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
disk_image_id,
|
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())
|
.name("virtio_blk".to_string())
|
||||||
.spawn(move || handler.run(queue_evt, kill_evt));
|
.spawn(move || handler.run(queue_evt, paused))
|
||||||
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
if let Err(e) = worker_result {
|
.map_err(|e| {
|
||||||
error!("failed to spawn virtio_blk worker: {}", e);
|
error!("failed to clone the virtio-blk epoll thread: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
ActivateError::BadActivate
|
||||||
}
|
})?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -680,6 +722,11 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -692,3 +739,10 @@ impl<T: 'static + DiskFile + Send> VirtioDevice for Block<T> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: 'static + DiskFile + Send> Pausable for Block<T> {
|
||||||
|
virtio_pausable_inner!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static + DiskFile + Send> Snapshotable for Block<T> {}
|
||||||
|
impl<T: 'static + DiskFile + Send> Migratable for Block<T> {}
|
||||||
|
|||||||
+61
-17
@@ -20,7 +20,8 @@ use super::{
|
|||||||
VirtioInterruptType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
VirtioInterruptType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||||
};
|
};
|
||||||
use crate::VirtioInterrupt;
|
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 vm_memory::{ByteValued, Bytes, GuestMemoryMmap};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -37,6 +38,8 @@ const INPUT_EVENT: DeviceEventT = 2;
|
|||||||
const KILL_EVENT: DeviceEventT = 3;
|
const KILL_EVENT: DeviceEventT = 3;
|
||||||
// Console configuration change event is triggered.
|
// Console configuration change event is triggered.
|
||||||
const CONFIG_EVENT: DeviceEventT = 4;
|
const CONFIG_EVENT: DeviceEventT = 4;
|
||||||
|
// The device should be paused.
|
||||||
|
const PAUSE_EVENT: DeviceEventT = 5;
|
||||||
|
|
||||||
//Console size feature bit
|
//Console size feature bit
|
||||||
const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
|
const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
|
||||||
@@ -64,6 +67,7 @@ struct ConsoleEpollHandler {
|
|||||||
input_evt: EventFd,
|
input_evt: EventFd,
|
||||||
config_evt: EventFd,
|
config_evt: EventFd,
|
||||||
kill_evt: EventFd,
|
kill_evt: EventFd,
|
||||||
|
pause_evt: EventFd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConsoleEpollHandler {
|
impl ConsoleEpollHandler {
|
||||||
@@ -157,7 +161,7 @@ impl ConsoleEpollHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> result::Result<(), DeviceError> {
|
fn run(&mut self, paused: Arc<AtomicBool>) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
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)),
|
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
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");
|
debug!("KILL_EVENT received, stopping epoll loop");
|
||||||
break 'epoll;
|
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");
|
error!("Unknown event for virtio-console");
|
||||||
}
|
}
|
||||||
@@ -323,6 +343,7 @@ impl VirtioConsoleConfig {
|
|||||||
/// Virtio device for exposing console to the guest OS through virtio.
|
/// Virtio device for exposing console to the guest OS through virtio.
|
||||||
pub struct Console {
|
pub struct Console {
|
||||||
kill_evt: Option<EventFd>,
|
kill_evt: Option<EventFd>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
config: Arc<Mutex<VirtioConsoleConfig>>,
|
config: Arc<Mutex<VirtioConsoleConfig>>,
|
||||||
@@ -330,6 +351,8 @@ pub struct Console {
|
|||||||
out: Arc<Mutex<Box<dyn io::Write + Send + Sync + 'static>>>,
|
out: Arc<Mutex<Box<dyn io::Write + Send + Sync + 'static>>>,
|
||||||
queue_evts: Option<Vec<EventFd>>,
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Console {
|
impl Console {
|
||||||
@@ -360,6 +383,7 @@ impl Console {
|
|||||||
Ok((
|
Ok((
|
||||||
Console {
|
Console {
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
|
pause_evt: None,
|
||||||
avail_features,
|
avail_features,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
config: console_config,
|
config: console_config,
|
||||||
@@ -367,6 +391,8 @@ impl Console {
|
|||||||
out: Arc::new(Mutex::new(out)),
|
out: Arc::new(Mutex::new(out)),
|
||||||
queue_evts: None,
|
queue_evts: None,
|
||||||
interrupt_cb: None,
|
interrupt_cb: None,
|
||||||
|
epoll_thread: None,
|
||||||
|
paused: Arc::new(AtomicBool::new(false)),
|
||||||
},
|
},
|
||||||
console_input,
|
console_input,
|
||||||
))
|
))
|
||||||
@@ -461,16 +487,23 @@ impl VirtioDevice for Console {
|
|||||||
return Err(ActivateError::BadActivate);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
self.interrupt_cb = Some(interrupt_cb.clone());
|
self.interrupt_cb = Some(interrupt_cb.clone());
|
||||||
@@ -507,21 +540,28 @@ impl VirtioDevice for Console {
|
|||||||
input_evt: self.input.input_evt.try_clone().unwrap(),
|
input_evt: self.input.input_evt.try_clone().unwrap(),
|
||||||
config_evt: self.input.config_evt.try_clone().unwrap(),
|
config_evt: self.input.config_evt.try_clone().unwrap(),
|
||||||
kill_evt,
|
kill_evt,
|
||||||
|
pause_evt,
|
||||||
};
|
};
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let paused = self.paused.clone();
|
||||||
|
thread::Builder::new()
|
||||||
.name("virtio_console".to_string())
|
.name("virtio_console".to_string())
|
||||||
.spawn(move || handler.run());
|
.spawn(move || handler.run(paused))
|
||||||
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
if let Err(e) = worker_result {
|
.map_err(|e| {
|
||||||
error!("failed to spawn virtio_console worker: {}", e);
|
error!("failed to clone the virtio-console epoll thread: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
ActivateError::BadActivate
|
||||||
}
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -534,3 +574,7 @@ impl VirtioDevice for Console {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtio_pausable!(Console);
|
||||||
|
impl Snapshotable for Console {}
|
||||||
|
impl Migratable for Console {}
|
||||||
|
|||||||
@@ -101,3 +101,45 @@ pub trait VirtioDevice: Send {
|
|||||||
pub trait DmaRemapping: Send + Sync {
|
pub trait DmaRemapping: Send + Sync {
|
||||||
fn translate(&self, id: u32, addr: u64) -> std::result::Result<u64, std::io::Error>;
|
fn translate(&self, id: u32, addr: u64) -> std::result::Result<u64, std::io::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+61
-18
@@ -12,6 +12,7 @@ use std::mem::size_of;
|
|||||||
use std::ops::Bound::Included;
|
use std::ops::Bound::Included;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ use super::{
|
|||||||
VirtioDeviceType, VIRTIO_F_VERSION_1,
|
VirtioDeviceType, VIRTIO_F_VERSION_1,
|
||||||
};
|
};
|
||||||
use crate::{DmaRemapping, VirtioInterrupt, VirtioInterruptType};
|
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 vm_memory::{Address, ByteValued, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -40,6 +41,8 @@ const REQUEST_Q_EVENT: DeviceEventT = 0;
|
|||||||
const EVENT_Q_EVENT: DeviceEventT = 1;
|
const EVENT_Q_EVENT: DeviceEventT = 1;
|
||||||
/// The device has been dropped.
|
/// The device has been dropped.
|
||||||
const KILL_EVENT: DeviceEventT = 2;
|
const KILL_EVENT: DeviceEventT = 2;
|
||||||
|
/// The device should be paused.
|
||||||
|
const PAUSE_EVENT: DeviceEventT = 3;
|
||||||
|
|
||||||
/// Virtio IOMMU features
|
/// Virtio IOMMU features
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
@@ -532,6 +535,7 @@ struct IommuEpollHandler {
|
|||||||
interrupt_cb: Arc<VirtioInterrupt>,
|
interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
queue_evts: Vec<EventFd>,
|
queue_evts: Vec<EventFd>,
|
||||||
kill_evt: EventFd,
|
kill_evt: EventFd,
|
||||||
|
pause_evt: EventFd,
|
||||||
mapping: Arc<IommuMapping>,
|
mapping: Arc<IommuMapping>,
|
||||||
ext_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
ext_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||||
ext_domain_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
ext_domain_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||||
@@ -591,7 +595,7 @@ impl IommuEpollHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> result::Result<(), DeviceError> {
|
fn run(&mut self, paused: Arc<AtomicBool>) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
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)),
|
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
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");
|
debug!("kill_evt received, stopping epoll loop");
|
||||||
break 'epoll;
|
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");
|
error!("Unknown event for virtio-iommu");
|
||||||
break 'epoll;
|
break 'epoll;
|
||||||
@@ -723,6 +743,7 @@ impl DmaRemapping for IommuMapping {
|
|||||||
|
|
||||||
pub struct Iommu {
|
pub struct Iommu {
|
||||||
kill_evt: Option<EventFd>,
|
kill_evt: Option<EventFd>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
config: VirtioIommuConfig,
|
config: VirtioIommuConfig,
|
||||||
@@ -730,6 +751,8 @@ pub struct Iommu {
|
|||||||
ext_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
ext_mapping: BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
|
||||||
queue_evts: Option<Vec<EventFd>>,
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iommu {
|
impl Iommu {
|
||||||
@@ -747,6 +770,7 @@ impl Iommu {
|
|||||||
Ok((
|
Ok((
|
||||||
Iommu {
|
Iommu {
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
|
pause_evt: None,
|
||||||
avail_features: 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP,
|
avail_features: 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_IOMMU_F_MAP_UNMAP,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
config,
|
config,
|
||||||
@@ -754,6 +778,8 @@ impl Iommu {
|
|||||||
ext_mapping: BTreeMap::new(),
|
ext_mapping: BTreeMap::new(),
|
||||||
queue_evts: None,
|
queue_evts: None,
|
||||||
interrupt_cb: None,
|
interrupt_cb: None,
|
||||||
|
epoll_thread: None,
|
||||||
|
paused: Arc::new(AtomicBool::new(false)),
|
||||||
},
|
},
|
||||||
mapping,
|
mapping,
|
||||||
))
|
))
|
||||||
@@ -851,16 +877,22 @@ impl VirtioDevice for Iommu {
|
|||||||
return Err(ActivateError::BadActivate);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
self.interrupt_cb = Some(interrupt_cb.clone());
|
self.interrupt_cb = Some(interrupt_cb.clone());
|
||||||
@@ -882,24 +914,31 @@ impl VirtioDevice for Iommu {
|
|||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
queue_evts,
|
queue_evts,
|
||||||
kill_evt,
|
kill_evt,
|
||||||
|
pause_evt,
|
||||||
mapping: self.mapping.clone(),
|
mapping: self.mapping.clone(),
|
||||||
ext_mapping: self.ext_mapping.clone(),
|
ext_mapping: self.ext_mapping.clone(),
|
||||||
ext_domain_mapping: BTreeMap::new(),
|
ext_domain_mapping: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let paused = self.paused.clone();
|
||||||
.name("virtio-iommu".to_string())
|
thread::Builder::new()
|
||||||
.spawn(move || handler.run());
|
.name("virtio_iommu".to_string())
|
||||||
|
.spawn(move || handler.run(paused))
|
||||||
if let Err(e) = worker_result {
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
error!("failed to spawn virtio-iommu worker: {}", e);
|
.map_err(|e| {
|
||||||
return Err(ActivateError::BadActivate);
|
error!("failed to clone the virtio-iommu epoll thread: {}", e);
|
||||||
}
|
ActivateError::BadActivate
|
||||||
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -912,3 +951,7 @@ impl VirtioDevice for Iommu {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtio_pausable!(Iommu);
|
||||||
|
impl Snapshotable for Iommu {}
|
||||||
|
impl Migratable for Iommu {}
|
||||||
|
|||||||
@@ -16,14 +16,16 @@ extern crate log;
|
|||||||
extern crate pci;
|
extern crate pci;
|
||||||
extern crate vhost_rs;
|
extern crate vhost_rs;
|
||||||
extern crate virtio_bindings;
|
extern crate virtio_bindings;
|
||||||
|
extern crate vm_device;
|
||||||
extern crate vm_memory;
|
extern crate vm_memory;
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod device;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
mod console;
|
mod console;
|
||||||
mod device;
|
|
||||||
mod iommu;
|
mod iommu;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
mod pmem;
|
mod pmem;
|
||||||
|
|||||||
+60
-16
@@ -15,6 +15,7 @@ use std::mem;
|
|||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
@@ -29,6 +30,7 @@ use super::{
|
|||||||
use crate::VirtioInterrupt;
|
use crate::VirtioInterrupt;
|
||||||
use net_util::{MacAddr, Tap, TapError, MAC_ADDR_LEN};
|
use net_util::{MacAddr, Tap, TapError, MAC_ADDR_LEN};
|
||||||
use virtio_bindings::bindings::virtio_net::*;
|
use virtio_bindings::bindings::virtio_net::*;
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -50,6 +52,8 @@ const TX_QUEUE_EVENT: DeviceEventT = 2;
|
|||||||
pub const KILL_EVENT: DeviceEventT = 3;
|
pub const KILL_EVENT: DeviceEventT = 3;
|
||||||
// Number of DeviceEventT events supported by this implementation.
|
// Number of DeviceEventT events supported by this implementation.
|
||||||
pub const NET_EVENTS_COUNT: usize = 4;
|
pub const NET_EVENTS_COUNT: usize = 4;
|
||||||
|
// The device should be paused.
|
||||||
|
const PAUSE_EVENT: DeviceEventT = 5;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -121,6 +125,7 @@ struct NetEpollHandler {
|
|||||||
tx: TxVirtio,
|
tx: TxVirtio,
|
||||||
interrupt_cb: Arc<VirtioInterrupt>,
|
interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
kill_evt: EventFd,
|
kill_evt: EventFd,
|
||||||
|
pause_evt: EventFd,
|
||||||
epoll_fd: RawFd,
|
epoll_fd: RawFd,
|
||||||
rx_tap_listening: bool,
|
rx_tap_listening: bool,
|
||||||
}
|
}
|
||||||
@@ -323,7 +328,7 @@ impl NetEpollHandler {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> result::Result<(), DeviceError> {
|
fn run(&mut self, paused: Arc<AtomicBool>) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
self.epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
self.epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
||||||
// Add events
|
// Add events
|
||||||
@@ -351,6 +356,13 @@ impl NetEpollHandler {
|
|||||||
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
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");
|
debug!("KILL_EVENT received, stopping epoll loop");
|
||||||
break 'epoll;
|
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");
|
error!("Unknown event for virtio-net");
|
||||||
}
|
}
|
||||||
@@ -433,6 +454,7 @@ impl NetEpollHandler {
|
|||||||
|
|
||||||
pub struct Net {
|
pub struct Net {
|
||||||
kill_evt: Option<EventFd>,
|
kill_evt: Option<EventFd>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
tap: Option<Tap>,
|
tap: Option<Tap>,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
@@ -441,6 +463,8 @@ pub struct Net {
|
|||||||
config_space: Vec<u8>,
|
config_space: Vec<u8>,
|
||||||
queue_evts: Option<Vec<EventFd>>,
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Net {
|
impl Net {
|
||||||
@@ -483,12 +507,15 @@ impl Net {
|
|||||||
|
|
||||||
Ok(Net {
|
Ok(Net {
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
|
pause_evt: None,
|
||||||
tap: Some(tap),
|
tap: Some(tap),
|
||||||
avail_features,
|
avail_features,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
config_space,
|
config_space,
|
||||||
queue_evts: None,
|
queue_evts: None,
|
||||||
interrupt_cb: 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);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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() {
|
if let Some(tap) = self.tap.clone() {
|
||||||
// Save the interrupt EventFD as we need to return it on reset
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
@@ -637,18 +670,20 @@ impl VirtioDevice for Net {
|
|||||||
tx: TxVirtio::new(tx_queue, tx_queue_evt),
|
tx: TxVirtio::new(tx_queue, tx_queue_evt),
|
||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
kill_evt,
|
kill_evt,
|
||||||
|
pause_evt,
|
||||||
epoll_fd: 0,
|
epoll_fd: 0,
|
||||||
rx_tap_listening: false,
|
rx_tap_listening: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let paused = self.paused.clone();
|
||||||
|
thread::Builder::new()
|
||||||
.name("virtio_net".to_string())
|
.name("virtio_net".to_string())
|
||||||
.spawn(move || handler.run());
|
.spawn(move || handler.run(paused))
|
||||||
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
if let Err(e) = worker_result {
|
.map_err(|e| {
|
||||||
error!("failed to spawn virtio_blk worker: {}", e);
|
error!("failed to clone the virtio-net epoll thread: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
ActivateError::BadActivate
|
||||||
}
|
})?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -656,6 +691,11 @@ impl VirtioDevice for Net {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -668,3 +708,7 @@ impl VirtioDevice for Net {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtio_pausable!(Net);
|
||||||
|
impl Snapshotable for Net {}
|
||||||
|
impl Migratable for Net {}
|
||||||
|
|||||||
+61
-16
@@ -15,6 +15,7 @@ use std::io::{self, Write};
|
|||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ use super::{
|
|||||||
VirtioDeviceType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
VirtioDeviceType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||||
};
|
};
|
||||||
use crate::{VirtioInterrupt, VirtioInterruptType};
|
use crate::{VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::{
|
use vm_memory::{
|
||||||
Address, ByteValued, Bytes, GuestAddress, GuestMemoryError, GuestMemoryMmap, GuestUsize,
|
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;
|
const QUEUE_AVAIL_EVENT: DeviceEventT = 0;
|
||||||
// The device has been dropped.
|
// The device has been dropped.
|
||||||
const KILL_EVENT: DeviceEventT = 1;
|
const KILL_EVENT: DeviceEventT = 1;
|
||||||
|
// The device should be paused.
|
||||||
|
const PAUSE_EVENT: DeviceEventT = 2;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@@ -159,6 +163,7 @@ struct PmemEpollHandler {
|
|||||||
interrupt_cb: Arc<VirtioInterrupt>,
|
interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
queue_evt: EventFd,
|
queue_evt: EventFd,
|
||||||
kill_evt: EventFd,
|
kill_evt: EventFd,
|
||||||
|
pause_evt: EventFd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PmemEpollHandler {
|
impl PmemEpollHandler {
|
||||||
@@ -214,7 +219,7 @@ impl PmemEpollHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> result::Result<(), DeviceError> {
|
fn run(&mut self, paused: Arc<AtomicBool>) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
||||||
|
|
||||||
@@ -234,6 +239,14 @@ impl PmemEpollHandler {
|
|||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
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");
|
debug!("kill_evt received, stopping epoll loop");
|
||||||
break 'epoll;
|
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");
|
error!("Unknown event for virtio-block");
|
||||||
}
|
}
|
||||||
@@ -287,12 +309,15 @@ impl PmemEpollHandler {
|
|||||||
|
|
||||||
pub struct Pmem {
|
pub struct Pmem {
|
||||||
kill_evt: Option<EventFd>,
|
kill_evt: Option<EventFd>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
disk: Option<File>,
|
disk: Option<File>,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
config: VirtioPmemConfig,
|
config: VirtioPmemConfig,
|
||||||
queue_evts: Option<Vec<EventFd>>,
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pmem {
|
impl Pmem {
|
||||||
@@ -310,12 +335,15 @@ impl Pmem {
|
|||||||
|
|
||||||
Ok(Pmem {
|
Ok(Pmem {
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
|
pause_evt: None,
|
||||||
disk: Some(disk),
|
disk: Some(disk),
|
||||||
avail_features,
|
avail_features,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
config,
|
config,
|
||||||
queue_evts: None,
|
queue_evts: None,
|
||||||
interrupt_cb: 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);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
self.interrupt_cb = Some(interrupt_cb.clone());
|
self.interrupt_cb = Some(interrupt_cb.clone());
|
||||||
@@ -444,16 +478,18 @@ impl VirtioDevice for Pmem {
|
|||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
queue_evt: queue_evts.remove(0),
|
queue_evt: queue_evts.remove(0),
|
||||||
kill_evt,
|
kill_evt,
|
||||||
|
pause_evt,
|
||||||
};
|
};
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let paused = self.paused.clone();
|
||||||
|
thread::Builder::new()
|
||||||
.name("virtio_pmem".to_string())
|
.name("virtio_pmem".to_string())
|
||||||
.spawn(move || handler.run());
|
.spawn(move || handler.run(paused))
|
||||||
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
if let Err(e) = worker_result {
|
.map_err(|e| {
|
||||||
error!("failed to spawn virtio_pmem worker: {}", e);
|
error!("failed to clone virtio-pmem epoll thread: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
ActivateError::BadActivate
|
||||||
}
|
})?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -461,6 +497,11 @@ impl VirtioDevice for Pmem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -473,3 +514,7 @@ impl VirtioDevice for Pmem {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtio_pausable!(Pmem);
|
||||||
|
impl Snapshotable for Pmem {}
|
||||||
|
impl Migratable for Pmem {}
|
||||||
|
|||||||
+61
-16
@@ -9,6 +9,7 @@ use std::fs::File;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ use super::{
|
|||||||
VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||||
};
|
};
|
||||||
use crate::{VirtioInterrupt, VirtioInterruptType};
|
use crate::{VirtioInterrupt, VirtioInterruptType};
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::{Bytes, GuestMemoryMmap};
|
use vm_memory::{Bytes, GuestMemoryMmap};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -29,6 +31,8 @@ const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
|
|||||||
const QUEUE_AVAIL_EVENT: DeviceEventT = 0;
|
const QUEUE_AVAIL_EVENT: DeviceEventT = 0;
|
||||||
// The device has been dropped.
|
// The device has been dropped.
|
||||||
const KILL_EVENT: DeviceEventT = 1;
|
const KILL_EVENT: DeviceEventT = 1;
|
||||||
|
// The device should be paused.
|
||||||
|
const PAUSE_EVENT: DeviceEventT = 2;
|
||||||
|
|
||||||
struct RngEpollHandler {
|
struct RngEpollHandler {
|
||||||
queues: Vec<Queue>,
|
queues: Vec<Queue>,
|
||||||
@@ -37,6 +41,7 @@ struct RngEpollHandler {
|
|||||||
interrupt_cb: Arc<VirtioInterrupt>,
|
interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
queue_evt: EventFd,
|
queue_evt: EventFd,
|
||||||
kill_evt: EventFd,
|
kill_evt: EventFd,
|
||||||
|
pause_evt: EventFd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RngEpollHandler {
|
impl RngEpollHandler {
|
||||||
@@ -81,7 +86,7 @@ impl RngEpollHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> result::Result<(), DeviceError> {
|
fn run(&mut self, paused: Arc<AtomicBool>) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
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)),
|
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
|
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");
|
debug!("KILL_EVENT received, stopping epoll loop");
|
||||||
break 'epoll;
|
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");
|
error!("Unknown event for virtio-block");
|
||||||
}
|
}
|
||||||
@@ -155,11 +176,14 @@ impl RngEpollHandler {
|
|||||||
/// Virtio device for exposing entropy to the guest OS through virtio.
|
/// Virtio device for exposing entropy to the guest OS through virtio.
|
||||||
pub struct Rng {
|
pub struct Rng {
|
||||||
kill_evt: Option<EventFd>,
|
kill_evt: Option<EventFd>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
random_file: Option<File>,
|
random_file: Option<File>,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
queue_evts: Option<Vec<EventFd>>,
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rng {
|
impl Rng {
|
||||||
@@ -174,11 +198,14 @@ impl Rng {
|
|||||||
|
|
||||||
Ok(Rng {
|
Ok(Rng {
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
|
pause_evt: None,
|
||||||
random_file: Some(random_file),
|
random_file: Some(random_file),
|
||||||
avail_features,
|
avail_features,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
queue_evts: None,
|
queue_evts: None,
|
||||||
interrupt_cb: 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);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
self.interrupt_cb = Some(interrupt_cb.clone());
|
self.interrupt_cb = Some(interrupt_cb.clone());
|
||||||
@@ -296,16 +329,18 @@ impl VirtioDevice for Rng {
|
|||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
queue_evt: queue_evts.remove(0),
|
queue_evt: queue_evts.remove(0),
|
||||||
kill_evt,
|
kill_evt,
|
||||||
|
pause_evt,
|
||||||
};
|
};
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let paused = self.paused.clone();
|
||||||
|
thread::Builder::new()
|
||||||
.name("virtio_rng".to_string())
|
.name("virtio_rng".to_string())
|
||||||
.spawn(move || handler.run());
|
.spawn(move || handler.run(paused))
|
||||||
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
if let Err(e) = worker_result {
|
.map_err(|e| {
|
||||||
error!("failed to spawn virtio_rng worker: {}", e);
|
error!("failed to clone the virtio-rng epoll thread: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
ActivateError::BadActivate
|
||||||
}
|
})?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -313,6 +348,12 @@ impl VirtioDevice for Rng {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -325,3 +366,7 @@ impl VirtioDevice for Rng {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtio_pausable!(Rng);
|
||||||
|
impl Snapshotable for Rng {}
|
||||||
|
impl Migratable for Rng {}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
use std::result;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ use crate::{
|
|||||||
INTERRUPT_STATUS_CONFIG_CHANGED, INTERRUPT_STATUS_USED_RING,
|
INTERRUPT_STATUS_CONFIG_CHANGED, INTERRUPT_STATUS_USED_RING,
|
||||||
};
|
};
|
||||||
use devices::{BusDevice, Interrupt};
|
use devices::{BusDevice, Interrupt};
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
use vm_memory::{GuestAddress, GuestMemoryMmap};
|
||||||
use vmm_sys_util::{errno::Result, eventfd::EventFd};
|
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 {}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ extern crate vmm_sys_util;
|
|||||||
|
|
||||||
use libc::EFD_NONBLOCK;
|
use libc::EFD_NONBLOCK;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::result;
|
||||||
use std::sync::atomic::{AtomicU16, AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicU16, AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ use pci::{
|
|||||||
PciMassStorageSubclass, PciNetworkControllerSubclass, PciSubclass,
|
PciMassStorageSubclass, PciNetworkControllerSubclass, PciSubclass,
|
||||||
};
|
};
|
||||||
use vm_allocator::SystemAllocator;
|
use vm_allocator::SystemAllocator;
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryMmap, GuestUsize, Le32};
|
use vm_memory::{Address, ByteValued, GuestAddress, GuestMemoryMmap, GuestUsize, Le32};
|
||||||
use vmm_sys_util::{errno::Result, eventfd::EventFd};
|
use vmm_sys_util::{errno::Result, eventfd::EventFd};
|
||||||
|
|
||||||
@@ -762,3 +764,16 @@ impl BusDevice for VirtioPciDevice {
|
|||||||
self.write_bar(base, offset, data)
|
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 {}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use std;
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ use crate::{
|
|||||||
VirtioInterruptType, VIRTIO_F_IN_ORDER, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
VirtioInterruptType, VIRTIO_F_IN_ORDER, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
|
||||||
};
|
};
|
||||||
use byteorder::{ByteOrder, LittleEndian};
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
|
use vm_device::{Migratable, MigratableError, Pausable, Snapshotable};
|
||||||
use vm_memory::GuestMemoryMmap;
|
use vm_memory::GuestMemoryMmap;
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -61,7 +63,9 @@ pub const EVT_QUEUE_EVENT: DeviceEventT = 2;
|
|||||||
pub const BACKEND_EVENT: DeviceEventT = 3;
|
pub const BACKEND_EVENT: DeviceEventT = 3;
|
||||||
// The device has been dropped.
|
// The device has been dropped.
|
||||||
pub const KILL_EVENT: DeviceEventT = 4;
|
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:
|
/// 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
|
/// 1. Respond to TX queue events by wrapping virtio buffers into `VsockPacket`s, then sending those
|
||||||
@@ -86,6 +90,7 @@ pub struct VsockEpollHandler<B: VsockBackend> {
|
|||||||
pub queues: Vec<Queue>,
|
pub queues: Vec<Queue>,
|
||||||
pub queue_evts: Vec<EventFd>,
|
pub queue_evts: Vec<EventFd>,
|
||||||
pub kill_evt: EventFd,
|
pub kill_evt: EventFd,
|
||||||
|
pub pause_evt: EventFd,
|
||||||
pub interrupt_cb: Arc<VirtioInterrupt>,
|
pub interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
pub backend: Arc<RwLock<B>>,
|
pub backend: Arc<RwLock<B>>,
|
||||||
}
|
}
|
||||||
@@ -188,7 +193,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> result::Result<(), DeviceError> {
|
fn run(&mut self, paused: Arc<AtomicBool>) -> result::Result<(), DeviceError> {
|
||||||
// Create the epoll file descriptor
|
// Create the epoll file descriptor
|
||||||
let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?;
|
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)),
|
epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)),
|
||||||
)
|
)
|
||||||
.map_err(DeviceError::EpollCtl)?;
|
.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];
|
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;
|
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;
|
break 'epoll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,6 +289,7 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
device_event: DeviceEventT,
|
device_event: DeviceEventT,
|
||||||
evset: epoll::Events,
|
evset: epoll::Events,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
) -> Result<bool, DeviceError> {
|
) -> Result<bool, DeviceError> {
|
||||||
match device_event {
|
match device_event {
|
||||||
RX_QUEUE_EVENT => {
|
RX_QUEUE_EVENT => {
|
||||||
@@ -336,6 +349,15 @@ where
|
|||||||
debug!("KILL_EVENT received, stopping epoll loop");
|
debug!("KILL_EVENT received, stopping epoll loop");
|
||||||
return Ok(true);
|
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 => {
|
other => {
|
||||||
error!("Unknown event for virtio-vsock");
|
error!("Unknown event for virtio-vsock");
|
||||||
return Err(DeviceError::UnknownEvent {
|
return Err(DeviceError::UnknownEvent {
|
||||||
@@ -354,10 +376,13 @@ pub struct Vsock<B: VsockBackend> {
|
|||||||
cid: u64,
|
cid: u64,
|
||||||
backend: Arc<RwLock<B>>,
|
backend: Arc<RwLock<B>>,
|
||||||
kill_evt: Option<EventFd>,
|
kill_evt: Option<EventFd>,
|
||||||
|
pause_evt: Option<EventFd>,
|
||||||
avail_features: u64,
|
avail_features: u64,
|
||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
queue_evts: Option<Vec<EventFd>>,
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
|
epoll_thread: Option<thread::JoinHandle<result::Result<(), DeviceError>>>,
|
||||||
|
paused: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B> Vsock<B>
|
impl<B> Vsock<B>
|
||||||
@@ -377,10 +402,13 @@ where
|
|||||||
cid,
|
cid,
|
||||||
backend: Arc::new(RwLock::new(backend)),
|
backend: Arc::new(RwLock::new(backend)),
|
||||||
kill_evt: None,
|
kill_evt: None,
|
||||||
|
pause_evt: None,
|
||||||
avail_features,
|
avail_features,
|
||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
queue_evts: None,
|
queue_evts: None,
|
||||||
interrupt_cb: None,
|
interrupt_cb: None,
|
||||||
|
epoll_thread: None,
|
||||||
|
paused: Arc::new(AtomicBool::new(false)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -482,16 +510,22 @@ where
|
|||||||
return Err(ActivateError::BadActivate);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (self_kill_evt, kill_evt) =
|
let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK)
|
||||||
match EventFd::new(EFD_NONBLOCK).and_then(|e| Ok((e.try_clone()?, e))) {
|
.and_then(|e| Ok((e.try_clone()?, e)))
|
||||||
Ok(v) => v,
|
.map_err(|e| {
|
||||||
Err(e) => {
|
error!("failed creating kill EventFd pair: {}", e);
|
||||||
error!("failed creating kill EventFd pair: {}", e);
|
ActivateError::BadActivate
|
||||||
return Err(ActivateError::BadActivate);
|
})?;
|
||||||
}
|
|
||||||
};
|
|
||||||
self.kill_evt = Some(self_kill_evt);
|
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
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
// but clone it to pass into the thread.
|
// but clone it to pass into the thread.
|
||||||
self.interrupt_cb = Some(interrupt_cb.clone());
|
self.interrupt_cb = Some(interrupt_cb.clone());
|
||||||
@@ -512,23 +546,30 @@ where
|
|||||||
queues,
|
queues,
|
||||||
queue_evts,
|
queue_evts,
|
||||||
kill_evt,
|
kill_evt,
|
||||||
|
pause_evt,
|
||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
backend: self.backend.clone(),
|
backend: self.backend.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let paused = self.paused.clone();
|
||||||
|
thread::Builder::new()
|
||||||
.name("virtio_vsock".to_string())
|
.name("virtio_vsock".to_string())
|
||||||
.spawn(move || handler.run());
|
.spawn(move || handler.run(paused))
|
||||||
|
.map(|thread| self.epoll_thread = Some(thread))
|
||||||
if let Err(e) = worker_result {
|
.map_err(|e| {
|
||||||
error!("failed to spawn virtio_vsock worker: {}", e);
|
error!("failed to clone the vsock epoll thread: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
ActivateError::BadActivate
|
||||||
}
|
})?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
// 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() {
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
// Ignore the result because there is nothing we can do about it.
|
// Ignore the result because there is nothing we can do about it.
|
||||||
let _ = kill_evt.write(1);
|
let _ = kill_evt.write(1);
|
||||||
@@ -542,6 +583,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<B> Pausable for Vsock<B>
|
||||||
|
where
|
||||||
|
B: VsockBackend + Sync + 'static,
|
||||||
|
{
|
||||||
|
virtio_pausable_inner!();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> Snapshotable for Vsock<B> where B: VsockBackend + Sync + 'static {}
|
||||||
|
impl<B> Migratable for Vsock<B> where B: VsockBackend + Sync + 'static {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::super::tests::TestContext;
|
use super::super::tests::TestContext;
|
||||||
@@ -730,10 +781,11 @@ mod tests {
|
|||||||
let test_ctx = TestContext::new();
|
let test_ctx = TestContext::new();
|
||||||
let mut ctx = test_ctx.create_epoll_handler_context();
|
let mut ctx = test_ctx.create_epoll_handler_context();
|
||||||
|
|
||||||
match ctx
|
match ctx.handler.handle_event(
|
||||||
.handler
|
TX_QUEUE_EVENT,
|
||||||
.handle_event(TX_QUEUE_EVENT, epoll::Events::EPOLLIN)
|
epoll::Events::EPOLLIN,
|
||||||
{
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
) {
|
||||||
Err(DeviceError::FailedReadingQueue { .. }) => (),
|
Err(DeviceError::FailedReadingQueue { .. }) => (),
|
||||||
other => panic!("{:?}", other),
|
other => panic!("{:?}", other),
|
||||||
}
|
}
|
||||||
@@ -796,10 +848,11 @@ mod tests {
|
|||||||
let test_ctx = TestContext::new();
|
let test_ctx = TestContext::new();
|
||||||
let mut ctx = test_ctx.create_epoll_handler_context();
|
let mut ctx = test_ctx.create_epoll_handler_context();
|
||||||
ctx.handler.backend.write().unwrap().set_pending_rx(false);
|
ctx.handler.backend.write().unwrap().set_pending_rx(false);
|
||||||
match ctx
|
match ctx.handler.handle_event(
|
||||||
.handler
|
RX_QUEUE_EVENT,
|
||||||
.handle_event(RX_QUEUE_EVENT, epoll::Events::EPOLLIN)
|
epoll::Events::EPOLLIN,
|
||||||
{
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
) {
|
||||||
Err(DeviceError::FailedReadingQueue { .. }) => (),
|
Err(DeviceError::FailedReadingQueue { .. }) => (),
|
||||||
other => panic!("{:?}", other),
|
other => panic!("{:?}", other),
|
||||||
}
|
}
|
||||||
@@ -813,10 +866,11 @@ mod tests {
|
|||||||
let test_ctx = TestContext::new();
|
let test_ctx = TestContext::new();
|
||||||
let mut ctx = test_ctx.create_epoll_handler_context();
|
let mut ctx = test_ctx.create_epoll_handler_context();
|
||||||
ctx.handler.backend.write().unwrap().set_pending_rx(false);
|
ctx.handler.backend.write().unwrap().set_pending_rx(false);
|
||||||
match ctx
|
match ctx.handler.handle_event(
|
||||||
.handler
|
EVT_QUEUE_EVENT,
|
||||||
.handle_event(EVT_QUEUE_EVENT, epoll::Events::EPOLLIN)
|
epoll::Events::EPOLLIN,
|
||||||
{
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
) {
|
||||||
Err(DeviceError::FailedReadingQueue { .. }) => (),
|
Err(DeviceError::FailedReadingQueue { .. }) => (),
|
||||||
other => panic!("{:?}", other),
|
other => panic!("{:?}", other),
|
||||||
}
|
}
|
||||||
@@ -834,7 +888,11 @@ mod tests {
|
|||||||
|
|
||||||
ctx.handler.backend.write().unwrap().set_pending_rx(true);
|
ctx.handler.backend.write().unwrap().set_pending_rx(true);
|
||||||
ctx.handler
|
ctx.handler
|
||||||
.handle_event(BACKEND_EVENT, epoll::Events::EPOLLIN)
|
.handle_event(
|
||||||
|
BACKEND_EVENT,
|
||||||
|
epoll::Events::EPOLLIN,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// The backend should've received this event.
|
// The backend should've received this event.
|
||||||
@@ -857,7 +915,11 @@ mod tests {
|
|||||||
|
|
||||||
ctx.handler.backend.write().unwrap().set_pending_rx(false);
|
ctx.handler.backend.write().unwrap().set_pending_rx(false);
|
||||||
ctx.handler
|
ctx.handler
|
||||||
.handle_event(BACKEND_EVENT, epoll::Events::EPOLLIN)
|
.handle_event(
|
||||||
|
BACKEND_EVENT,
|
||||||
|
epoll::Events::EPOLLIN,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// The backend should've received this event.
|
// The backend should've received this event.
|
||||||
@@ -877,7 +939,11 @@ mod tests {
|
|||||||
let test_ctx = TestContext::new();
|
let test_ctx = TestContext::new();
|
||||||
let mut ctx = test_ctx.create_epoll_handler_context();
|
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 { .. }) => (),
|
Err(DeviceError::UnknownEvent { .. }) => (),
|
||||||
other => panic!("{:?}", other),
|
other => panic!("{:?}", other),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
@@ -306,6 +307,7 @@ mod tests {
|
|||||||
queues,
|
queues,
|
||||||
queue_evts,
|
queue_evts,
|
||||||
kill_evt: EventFd::new(EFD_NONBLOCK).unwrap(),
|
kill_evt: EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
|
pause_evt: EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||||
interrupt_cb,
|
interrupt_cb,
|
||||||
backend: Arc::new(RwLock::new(TestBackend::new())),
|
backend: Arc::new(RwLock::new(TestBackend::new())),
|
||||||
},
|
},
|
||||||
@@ -324,13 +326,21 @@ mod tests {
|
|||||||
pub fn signal_txq_event(&mut self) {
|
pub fn signal_txq_event(&mut self) {
|
||||||
self.handler.queue_evts[1].write(1).unwrap();
|
self.handler.queue_evts[1].write(1).unwrap();
|
||||||
self.handler
|
self.handler
|
||||||
.handle_event(TX_QUEUE_EVENT, epoll::Events::EPOLLIN)
|
.handle_event(
|
||||||
|
TX_QUEUE_EVENT,
|
||||||
|
epoll::Events::EPOLLIN,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
pub fn signal_rxq_event(&mut self) {
|
pub fn signal_rxq_event(&mut self) {
|
||||||
self.handler.queue_evts[0].write(1).unwrap();
|
self.handler.queue_evts[0].write(1).unwrap();
|
||||||
self.handler
|
self.handler
|
||||||
.handle_event(RX_QUEUE_EVENT, epoll::Events::EPOLLIN)
|
.handle_event(
|
||||||
|
RX_QUEUE_EVENT,
|
||||||
|
epoll::Events::EPOLLIN,
|
||||||
|
Arc::new(AtomicBool::new(false)),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user