vm-virtio: Ensure pause event is caught by every virtio thread

Each virtio thread was reading/draining the pause_evt pipe when
detecting the associated event. Problem is, when a virtio device has
multiple threads, they all share the same pause_evt pipe, which can
prevent some threads from receiving the event. If the first thread to
catch the event is quickly clearing the pipe, some other threads might
simply miss the event and they will not enter the "paused" state as
expected.

This is a behavior that was spotted with virtio-net as it usually uses
2 threads by default (1 for TX/RX queues and 1 for the control queue).

The way to solve this issue is by letting each thread drain the pipe
during the resume codepath, that is after the thread has been unparked.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-06-25 10:08:05 +02:00
parent 86377127df
commit a7f0f9dfea
10 changed files with 50 additions and 18 deletions

View File

@ -832,8 +832,6 @@ impl<T: DiskFile> BlockEpollHandler<T> {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
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
@ -841,6 +839,11 @@ impl<T: DiskFile> BlockEpollHandler<T> {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-block");

View File

@ -313,8 +313,6 @@ impl ConsoleEpollHandler {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
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
@ -322,6 +320,11 @@ impl ConsoleEpollHandler {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-console");

View File

@ -744,8 +744,6 @@ impl IommuEpollHandler {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
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
@ -753,6 +751,11 @@ impl IommuEpollHandler {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-iommu");

View File

@ -747,6 +747,11 @@ impl MemEpollHandler {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
return Err(DeviceError::EpollHander(String::from(

View File

@ -395,8 +395,6 @@ impl NetEpollHandler {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
debug!("PAUSE_EVENT received, pausing virtio-net epoll loop");
// We loop here to handle spurious park() returns.
@ -405,6 +403,11 @@ impl NetEpollHandler {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-net");

View File

@ -316,8 +316,6 @@ impl NetCtrlEpollHandler {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
debug!("PAUSE_EVENT received, pausing vhost-user epoll loop");
// We loop here to handle spurious park() returns.
// Until we have not resumed, the paused boolean will
@ -325,6 +323,11 @@ impl NetCtrlEpollHandler {
while paused.load(Ordering::SeqCst) {
std::thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-net");

View File

@ -324,8 +324,6 @@ impl PmemEpollHandler {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
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
@ -333,6 +331,11 @@ impl PmemEpollHandler {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-block");

View File

@ -167,8 +167,6 @@ impl RngEpollHandler {
break 'epoll;
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
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
@ -176,6 +174,11 @@ impl RngEpollHandler {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
_ => {
error!("Unknown event for virtio-block");

View File

@ -163,8 +163,6 @@ impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
break 'poll;
}
x if pause_evt_index == x => {
// Drain pause event
let _ = self.vu_epoll_cfg.pause_evt.read();
debug!("PAUSE_EVENT received, pausing vhost-user epoll loop");
// We loop here to handle spurious park() returns.
// Until we have not resumed, the paused boolean will
@ -172,6 +170,11 @@ impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.vu_epoll_cfg.pause_evt.read();
}
x if (slave_evt_index.is_some() && slave_evt_index.unwrap() == x) => {
if let Some(slave_req_handler) =

View File

@ -365,8 +365,6 @@ where
return Ok(true);
}
PAUSE_EVENT => {
// Drain pause event
let _ = self.pause_evt.read();
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
@ -374,6 +372,11 @@ where
while paused.load(Ordering::SeqCst) {
thread::park();
}
// Drain pause event after the device has been resumed.
// This ensures the pause event has been seen by each
// and every thread related to this virtio device.
let _ = self.pause_evt.read();
}
other => {
error!("Unknown event for virtio-vsock");