From baec27698eadbb342eb2ba81e549a9951d6ec7d5 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Thu, 1 Aug 2019 13:08:47 -0700 Subject: [PATCH] vm-virtio: Don't break from epoll loop on EINTR The existing code taking care of the epoll loop was too restrictive as it was propagating the error returned from the epoll_wait() syscall, no matter what was the error. This causes the epoll loop to be broken, leading to a non-functional virtio device. This patch enforces the parsing of the returned error and prevent from the error propagation in case it is EINTR, which stands for Interrupted. In case the epoll loop is interrupted, it is appropriate to retry. Signed-off-by: Sebastien Boeuf --- vm-virtio/src/block.rs | 18 ++++++++++++++++-- vm-virtio/src/console.rs | 18 ++++++++++++++++-- vm-virtio/src/fs.rs | 18 ++++++++++++++++-- vm-virtio/src/net.rs | 18 ++++++++++++++++-- vm-virtio/src/pmem.rs | 18 ++++++++++++++++-- vm-virtio/src/rng.rs | 18 ++++++++++++++++-- 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/vm-virtio/src/block.rs b/vm-virtio/src/block.rs index 04aad2a21..7e0b2d50e 100755 --- a/vm-virtio/src/block.rs +++ b/vm-virtio/src/block.rs @@ -422,8 +422,22 @@ impl BlockEpollHandler { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 'epoll: loop { - let num_events = - epoll::wait(epoll_fd, -1, &mut events[..]).map_err(DeviceError::EpollWait)?; + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(DeviceError::EpollWait(e)); + } + }; for event in events.iter().take(num_events) { let ev_type = event.data as u16; diff --git a/vm-virtio/src/console.rs b/vm-virtio/src/console.rs index 0827223e0..f41b19f38 100755 --- a/vm-virtio/src/console.rs +++ b/vm-virtio/src/console.rs @@ -170,8 +170,22 @@ impl ConsoleEpollHandler { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 'epoll: loop { - let num_events = - epoll::wait(epoll_fd, -1, &mut events[..]).map_err(DeviceError::EpollWait)?; + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(DeviceError::EpollWait(e)); + } + }; for event in events.iter().take(num_events) { let ev_type = event.data as u16; diff --git a/vm-virtio/src/fs.rs b/vm-virtio/src/fs.rs index 9f21b6a72..571793355 100644 --- a/vm-virtio/src/fs.rs +++ b/vm-virtio/src/fs.rs @@ -121,8 +121,22 @@ impl FsEpollHandler { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 'epoll: loop { - let num_events = - epoll::wait(epoll_fd, -1, &mut events[..]).map_err(DeviceError::EpollWait)?; + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(DeviceError::EpollWait(e)); + } + }; for event in events.iter().take(num_events) { let ev_type = event.data as usize; diff --git a/vm-virtio/src/net.rs b/vm-virtio/src/net.rs index 0050fb426..29fb6d88e 100644 --- a/vm-virtio/src/net.rs +++ b/vm-virtio/src/net.rs @@ -333,8 +333,22 @@ impl NetEpollHandler { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 'epoll: loop { - let num_events = - epoll::wait(epoll_fd, -1, &mut events[..]).map_err(DeviceError::EpollWait)?; + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(DeviceError::EpollWait(e)); + } + }; for event in events.iter().take(num_events) { let ev_type = event.data as u16; diff --git a/vm-virtio/src/pmem.rs b/vm-virtio/src/pmem.rs index cc3de2122..0f1191201 100644 --- a/vm-virtio/src/pmem.rs +++ b/vm-virtio/src/pmem.rs @@ -237,8 +237,22 @@ impl PmemEpollHandler { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 'epoll: loop { - let num_events = - epoll::wait(epoll_fd, -1, &mut events[..]).map_err(DeviceError::EpollWait)?; + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(DeviceError::EpollWait(e)); + } + }; for event in events.iter().take(num_events) { let ev_type = event.data as u16; diff --git a/vm-virtio/src/rng.rs b/vm-virtio/src/rng.rs index b86692b7a..c0f62b1cd 100755 --- a/vm-virtio/src/rng.rs +++ b/vm-virtio/src/rng.rs @@ -105,8 +105,22 @@ impl RngEpollHandler { let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 'epoll: loop { - let num_events = - epoll::wait(epoll_fd, -1, &mut events[..]).map_err(DeviceError::EpollWait)?; + let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { + Ok(res) => res, + Err(e) => { + if e.kind() == io::ErrorKind::Interrupted { + // It's well defined from the epoll_wait() syscall + // documentation that the epoll loop can be interrupted + // before any of the requested events occurred or the + // timeout expired. In both those cases, epoll_wait() + // returns an error of type EINTR, but this should not + // be considered as a regular error. Instead it is more + // appropriate to retry, by calling into epoll_wait(). + continue; + } + return Err(DeviceError::EpollWait(e)); + } + }; for event in events.iter().take(num_events) { let ev_type = event.data as u16;