mirror of
https://github.com/rust-vmm/vhost-device.git
synced 2026-01-08 12:38:21 +00:00
gpio: Add support for interrupts
Add support for GPIO interrupts. Only edge interrupts are supported by libgpiod. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
parent
8a021c704f
commit
679224af8d
193
gpio/src/gpio.rs
193
gpio/src/gpio.rs
@ -6,9 +6,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use log::error;
|
||||
use std::sync::RwLock;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use libgpiod::{Chip, Direction, Error as LibGpiodError, LineConfig, LineRequest, RequestConfig};
|
||||
use libgpiod::{
|
||||
Chip, Direction, Edge, EdgeEventBuffer, Error as LibGpiodError, LineConfig, LineRequest,
|
||||
RequestConfig,
|
||||
};
|
||||
use thiserror::Error as ThisError;
|
||||
use vm_memory::{Le16, Le32};
|
||||
|
||||
@ -27,6 +31,22 @@ pub(crate) enum Error {
|
||||
GpioMessageInvalid(u16),
|
||||
#[error("Gpiod operation failed {0:?}")]
|
||||
GpiodFailed(LibGpiodError),
|
||||
#[error("Gpio irq operation timed out")]
|
||||
GpioIrqOpTimedOut,
|
||||
#[error("Gpio irq type invalid {0}")]
|
||||
GpioIrqTypeInvalid(u16),
|
||||
#[error("Gpio irq type not supported {0}")]
|
||||
GpioIrqTypeNotSupported(u16),
|
||||
#[error("Gpio irq type same as current {0}")]
|
||||
GpioIrqTypeNoChange(u16),
|
||||
#[error("Gpio irq not enabled yet")]
|
||||
GpioIrqNotEnabled,
|
||||
#[error(
|
||||
"Current Gpio irq type is valid, must configure to VIRTIO_GPIO_IRQ_TYPE_NONE first {0}"
|
||||
)]
|
||||
GpioOldIrqTypeValid(u16),
|
||||
#[error("Gpio line-request not configured")]
|
||||
GpioLineRequestNotConfigured,
|
||||
}
|
||||
|
||||
/// Virtio specification definitions
|
||||
@ -36,12 +56,25 @@ pub(crate) const VIRTIO_GPIO_MSG_GET_DIRECTION: u16 = 0x0002;
|
||||
pub(crate) const VIRTIO_GPIO_MSG_SET_DIRECTION: u16 = 0x0003;
|
||||
pub(crate) const VIRTIO_GPIO_MSG_GET_VALUE: u16 = 0x0004;
|
||||
pub(crate) const VIRTIO_GPIO_MSG_SET_VALUE: u16 = 0x0005;
|
||||
pub(crate) const VIRTIO_GPIO_MSG_IRQ_TYPE: u16 = 0x0006;
|
||||
|
||||
/// Direction types
|
||||
pub(crate) const VIRTIO_GPIO_DIRECTION_NONE: u8 = 0x00;
|
||||
pub(crate) const VIRTIO_GPIO_DIRECTION_OUT: u8 = 0x01;
|
||||
pub(crate) const VIRTIO_GPIO_DIRECTION_IN: u8 = 0x02;
|
||||
|
||||
/// Virtio GPIO IRQ types
|
||||
pub(crate) const VIRTIO_GPIO_IRQ_TYPE_NONE: u16 = 0x00;
|
||||
pub(crate) const VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING: u16 = 0x01;
|
||||
pub(crate) const VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING: u16 = 0x02;
|
||||
pub(crate) const VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH: u16 =
|
||||
VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING | VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING;
|
||||
pub(crate) const VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH: u16 = 0x04;
|
||||
pub(crate) const VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW: u16 = 0x08;
|
||||
const VIRTIO_GPIO_IRQ_TYPE_ALL: u16 = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH
|
||||
| VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH
|
||||
| VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW;
|
||||
|
||||
/// Virtio GPIO Configuration
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
@ -57,7 +90,7 @@ pub(crate) struct VirtioGpioConfig {
|
||||
/// be used outside of this crate. The purpose of this trait is to provide a
|
||||
/// mock implementation for the GPIO driver so that we can test the GPIO
|
||||
/// functionality without the need of a physical device.
|
||||
pub(crate) trait GpioDevice {
|
||||
pub(crate) trait GpioDevice: Send + Sync + 'static {
|
||||
fn open(device: u32) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
@ -68,10 +101,15 @@ pub(crate) trait GpioDevice {
|
||||
fn set_direction(&self, gpio: u16, dir: u8, value: u32) -> Result<()>;
|
||||
fn get_value(&self, gpio: u16) -> Result<u8>;
|
||||
fn set_value(&self, gpio: u16, value: u32) -> Result<()>;
|
||||
|
||||
fn set_irq_type(&self, gpio: u16, value: u16) -> Result<()>;
|
||||
fn wait_for_interrupt(&self, gpio: u16) -> Result<()>;
|
||||
}
|
||||
|
||||
pub(crate) struct PhysLineState {
|
||||
request: Option<LineRequest>,
|
||||
// See wait_for_interrupt() for explanation of Arc.
|
||||
request: Option<Arc<LineRequest>>,
|
||||
buffer: Option<EdgeEventBuffer>,
|
||||
}
|
||||
|
||||
pub(crate) struct PhysDevice {
|
||||
@ -97,6 +135,7 @@ impl GpioDevice for PhysDevice {
|
||||
state.resize_with(ngpio as usize, || {
|
||||
RwLock::new(PhysLineState {
|
||||
request: None,
|
||||
buffer: None,
|
||||
})
|
||||
});
|
||||
|
||||
@ -160,11 +199,11 @@ impl GpioDevice for PhysDevice {
|
||||
rconfig.set_consumer("vhu-gpio");
|
||||
rconfig.set_offsets(&[gpio as u32]);
|
||||
|
||||
state.request = Some(
|
||||
state.request = Some(Arc::new(
|
||||
self.chip
|
||||
.request_lines(&rconfig, &config)
|
||||
.map_err(Error::GpiodFailed)?,
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -195,11 +234,103 @@ impl GpioDevice for PhysDevice {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_irq_type(&self, gpio: u16, value: u16) -> Result<()> {
|
||||
let state = &mut self.state[gpio as usize].write().unwrap();
|
||||
let mut config = LineConfig::new().map_err(Error::GpiodFailed)?;
|
||||
|
||||
match value as u16 {
|
||||
VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING => config.set_edge_detection(Edge::Rising),
|
||||
VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING => config.set_edge_detection(Edge::Falling),
|
||||
VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH => config.set_edge_detection(Edge::Both),
|
||||
|
||||
// Drop the buffer.
|
||||
VIRTIO_GPIO_IRQ_TYPE_NONE => {
|
||||
state.buffer = None;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Only edge IRQs are supported for now.
|
||||
_ => return Err(Error::GpioIrqTypeNotSupported(value)),
|
||||
};
|
||||
|
||||
// Allocate the buffer and configure the line for interrupt.
|
||||
if state.request.is_none() {
|
||||
Err(Error::GpioLineRequestNotConfigured)
|
||||
} else {
|
||||
// The GPIO Virtio specification allows a single interrupt event for each
|
||||
// `wait_for_interrupt()` message. And for that we need a single `EdgeEventBuffer`.
|
||||
state.buffer = Some(EdgeEventBuffer::new(1).map_err(Error::GpiodFailed)?);
|
||||
|
||||
state
|
||||
.request
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.reconfigure_lines(&config)
|
||||
.map_err(Error::GpiodFailed)
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_for_interrupt(&self, gpio: u16) -> Result<()> {
|
||||
// While waiting here for the interrupt to occur, it is possible that we receive another
|
||||
// request from the guest to disable the interrupt instead, via a call to `set_irq_type()`.
|
||||
//
|
||||
// The interrupt design here should allow that call to return as soon as possible, after
|
||||
// disabling the interrupt and at the same time we need to make sure that we don't end up
|
||||
// freeing resources currently used by `wait_for_interrupt()`.
|
||||
//
|
||||
// To allow that, the line state management is done via two resources: `request` and
|
||||
// `buffer`.
|
||||
//
|
||||
// The `request` is required by `wait_for_interrupt()` to query libgpiod and must not get
|
||||
// freed while we are waiting for an interrupt. This can happen, for example, if another
|
||||
// thread disables the interrupt, via `set_irq_type(VIRTIO_GPIO_IRQ_TYPE_NONE)`, followed
|
||||
// by `set_direction(VIRTIO_GPIO_DIRECTION_NONE)`, where we drop the `request`. For this
|
||||
// reason, the `request` is implemented as an Arc instance.
|
||||
//
|
||||
// The `buffer` on the other hand is required only after we have sensed an interrupt and
|
||||
// need to read it. The design here takes advantage of that and allows `set_irq_type()` to
|
||||
// go and free the `buffer`, while this routine is waiting for the interrupt. Once the
|
||||
// waiting period is over or an interrupt is sensed, `wait_for_interrupt() will find the
|
||||
// buffer being dropped and return an error which will be handled by
|
||||
// `Controller::wait_for_interrupt()`.
|
||||
//
|
||||
// This design also allows `wait_for_interrupt()` to not take a lock for the entire
|
||||
// duration, which can potentially also starve the other thread trying to disable the
|
||||
// interrupt.
|
||||
let request = {
|
||||
let state = &self.state[gpio as usize].write().unwrap();
|
||||
|
||||
match &state.request {
|
||||
Some(x) => x.clone(),
|
||||
None => return Err(Error::GpioIrqNotEnabled),
|
||||
}
|
||||
};
|
||||
|
||||
// Wait for the interrupt for a second.
|
||||
match request.edge_event_wait(Duration::new(1, 0)) {
|
||||
Err(LibGpiodError::OperationTimedOut) => return Err(Error::GpioIrqOpTimedOut),
|
||||
x => x.map_err(Error::GpiodFailed)?,
|
||||
}
|
||||
|
||||
// The interrupt has already occurred, we can lock now just fine.
|
||||
let state = &self.state[gpio as usize].write().unwrap();
|
||||
if let Some(buffer) = &state.buffer {
|
||||
request
|
||||
.edge_event_read(buffer, 1)
|
||||
.map_err(Error::GpiodFailed)?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::GpioLineRequestNotConfigured)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GpioState {
|
||||
dir: u8,
|
||||
val: Option<u16>,
|
||||
irq_type: u16,
|
||||
}
|
||||
|
||||
pub(crate) struct GpioController<D: GpioDevice> {
|
||||
@ -229,6 +360,7 @@ impl<D: GpioDevice> GpioController<D> {
|
||||
state.push(RwLock::new(GpioState {
|
||||
dir: device.get_direction(offset)?,
|
||||
val: None,
|
||||
irq_type: VIRTIO_GPIO_IRQ_TYPE_NONE,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -244,6 +376,10 @@ impl<D: GpioDevice> GpioController<D> {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_num_gpios(&self) -> u16 {
|
||||
self.device.get_num_gpios().unwrap()
|
||||
}
|
||||
|
||||
fn get_direction(&self, gpio: u16) -> Result<u8> {
|
||||
self.device.get_direction(gpio)
|
||||
}
|
||||
@ -285,6 +421,47 @@ impl<D: GpioDevice> GpioController<D> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_irq_type(&self, gpio: u16) -> u16 {
|
||||
self.state[gpio as usize].read().unwrap().irq_type
|
||||
}
|
||||
|
||||
fn set_irq_type(&self, gpio: u16, value: u32) -> Result<()> {
|
||||
let irq_type = value as u16;
|
||||
|
||||
// Invalid irq type
|
||||
if (irq_type & !VIRTIO_GPIO_IRQ_TYPE_ALL) != 0 {
|
||||
return Err(Error::GpioIrqTypeInvalid(irq_type));
|
||||
}
|
||||
|
||||
// Begin critical section
|
||||
let state = &mut self.state[gpio as usize].write().unwrap();
|
||||
let prev_irq_type = state.irq_type;
|
||||
|
||||
// New irq type same as current one.
|
||||
if irq_type == prev_irq_type {
|
||||
return Err(Error::GpioIrqTypeNoChange(irq_type));
|
||||
}
|
||||
|
||||
// Must configure to VIRTIO_GPIO_IRQ_TYPE_NONE first before changing irq type.
|
||||
if prev_irq_type != VIRTIO_GPIO_IRQ_TYPE_NONE && irq_type != VIRTIO_GPIO_IRQ_TYPE_NONE {
|
||||
return Err(Error::GpioOldIrqTypeValid(prev_irq_type));
|
||||
}
|
||||
|
||||
self.device.set_irq_type(gpio, irq_type)?;
|
||||
state.irq_type = irq_type;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn wait_for_interrupt(&self, gpio: u16) -> Result<()> {
|
||||
loop {
|
||||
match self.device.wait_for_interrupt(gpio) {
|
||||
Err(Error::GpioIrqOpTimedOut) => continue,
|
||||
Ok(_) => return Ok(()),
|
||||
x => x?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_config(&self) -> &VirtioGpioConfig {
|
||||
&self.config
|
||||
}
|
||||
@ -302,6 +479,10 @@ impl<D: GpioDevice> GpioController<D> {
|
||||
self.set_value(gpio, value)?;
|
||||
vec![0]
|
||||
}
|
||||
VIRTIO_GPIO_MSG_IRQ_TYPE => {
|
||||
self.set_irq_type(gpio, value)?;
|
||||
vec![0]
|
||||
}
|
||||
msg => return Err(Error::GpioMessageInvalid(msg)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
use log::error;
|
||||
use std::mem::size_of;
|
||||
use std::slice::from_raw_parts;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread::{spawn, JoinHandle};
|
||||
use std::{convert, io};
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
@ -19,22 +21,27 @@ use virtio_bindings::bindings::virtio_ring::{
|
||||
};
|
||||
use virtio_queue::DescriptorChain;
|
||||
use vm_memory::{
|
||||
ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, Le16, Le32,
|
||||
ByteValued, Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap,
|
||||
Le16, Le32,
|
||||
};
|
||||
use vmm_sys_util::epoll::EventSet;
|
||||
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
|
||||
|
||||
use crate::gpio::{GpioController, GpioDevice, VirtioGpioConfig};
|
||||
use crate::gpio::{GpioController, GpioDevice, VirtioGpioConfig, VIRTIO_GPIO_IRQ_TYPE_NONE};
|
||||
|
||||
/// Possible values of the status field
|
||||
const VIRTIO_GPIO_STATUS_OK: u8 = 0x0;
|
||||
const VIRTIO_GPIO_STATUS_ERR: u8 = 0x1;
|
||||
|
||||
/// Virtio GPIO Feature bits
|
||||
const VIRTIO_GPIO_F_IRQ: u16 = 0;
|
||||
|
||||
const QUEUE_SIZE: usize = 256;
|
||||
const NUM_QUEUES: usize = 2;
|
||||
|
||||
/// Queues
|
||||
const REQUEST_QUEUE: u16 = 0;
|
||||
const EVENT_QUEUE: u16 = 1;
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
type VhostUserBackendResult<T> = std::result::Result<T, std::io::Error>;
|
||||
@ -85,8 +92,60 @@ struct VirtioGpioRequest {
|
||||
}
|
||||
unsafe impl ByteValued for VirtioGpioRequest {}
|
||||
|
||||
/// Virtio GPIO IRQ Request / Response
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct VirtioGpioIrqRequest {
|
||||
gpio: Le16,
|
||||
}
|
||||
unsafe impl ByteValued for VirtioGpioIrqRequest {}
|
||||
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct VirtioGpioIrqResponse {
|
||||
#[allow(dead_code)]
|
||||
status: u8,
|
||||
}
|
||||
unsafe impl ByteValued for VirtioGpioIrqResponse {}
|
||||
|
||||
/// Possible values of the interrupt status field
|
||||
const VIRTIO_GPIO_IRQ_STATUS_INVALID: u8 = 0x0;
|
||||
const VIRTIO_GPIO_IRQ_STATUS_VALID: u8 = 0x1;
|
||||
|
||||
/// Send response over the eventq virtqueue.
|
||||
fn send_event_response(
|
||||
vring: &VringRwLock,
|
||||
desc_chain: GpioDescriptorChain,
|
||||
addr: GuestAddress,
|
||||
status: u8,
|
||||
) {
|
||||
let response = VirtioGpioIrqResponse { status };
|
||||
|
||||
if desc_chain
|
||||
.memory()
|
||||
.write_obj::<VirtioGpioIrqResponse>(response, addr)
|
||||
.is_err()
|
||||
{
|
||||
error!("Failed to write response");
|
||||
}
|
||||
|
||||
if vring
|
||||
.add_used(
|
||||
desc_chain.head_index(),
|
||||
size_of::<VirtioGpioIrqResponse>() as u32,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
error!("Couldn't return used descriptors to the ring");
|
||||
}
|
||||
|
||||
// Send notification once all the requests are processed
|
||||
if vring.signal_used_queue().is_err() {
|
||||
error!("Couldn't signal used queue");
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct VhostUserGpioBackend<D: GpioDevice> {
|
||||
controller: GpioController<D>,
|
||||
controller: Arc<GpioController<D>>,
|
||||
handles: Arc<RwLock<Vec<Option<JoinHandle<()>>>>>,
|
||||
event_idx: bool,
|
||||
pub(crate) exit_event: EventFd,
|
||||
}
|
||||
@ -95,8 +154,13 @@ type GpioDescriptorChain = DescriptorChain<GuestMemoryLoadGuard<GuestMemoryMmap<
|
||||
|
||||
impl<D: GpioDevice> VhostUserGpioBackend<D> {
|
||||
pub(crate) fn new(controller: GpioController<D>) -> Result<Self> {
|
||||
// Can't set a vector to all None easily
|
||||
let mut handles: Vec<Option<JoinHandle<()>>> = Vec::new();
|
||||
handles.resize_with(controller.get_num_gpios() as usize, || None);
|
||||
|
||||
Ok(VhostUserGpioBackend {
|
||||
controller,
|
||||
controller: Arc::new(controller),
|
||||
handles: Arc::new(RwLock::new(handles)),
|
||||
event_idx: false,
|
||||
exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| Error::EventFdFailed)?,
|
||||
})
|
||||
@ -200,6 +264,108 @@ impl<D: GpioDevice> VhostUserGpioBackend<D> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process the request in the event queue
|
||||
fn process_event(
|
||||
&mut self,
|
||||
vring: &VringRwLock,
|
||||
desc_chain: GpioDescriptorChain,
|
||||
gpio: u16,
|
||||
addr: GuestAddress,
|
||||
) {
|
||||
// Take lock here to avoid race with thread.
|
||||
let handle = &mut self.handles.write().unwrap()[gpio as usize];
|
||||
let controller = self.controller.clone();
|
||||
|
||||
// Interrupt should be enabled before sending buffer and no other buffer
|
||||
// should have been received earlier for this GPIO pin.
|
||||
if controller.get_irq_type(gpio) == VIRTIO_GPIO_IRQ_TYPE_NONE || handle.is_some() {
|
||||
send_event_response(vring, desc_chain, addr, VIRTIO_GPIO_IRQ_STATUS_INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue a thread to wait for and process the interrupt.
|
||||
let handles = self.handles.clone();
|
||||
let vring = vring.clone();
|
||||
*handle = Some(spawn(move || {
|
||||
let status = match controller.wait_for_interrupt(gpio) {
|
||||
Ok(_) => VIRTIO_GPIO_IRQ_STATUS_VALID,
|
||||
_ => VIRTIO_GPIO_IRQ_STATUS_INVALID,
|
||||
};
|
||||
|
||||
send_event_response(&vring, desc_chain, addr, status);
|
||||
handles.write().unwrap()[gpio as usize] = None;
|
||||
}));
|
||||
}
|
||||
|
||||
/// Process the requests in the event queue
|
||||
fn process_events(
|
||||
&mut self,
|
||||
requests: Vec<GpioDescriptorChain>,
|
||||
vring: &VringRwLock,
|
||||
) -> Result<()> {
|
||||
if requests.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for desc_chain in requests {
|
||||
let descriptors: Vec<_> = desc_chain.clone().collect();
|
||||
if descriptors.len() != 2 {
|
||||
return Err(Error::UnexpectedDescriptorCount(descriptors.len()));
|
||||
}
|
||||
|
||||
let desc_request = descriptors[0];
|
||||
if desc_request.is_write_only() {
|
||||
return Err(Error::UnexpectedWriteOnlyDescriptor(0));
|
||||
}
|
||||
|
||||
if desc_request.len() as usize != size_of::<VirtioGpioIrqRequest>() {
|
||||
return Err(Error::UnexpectedDescriptorSize(
|
||||
size_of::<VirtioGpioIrqRequest>(),
|
||||
desc_request.len(),
|
||||
));
|
||||
}
|
||||
|
||||
let request = desc_chain
|
||||
.memory()
|
||||
.read_obj::<VirtioGpioIrqRequest>(desc_request.addr())
|
||||
.map_err(|_| Error::DescriptorReadFailed)?;
|
||||
|
||||
let desc_response = descriptors[1];
|
||||
if !desc_response.is_write_only() {
|
||||
return Err(Error::UnexpectedReadableDescriptor(1));
|
||||
}
|
||||
|
||||
if desc_response.len() as usize != size_of::<VirtioGpioIrqResponse>() {
|
||||
return Err(Error::UnexpectedDescriptorSize(
|
||||
size_of::<VirtioGpioIrqResponse>(),
|
||||
desc_response.len(),
|
||||
));
|
||||
}
|
||||
|
||||
self.process_event(
|
||||
vring,
|
||||
desc_chain,
|
||||
request.gpio.to_native(),
|
||||
desc_response.addr(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process the messages in the vring and dispatch replies
|
||||
fn process_event_queue(&mut self, vring: &VringRwLock) -> Result<()> {
|
||||
let requests: Vec<_> = vring
|
||||
.get_mut()
|
||||
.get_queue_mut()
|
||||
.iter()
|
||||
.map_err(|_| Error::DescriptorNotFound)?
|
||||
.collect();
|
||||
|
||||
self.process_events(requests, vring)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// VhostUserBackendMut trait methods
|
||||
@ -220,6 +386,7 @@ impl<D: 'static + GpioDevice + Sync + Send> VhostUserBackendMut<VringRwLock, ()>
|
||||
| 1 << VIRTIO_F_NOTIFY_ON_EMPTY
|
||||
| 1 << VIRTIO_RING_F_INDIRECT_DESC
|
||||
| 1 << VIRTIO_RING_F_EVENT_IDX
|
||||
| 1 << VIRTIO_GPIO_F_IRQ
|
||||
| VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits()
|
||||
}
|
||||
|
||||
@ -281,6 +448,27 @@ impl<D: 'static + GpioDevice + Sync + Send> VhostUserBackendMut<VringRwLock, ()>
|
||||
}
|
||||
}
|
||||
|
||||
EVENT_QUEUE => {
|
||||
let vring = &vrings[1];
|
||||
|
||||
if self.event_idx {
|
||||
// vm-virtio's Queue implementation only checks avail_index
|
||||
// once, so to properly support EVENT_IDX we need to keep
|
||||
// calling process_event_queue() until it stops finding
|
||||
// new requests on the queue.
|
||||
loop {
|
||||
vring.disable_notification().unwrap();
|
||||
self.process_event_queue(vring)?;
|
||||
if !vring.enable_notification().unwrap() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Without EVENT_IDX, a single call is enough.
|
||||
self.process_event_queue(vring)?;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(Error::HandleEventUnknown.into());
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user