blob: f10aad3db734ef8aa577fcfa09a892f16ed9bd1e [file] [log] [blame] [edit]
// Copyright 2019 Intel Corporation. All Rights Reserved.
// Copyright 2019-2021 Alibaba Cloud. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
use std::fmt::{Display, Formatter};
use std::io::{self, Result};
use std::marker::PhantomData;
use std::os::unix::io::{AsRawFd, RawFd};
use vm_memory::bitmap::Bitmap;
use vmm_sys_util::epoll::{ControlOperation, Epoll, EpollEvent, EventSet};
use vmm_sys_util::eventfd::EventFd;
use super::backend::VhostUserBackend;
use super::vring::VringT;
use super::GM;
/// Errors related to vring epoll event handling.
#[derive(Debug)]
pub enum VringEpollError {
/// Failed to create epoll file descriptor.
EpollCreateFd(io::Error),
/// Failed while waiting for events.
EpollWait(io::Error),
/// Could not register exit event
RegisterExitEvent(io::Error),
/// Failed to read the event from kick EventFd.
HandleEventReadKick(io::Error),
/// Failed to handle the event from the backend.
HandleEventBackendHandling(io::Error),
}
impl Display for VringEpollError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
VringEpollError::EpollCreateFd(e) => write!(f, "cannot create epoll fd: {}", e),
VringEpollError::EpollWait(e) => write!(f, "failed to wait for epoll event: {}", e),
VringEpollError::RegisterExitEvent(e) => write!(f, "cannot register exit event: {}", e),
VringEpollError::HandleEventReadKick(e) => {
write!(f, "cannot read vring kick event: {}", e)
}
VringEpollError::HandleEventBackendHandling(e) => {
write!(f, "failed to handle epoll event: {}", e)
}
}
}
}
impl std::error::Error for VringEpollError {}
/// Result of vring epoll operations.
pub type VringEpollResult<T> = std::result::Result<T, VringEpollError>;
/// Epoll event handler to manage and process epoll events for registered file descriptor.
///
/// The `VringEpollHandler` structure provides interfaces to:
/// - add file descriptors to be monitored by the epoll fd
/// - remove registered file descriptors from the epoll fd
/// - run the event loop to handle pending events on the epoll fd
pub struct VringEpollHandler<S, V, B> {
epoll: Epoll,
backend: S,
vrings: Vec<V>,
thread_id: usize,
exit_event_fd: Option<EventFd>,
phantom: PhantomData<B>,
}
impl<S, V, B> VringEpollHandler<S, V, B> {
/// Send `exit event` to break the event loop.
pub fn send_exit_event(&self) {
if let Some(eventfd) = self.exit_event_fd.as_ref() {
let _ = eventfd.write(1);
}
}
}
impl<S, V, B> VringEpollHandler<S, V, B>
where
S: VhostUserBackend<V, B>,
V: VringT<GM<B>>,
B: Bitmap + 'static,
{
/// Create a `VringEpollHandler` instance.
pub(crate) fn new(backend: S, vrings: Vec<V>, thread_id: usize) -> VringEpollResult<Self> {
let epoll = Epoll::new().map_err(VringEpollError::EpollCreateFd)?;
let exit_event_fd = backend.exit_event(thread_id);
if let Some(exit_event_fd) = &exit_event_fd {
let id = backend.num_queues();
epoll
.ctl(
ControlOperation::Add,
exit_event_fd.as_raw_fd(),
EpollEvent::new(EventSet::IN, id as u64),
)
.map_err(VringEpollError::RegisterExitEvent)?;
}
Ok(VringEpollHandler {
epoll,
backend,
vrings,
thread_id,
exit_event_fd,
phantom: PhantomData,
})
}
/// Register an event into the epoll fd.
///
/// When this event is later triggered, the backend implementation of `handle_event` will be
/// called.
pub fn register_listener(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> {
// `data` range [0...num_queues] is reserved for queues and exit event.
if data <= self.backend.num_queues() as u64 {
Err(io::Error::from_raw_os_error(libc::EINVAL))
} else {
self.register_event(fd, ev_type, data)
}
}
/// Unregister an event from the epoll fd.
///
/// If the event is triggered after this function has been called, the event will be silently
/// dropped.
pub fn unregister_listener(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> {
// `data` range [0...num_queues] is reserved for queues and exit event.
if data <= self.backend.num_queues() as u64 {
Err(io::Error::from_raw_os_error(libc::EINVAL))
} else {
self.unregister_event(fd, ev_type, data)
}
}
pub(crate) fn register_event(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> {
self.epoll
.ctl(ControlOperation::Add, fd, EpollEvent::new(ev_type, data))
}
pub(crate) fn unregister_event(&self, fd: RawFd, ev_type: EventSet, data: u64) -> Result<()> {
self.epoll
.ctl(ControlOperation::Delete, fd, EpollEvent::new(ev_type, data))
}
/// Run the event poll loop to handle all pending events on registered fds.
///
/// The event loop will be terminated once an event is received from the `exit event fd`
/// associated with the backend.
pub(crate) fn run(&self) -> VringEpollResult<()> {
const EPOLL_EVENTS_LEN: usize = 100;
let mut events = vec![EpollEvent::new(EventSet::empty(), 0); EPOLL_EVENTS_LEN];
'epoll: loop {
let num_events = match self.epoll.wait(-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(VringEpollError::EpollWait(e));
}
};
for event in events.iter().take(num_events) {
let evset = match EventSet::from_bits(event.events) {
Some(evset) => evset,
None => {
let evbits = event.events;
println!("epoll: ignoring unknown event set: 0x{:x}", evbits);
continue;
}
};
let ev_type = event.data() as u16;
// handle_event() returns true if an event is received from the exit event fd.
if self.handle_event(ev_type, evset)? {
break 'epoll;
}
}
}
Ok(())
}
fn handle_event(&self, device_event: u16, evset: EventSet) -> VringEpollResult<bool> {
if self.exit_event_fd.is_some() && device_event as usize == self.backend.num_queues() {
return Ok(true);
}
if (device_event as usize) < self.vrings.len() {
let vring = &self.vrings[device_event as usize];
let enabled = vring
.read_kick()
.map_err(VringEpollError::HandleEventReadKick)?;
// If the vring is not enabled, it should not be processed.
if !enabled {
return Ok(false);
}
}
self.backend
.handle_event(device_event, evset, &self.vrings, self.thread_id)
.map_err(VringEpollError::HandleEventBackendHandling)
}
}
impl<S, V, B> AsRawFd for VringEpollHandler<S, V, B> {
fn as_raw_fd(&self) -> RawFd {
self.epoll.as_raw_fd()
}
}
#[cfg(test)]
mod tests {
use super::super::backend::tests::MockVhostBackend;
use super::super::vring::VringRwLock;
use super::*;
use std::sync::{Arc, Mutex};
use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
use vmm_sys_util::eventfd::EventFd;
#[test]
fn test_vring_epoll_handler() {
let mem = GuestMemoryAtomic::new(
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x100000), 0x10000)]).unwrap(),
);
let vring = VringRwLock::new(mem, 0x1000).unwrap();
let backend = Arc::new(Mutex::new(MockVhostBackend::new()));
let handler = VringEpollHandler::new(backend, vec![vring], 0x1).unwrap();
let eventfd = EventFd::new(0).unwrap();
handler
.register_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
.unwrap();
// Register an already registered fd.
handler
.register_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
.unwrap_err();
// Register an invalid data.
handler
.register_listener(eventfd.as_raw_fd(), EventSet::IN, 1)
.unwrap_err();
handler
.unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
.unwrap();
// unregister an already unregistered fd.
handler
.unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 3)
.unwrap_err();
// unregister an invalid data.
handler
.unregister_listener(eventfd.as_raw_fd(), EventSet::IN, 1)
.unwrap_err();
// Check we retrieve the correct file descriptor
assert_eq!(handler.as_raw_fd(), handler.epoll.as_raw_fd());
}
}