2019-08-28 09:50:48 +00:00
|
|
|
// Copyright (c) 2019 Intel Corporation. All rights reserved.
|
|
|
|
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE-BSD-3-Clause file.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
|
|
|
|
|
|
|
use super::super::{Queue, VirtioInterruptType};
|
|
|
|
use super::{Error, Result};
|
|
|
|
use epoll;
|
|
|
|
use vmm_sys_util::eventfd::EventFd;
|
|
|
|
|
|
|
|
use crate::VirtioInterrupt;
|
|
|
|
use std::io;
|
|
|
|
use std::os::unix::io::AsRawFd;
|
2019-12-02 20:08:53 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2019-08-28 09:50:48 +00:00
|
|
|
use std::sync::Arc;
|
2019-12-02 20:08:53 +00:00
|
|
|
use std::thread;
|
2019-08-30 18:47:24 +00:00
|
|
|
use vhost_rs::vhost_user::{MasterReqHandler, VhostUserMasterReqHandler};
|
2019-08-28 09:50:48 +00:00
|
|
|
|
|
|
|
/// Collection of common parameters required by vhost-user devices while
|
|
|
|
/// call Epoll handler.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `interrupt_cb` interrupt for virtqueue change.
|
|
|
|
/// * `kill_evt` - EventFd used to kill the vhost-user device.
|
|
|
|
/// * `vu_interrupt_list` - virtqueue and EventFd to signal when buffer used.
|
2019-08-30 18:47:24 +00:00
|
|
|
pub struct VhostUserEpollConfig<S: VhostUserMasterReqHandler> {
|
2019-08-28 09:50:48 +00:00
|
|
|
pub interrupt_cb: Arc<VirtioInterrupt>,
|
|
|
|
pub kill_evt: EventFd,
|
2019-12-02 20:08:53 +00:00
|
|
|
pub pause_evt: EventFd,
|
2019-08-28 09:50:48 +00:00
|
|
|
pub vu_interrupt_list: Vec<(EventFd, Queue)>,
|
2019-08-30 18:47:24 +00:00
|
|
|
pub slave_req_handler: Option<MasterReqHandler<S>>,
|
2019-08-28 09:50:48 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:47:24 +00:00
|
|
|
pub struct VhostUserEpollHandler<S: VhostUserMasterReqHandler> {
|
|
|
|
vu_epoll_cfg: VhostUserEpollConfig<S>,
|
2019-08-28 09:50:48 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:47:24 +00:00
|
|
|
impl<S: VhostUserMasterReqHandler> VhostUserEpollHandler<S> {
|
2019-08-28 09:50:48 +00:00
|
|
|
/// Construct a new event handler for vhost-user based devices.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `vu_epoll_cfg` - collection of common parameters for vhost-user devices
|
|
|
|
///
|
|
|
|
/// # Return
|
|
|
|
/// * `VhostUserEpollHandler` - epoll handler for vhost-user based devices
|
2019-08-30 18:47:24 +00:00
|
|
|
pub fn new(vu_epoll_cfg: VhostUserEpollConfig<S>) -> VhostUserEpollHandler<S> {
|
2019-08-28 09:50:48 +00:00
|
|
|
VhostUserEpollHandler { vu_epoll_cfg }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signal_used_queue(&self, queue: &Queue) -> Result<()> {
|
|
|
|
(self.vu_epoll_cfg.interrupt_cb)(&VirtioInterruptType::Queue, Some(queue))
|
2019-08-30 18:47:24 +00:00
|
|
|
.map_err(Error::FailedSignalingUsedQueue)
|
2019-08-28 09:50:48 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 20:08:53 +00:00
|
|
|
pub fn run(&mut self, paused: Arc<AtomicBool>) -> Result<()> {
|
2019-08-30 18:47:24 +00:00
|
|
|
// Create the epoll file descriptor
|
2019-08-28 09:50:48 +00:00
|
|
|
let epoll_fd = epoll::create(true).map_err(Error::EpollCreateFd)?;
|
|
|
|
|
|
|
|
for (index, vhost_user_interrupt) in self.vu_epoll_cfg.vu_interrupt_list.iter().enumerate()
|
|
|
|
{
|
2019-08-30 18:47:24 +00:00
|
|
|
// Add events
|
2019-08-28 09:50:48 +00:00
|
|
|
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)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
let kill_evt_index = self.vu_epoll_cfg.vu_interrupt_list.len();
|
|
|
|
|
|
|
|
epoll::ctl(
|
|
|
|
epoll_fd,
|
|
|
|
epoll::ControlOptions::EPOLL_CTL_ADD,
|
|
|
|
self.vu_epoll_cfg.kill_evt.as_raw_fd(),
|
|
|
|
epoll::Event::new(epoll::Events::EPOLLIN, kill_evt_index as u64),
|
|
|
|
)
|
|
|
|
.map_err(Error::EpollCtl)?;
|
|
|
|
|
2019-12-02 20:08:53 +00:00
|
|
|
let pause_evt_index = kill_evt_index + 1;
|
|
|
|
|
|
|
|
epoll::ctl(
|
|
|
|
epoll_fd,
|
|
|
|
epoll::ControlOptions::EPOLL_CTL_ADD,
|
|
|
|
self.vu_epoll_cfg.pause_evt.as_raw_fd(),
|
|
|
|
epoll::Event::new(epoll::Events::EPOLLIN, pause_evt_index as u64),
|
|
|
|
)
|
|
|
|
.map_err(Error::EpollCtl)?;
|
|
|
|
|
|
|
|
let mut index = pause_evt_index;
|
2019-09-03 07:55:20 +00:00
|
|
|
|
2019-08-30 18:47:24 +00:00
|
|
|
let slave_evt_index = if let Some(self_req_handler) = &self.vu_epoll_cfg.slave_req_handler {
|
2019-12-02 20:08:53 +00:00
|
|
|
index = pause_evt_index + 1;
|
2019-08-30 18:47:24 +00:00
|
|
|
epoll::ctl(
|
|
|
|
epoll_fd,
|
|
|
|
epoll::ControlOptions::EPOLL_CTL_ADD,
|
|
|
|
self_req_handler.as_raw_fd(),
|
|
|
|
epoll::Event::new(epoll::Events::EPOLLIN, index as u64),
|
|
|
|
)
|
|
|
|
.map_err(Error::EpollCtl)?;
|
|
|
|
|
|
|
|
Some(index)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2019-09-03 07:55:20 +00:00
|
|
|
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); index + 1];
|
2019-08-28 09:50:48 +00:00
|
|
|
|
|
|
|
'poll: loop {
|
|
|
|
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(Error::EpollWait(e));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for event in events.iter().take(num_events) {
|
|
|
|
let ev_type = event.data as usize;
|
|
|
|
|
|
|
|
match ev_type {
|
|
|
|
x if x < kill_evt_index => {
|
2019-08-30 18:47:24 +00:00
|
|
|
self.vu_epoll_cfg.vu_interrupt_list[x]
|
|
|
|
.0
|
2019-08-28 09:50:48 +00:00
|
|
|
.read()
|
|
|
|
.map_err(Error::FailedReadingQueue)?;
|
2019-08-30 18:47:24 +00:00
|
|
|
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;
|
2019-08-28 09:50:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
x if kill_evt_index == x => {
|
2019-08-30 18:47:24 +00:00
|
|
|
debug!("KILL_EVENT received, stopping epoll loop");
|
2019-08-28 09:50:48 +00:00
|
|
|
break 'poll;
|
|
|
|
}
|
2019-12-02 20:08:53 +00:00
|
|
|
x if pause_evt_index == x => {
|
|
|
|
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
|
|
|
|
// be true.
|
|
|
|
while paused.load(Ordering::SeqCst) {
|
|
|
|
thread::park();
|
|
|
|
}
|
|
|
|
}
|
2019-08-30 18:47:24 +00:00
|
|
|
x if (slave_evt_index.is_some() && slave_evt_index.unwrap() == x) => {
|
|
|
|
if let Some(slave_req_handler) =
|
|
|
|
self.vu_epoll_cfg.slave_req_handler.as_mut()
|
|
|
|
{
|
|
|
|
slave_req_handler
|
|
|
|
.handle_request()
|
|
|
|
.map_err(Error::VhostUserSlaveRequest)?;
|
|
|
|
}
|
|
|
|
}
|
2019-08-28 09:50:48 +00:00
|
|
|
_ => {
|
2019-08-30 18:47:24 +00:00
|
|
|
error!("Unknown event for vhost-user");
|
2019-08-28 09:50:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|