From ce65093f8d7943114c5f6cbb7b1f61087c09e4fe Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 4 Aug 2020 12:16:44 +0100 Subject: [PATCH] virtio-devices: pmem: Port to EpollHelper Migrate to EpollHelper so as to remove code that is duplicated between multiple virtio devices. Signed-off-by: Rob Bradford --- virtio-devices/src/pmem.rs | 132 ++++++++----------------------------- 1 file changed, 29 insertions(+), 103 deletions(-) diff --git a/virtio-devices/src/pmem.rs b/virtio-devices/src/pmem.rs index 039563086..957604dec 100644 --- a/virtio-devices/src/pmem.rs +++ b/virtio-devices/src/pmem.rs @@ -8,8 +8,9 @@ use super::Error as DeviceError; use super::{ - ActivateError, ActivateResult, DescriptorChain, DeviceEventT, Queue, UserspaceMapping, - VirtioDevice, VirtioDeviceType, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, + ActivateError, ActivateResult, DescriptorChain, EpollHelper, EpollHelperError, + EpollHelperHandler, Queue, UserspaceMapping, VirtioDevice, VirtioDeviceType, + EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, }; use crate::{VirtioInterrupt, VirtioInterruptType}; use anyhow::anyhow; @@ -19,7 +20,7 @@ use std::fmt::{self, Display}; use std::fs::File; use std::io; use std::mem::size_of; -use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::io::AsRawFd; use std::result; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -43,11 +44,7 @@ const VIRTIO_PMEM_RESP_TYPE_OK: u32 = 0; const VIRTIO_PMEM_RESP_TYPE_EIO: u32 = 1; // New descriptors are pending on the virtio queue. -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; +const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; #[derive(Copy, Clone, Debug, Default, Deserialize)] #[repr(C, packed)] @@ -244,106 +241,35 @@ impl PmemEpollHandler { }) } - fn run(&mut self, paused: Arc) -> result::Result<(), DeviceError> { - // Create the epoll file descriptor - let epoll_fd = epoll::create(true).map_err(DeviceError::EpollCreateFd)?; - // Use 'File' to enforce closing on 'epoll_fd' - let epoll_file = unsafe { File::from_raw_fd(epoll_fd) }; + fn run(&mut self, paused: Arc) -> result::Result<(), EpollHelperError> { + let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; + helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?; + helper.run(paused, self)?; - // Add events - epoll::ctl( - epoll_file.as_raw_fd(), - epoll::ControlOptions::EPOLL_CTL_ADD, - self.queue_evt.as_raw_fd(), - epoll::Event::new(epoll::Events::EPOLLIN, u64::from(QUEUE_AVAIL_EVENT)), - ) - .map_err(DeviceError::EpollCtl)?; - epoll::ctl( - epoll_file.as_raw_fd(), - epoll::ControlOptions::EPOLL_CTL_ADD, - self.kill_evt.as_raw_fd(), - epoll::Event::new(epoll::Events::EPOLLIN, u64::from(KILL_EVENT)), - ) - .map_err(DeviceError::EpollCtl)?; + Ok(()) + } +} - epoll::ctl( - epoll_file.as_raw_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]; - - // Before jumping into the epoll loop, check if the device is expected - // to be in a paused state. This is helpful for the restore code path - // as the device thread should not start processing anything before the - // device has been resumed. - while paused.load(Ordering::SeqCst) { - thread::park(); - } - - 'epoll: loop { - let num_events = match epoll::wait(epoll_file.as_raw_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; - - match ev_type { - QUEUE_AVAIL_EVENT => { - if let Err(e) = self.queue_evt.read() { - error!("Failed to get queue event: {:?}", e); - break 'epoll; - } else if self.process_queue() { - if let Err(e) = self.signal_used_queue() { - error!("Failed to signal used queue: {:?}", e); - break 'epoll; - } - } - } - KILL_EVENT => { - 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(); - } - - // 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"); +impl EpollHelperHandler for PmemEpollHandler { + fn handle_event(&mut self, _helper: &mut EpollHelper, event: u16) -> bool { + match event { + QUEUE_AVAIL_EVENT => { + if let Err(e) = self.queue_evt.read() { + error!("Failed to get queue event: {:?}", e); + return true; + } else if self.process_queue() { + if let Err(e) = self.signal_used_queue() { + error!("Failed to signal used queue: {:?}", e); + return true; } } } + _ => { + error!("Unexpected event: {}", event); + return true; + } } - - Ok(()) + false } } @@ -357,7 +283,7 @@ pub struct Pmem { config: VirtioPmemConfig, queue_evts: Option>, interrupt_cb: Option>, - epoll_threads: Option>>>, + epoll_threads: Option>>>, paused: Arc, mapping: UserspaceMapping,