From be421dcceac42af92b36167b67cbd2dd5547a73a Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Tue, 14 Jan 2020 08:18:35 +0100 Subject: [PATCH] vm-virtio: Optimize vhost-user interrupt notification Thanks to the recently introduced function notifier() in the VirtioInterrupt trait, all vhost-user devices can now bypass listening onto an intermediate event fd as they can provide the actual fd responsible for triggering the interrupt directly to the vhost-user backend. In case the notifier does not provide the event fd, the code falls back onto the creation of an intermediate event fd it needs to listen to, so that it can trigger the interrupt on behalf of the backend. Signed-off-by: Sebastien Boeuf --- vm-virtio/src/vhost_user/blk.rs | 1 + vm-virtio/src/vhost_user/fs.rs | 1 + vm-virtio/src/vhost_user/handler.rs | 37 +++++++++++----------- vm-virtio/src/vhost_user/net.rs | 3 +- vm-virtio/src/vhost_user/vu_common_ctrl.rs | 25 +++++++++++---- 5 files changed, 41 insertions(+), 26 deletions(-) diff --git a/vm-virtio/src/vhost_user/blk.rs b/vm-virtio/src/vhost_user/blk.rs index 79a46c9b5..293292b32 100644 --- a/vm-virtio/src/vhost_user/blk.rs +++ b/vm-virtio/src/vhost_user/blk.rs @@ -260,6 +260,7 @@ impl VirtioDevice for Blk { mem.load().as_ref(), queues, queue_evts, + &interrupt_cb, self.acked_features, ) .map_err(ActivateError::VhostUserBlkSetup)?; diff --git a/vm-virtio/src/vhost_user/fs.rs b/vm-virtio/src/vhost_user/fs.rs index 1cde98e85..770a1a238 100644 --- a/vm-virtio/src/vhost_user/fs.rs +++ b/vm-virtio/src/vhost_user/fs.rs @@ -382,6 +382,7 @@ impl VirtioDevice for Fs { mem.load().as_ref(), queues, queue_evts, + &interrupt_cb, self.acked_features, ) .map_err(ActivateError::VhostUserSetup)?; diff --git a/vm-virtio/src/vhost_user/handler.rs b/vm-virtio/src/vhost_user/handler.rs index 22a722aed..b5d5fa311 100644 --- a/vm-virtio/src/vhost_user/handler.rs +++ b/vm-virtio/src/vhost_user/handler.rs @@ -31,7 +31,7 @@ pub struct VhostUserEpollConfig { pub interrupt_cb: Arc, pub kill_evt: EventFd, pub pause_evt: EventFd, - pub vu_interrupt_list: Vec<(EventFd, Queue)>, + pub vu_interrupt_list: Vec<(Option, Queue)>, pub slave_req_handler: Option>, } @@ -64,14 +64,16 @@ impl VhostUserEpollHandler { for (index, vhost_user_interrupt) in self.vu_epoll_cfg.vu_interrupt_list.iter().enumerate() { - // Add events - epoll::ctl( - epoll_fd, - epoll::ControlOptions::EPOLL_CTL_ADD, - vhost_user_interrupt.0.as_raw_fd(), - epoll::Event::new(epoll::Events::EPOLLIN, index as u64), - ) - .map_err(Error::EpollCtl)?; + if let Some(eventfd) = &vhost_user_interrupt.0 { + // Add events + epoll::ctl( + epoll_fd, + epoll::ControlOptions::EPOLL_CTL_ADD, + eventfd.as_raw_fd(), + epoll::Event::new(epoll::Events::EPOLLIN, index as u64), + ) + .map_err(Error::EpollCtl)?; + } } let kill_evt_index = self.vu_epoll_cfg.vu_interrupt_list.len(); @@ -136,15 +138,14 @@ impl VhostUserEpollHandler { match ev_type { x if x < kill_evt_index => { - self.vu_epoll_cfg.vu_interrupt_list[x] - .0 - .read() - .map_err(Error::FailedReadingQueue)?; - if let Err(e) = - self.signal_used_queue(&self.vu_epoll_cfg.vu_interrupt_list[x].1) - { - error!("Failed to signal used queue: {:?}", e); - break 'poll; + if let Some(eventfd) = &self.vu_epoll_cfg.vu_interrupt_list[x].0 { + eventfd.read().map_err(Error::FailedReadingQueue)?; + if let Err(e) = + self.signal_used_queue(&self.vu_epoll_cfg.vu_interrupt_list[x].1) + { + error!("Failed to signal used queue: {:?}", e); + break 'poll; + } } } x if kill_evt_index == x => { diff --git a/vm-virtio/src/vhost_user/net.rs b/vm-virtio/src/vhost_user/net.rs index 7e2c26f3b..46d5881e6 100644 --- a/vm-virtio/src/vhost_user/net.rs +++ b/vm-virtio/src/vhost_user/net.rs @@ -302,13 +302,14 @@ impl VirtioDevice for Net { mem.load().as_ref(), queues, queue_evts, + &interrupt_cb, self.acked_features & self.backend_features, ) .map_err(ActivateError::VhostUserNetSetup)?; let mut epoll_thread = Vec::new(); for _ in 0..vu_interrupt_list.len() / 2 { - let mut interrupt_list_sub: Vec<(EventFd, Queue)> = Vec::with_capacity(2); + let mut interrupt_list_sub: Vec<(Option, Queue)> = Vec::with_capacity(2); interrupt_list_sub.push(vu_interrupt_list.remove(0)); interrupt_list_sub.push(vu_interrupt_list.remove(0)); diff --git a/vm-virtio/src/vhost_user/vu_common_ctrl.rs b/vm-virtio/src/vhost_user/vu_common_ctrl.rs index 122ff1454..89318a2cf 100644 --- a/vm-virtio/src/vhost_user/vu_common_ctrl.rs +++ b/vm-virtio/src/vhost_user/vu_common_ctrl.rs @@ -5,9 +5,11 @@ use libc; use libc::EFD_NONBLOCK; use std::convert::TryInto; use std::os::unix::io::AsRawFd; +use std::sync::Arc; use std::vec::Vec; use crate::queue::Descriptor; +use crate::{VirtioInterrupt, VirtioInterruptType}; use vm_device::get_host_address_range; use vm_memory::{Address, Error as MmapError, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; @@ -30,7 +32,8 @@ pub fn setup_vhost_user_vring( mem: &GuestMemoryMmap, queues: Vec, queue_evts: Vec, -) -> Result> { + virtio_interrupt: &Arc, +) -> Result, Queue)>> { let mut regions: Vec = Vec::new(); mem.with_regions_mut(|_, region| { let (mmap_handle, mmap_offset) = match region.file_offset() { @@ -89,10 +92,17 @@ pub fn setup_vhost_user_vring( vu.set_vring_base(queue_index, 0u16) .map_err(Error::VhostUserSetVringBase)?; - let vhost_user_interrupt = EventFd::new(EFD_NONBLOCK).map_err(Error::VhostIrqCreate)?; - vu.set_vring_call(queue_index, &vhost_user_interrupt) - .map_err(Error::VhostUserSetVringCall)?; - vu_interrupt_list.push((vhost_user_interrupt, queue)); + if let Some(eventfd) = virtio_interrupt.notifier(&VirtioInterruptType::Queue, Some(&queue)) + { + vu.set_vring_call(queue_index, &eventfd) + .map_err(Error::VhostUserSetVringCall)?; + vu_interrupt_list.push((None, queue)); + } else { + let eventfd = EventFd::new(EFD_NONBLOCK).map_err(Error::VhostIrqCreate)?; + vu.set_vring_call(queue_index, &eventfd) + .map_err(Error::VhostUserSetVringCall)?; + vu_interrupt_list.push((Some(eventfd), queue)); + } vu.set_vring_kick(queue_index, &queue_evts[queue_index]) .map_err(Error::VhostUserSetVringKick)?; @@ -109,13 +119,14 @@ pub fn setup_vhost_user( mem: &GuestMemoryMmap, queues: Vec, queue_evts: Vec, + virtio_interrupt: &Arc, acked_features: u64, -) -> Result> { +) -> Result, Queue)>> { // Set features based on the acked features from the guest driver. vu.set_features(acked_features) .map_err(Error::VhostUserSetFeatures)?; - setup_vhost_user_vring(vu, mem, queues, queue_evts) + setup_vhost_user_vring(vu, mem, queues, queue_evts, virtio_interrupt) } pub fn reset_vhost_user(vu: &mut Master, num_queues: usize) -> Result<()> {