// Copyright 2019 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 file.
use super::{INTERRUPT_STATUS_CONFIG_CHANGED, INTERRUPT_STATUS_USED_RING, VIRTIO_MSI_NO_VECTOR};
use crate::pci::MsixConfig;
use msg_socket::{MsgError, MsgOnSocket, MsgSender};
use std::fmt::Debug;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use sync::Mutex;
use sys_util::EventFd;
pub trait Interrupt: Send + Sync {
type Error;
/// Notify the driver that buffers have been placed in the used queue.
fn signal_used_queue(&self, vector: u16) -> Result<(), Self::Error>;
/// Notify the driver that the device configuration has changed.
fn signal_config_changed(&self) -> Result<(), Self::Error>;
/// Handle interrupt resampling event
fn interrupt_resample(&self) -> Result<(), Self::Error>;
fn msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>>;
/// Return the reference of interrupt_resample_evt
/// To keep the interface clean, this member is private.
fn get_resample_evt(&self) -> &EventFd;
}
#[derive(Copy, Clone, Debug, MsgOnSocket)]
pub enum InterruptProxyEvent {
SignalUsedQueue(u16),
SignalConfigChanged,
InterruptResample,
}
#[derive(Debug)]
pub struct InterruptProxy<Socket> {
socket: Socket,
interrupt_resample_evt: EventFd,
}
impl<Socket> InterruptProxy<Socket> {
pub fn new(socket: Socket, interrupt_resample_evt: EventFd) -> Self {
Self {
socket,
interrupt_resample_evt,
}
}
}
#[derive(Debug)]
pub enum ProxyError {
Socket(MsgError),
}
use InterruptProxyEvent::*;
impl<Socket> Interrupt for InterruptProxy<Socket>
where
Socket: MsgSender<M = InterruptProxyEvent> + Sync + Send,
{
type Error = Box<dyn Debug>;
fn signal_used_queue(&self, vector: u16) -> Result<(), Self::Error> {
self.socket
.send(&SignalUsedQueue(vector))
.map_err(|e| Box::new(ProxyError::Socket(e)) as Box<dyn Debug>)
}
fn signal_config_changed(&self) -> Result<(), Self::Error> {
self.socket
.send(&SignalConfigChanged)
.map_err(|e| Box::new(ProxyError::Socket(e)) as Box<dyn Debug>)
}
fn interrupt_resample(&self) -> Result<(), Self::Error> {
self.socket
.send(&InterruptResample)
.map_err(|e| Box::new(ProxyError::Socket(e)) as Box<dyn Debug>)
}
fn msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
// Don't make any attempt to forward an msix_config for now,
// because the amount of round-tripping a naive implementation
// would require outweighs any benefits of not going through
// crosvm vhost-net.
&None
}
fn get_resample_evt(&self) -> &EventFd {
&self.interrupt_resample_evt
}
}
pub struct InterruptImpl {
interrupt_status: Arc<AtomicUsize>,
interrupt_evt: EventFd,
interrupt_resample_evt: EventFd,
msix_config: Option<Arc<Mutex<MsixConfig>>>,
config_msix_vector: u16,
}
impl InterruptImpl {
pub fn new(
interrupt_status: Arc<AtomicUsize>,
interrupt_evt: EventFd,
interrupt_resample_evt: EventFd,
msix_config: Option<Arc<Mutex<MsixConfig>>>,
config_msix_vector: u16,
) -> Self {
Self {
interrupt_status,
interrupt_evt,
interrupt_resample_evt,
msix_config,
config_msix_vector,
}
}
/// Virtqueue Interrupts From The Device
///
/// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
/// Write to the irqfd to VMM to deliver virtual interrupt to the guest
fn signal(&self, vector: u16, interrupt_status_mask: u32) {
// Don't need to set ISR for MSI-X interrupts
if let Some(msix_config) = &self.msix_config {
let mut msix_config = msix_config.lock();
if msix_config.enabled() {
if vector != VIRTIO_MSI_NO_VECTOR {
msix_config.trigger(vector);
}
return;
}
}
// Set bit in ISR and inject the interrupt if it was not already pending.
// Don't need to inject the interrupt if the guest hasn't processed it.
if self
.interrupt_status
.fetch_or(interrupt_status_mask as usize, Ordering::SeqCst)
== 0
{
// Write to irqfd to inject INTx interrupt
self.interrupt_evt.write(1).unwrap();
}
}
}
impl Interrupt for InterruptImpl {
type Error = Box<dyn Debug>;
fn signal_used_queue(&self, vector: u16) -> Result<(), Self::Error> {
self.signal(vector, INTERRUPT_STATUS_USED_RING);
Ok(())
}
fn signal_config_changed(&self) -> Result<(), Self::Error> {
self.signal(self.config_msix_vector, INTERRUPT_STATUS_CONFIG_CHANGED);
Ok(())
}
fn interrupt_resample(&self) -> Result<(), Self::Error> {
let _ = self.interrupt_resample_evt.read();
if self.interrupt_status.load(Ordering::SeqCst) != 0 {
self.interrupt_evt.write(1).unwrap();
}
Ok(())
}
fn msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
&self.msix_config
}
fn get_resample_evt(&self) -> &EventFd {
&self.interrupt_resample_evt
}
}