virt/
irqfd.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Traits for irqfd-based interrupt delivery.
5//!
6//! irqfd allows a hypervisor to directly inject an MSI into a guest when an
7//! event is signaled, without involving userspace in the interrupt delivery
8//! path. This is used for device passthrough (e.g., VFIO) where the physical
9//! device signals an event and the hypervisor injects the corresponding MSI
10//! into the guest VM.
11
12use pal_event::Event;
13
14/// Trait for partitions that support irqfd-based interrupt delivery.
15///
16/// An irqfd associates an event with a GSI (Global System Interrupt), and a
17/// GSI routing table maps GSIs to MSI addresses and data values. When the
18/// event is signaled, the kernel looks up the GSI routing and injects the
19/// configured MSI into the guest without a usermode transition.
20pub trait IrqFd: Send + Sync {
21    /// Creates a new irqfd route.
22    ///
23    /// Allocates a GSI, creates an event, and registers the event with the
24    /// hypervisor so that signaling it injects the configured MSI into the
25    /// guest.
26    ///
27    /// The caller retrieves the event via [`IrqFdRoute::event`] to pass to
28    /// VFIO or other interrupt sources.
29    ///
30    /// When the route is dropped, the irqfd is unregistered and the GSI is
31    /// freed.
32    fn new_irqfd_route(&self) -> anyhow::Result<Box<dyn IrqFdRoute>>;
33}
34
35/// A handle to a registered irqfd route.
36///
37/// Each route represents a single GSI with an associated event. When the
38/// event is signaled (e.g., by VFIO on a device interrupt), the kernel injects
39/// the MSI configured via [`set_msi`](IrqFdRoute::set_msi) into the guest.
40///
41/// Dropping this handle unregisters the irqfd and frees the GSI.
42pub trait IrqFdRoute: Send + Sync {
43    /// Returns the event that triggers interrupt injection when signaled.
44    ///
45    /// Pass this to VFIO `map_msix` or any other interrupt source. On Linux,
46    /// this is an eventfd created by the implementation. On WHP (future), this
47    /// is the event handle returned by `WHvCreateTrigger`.
48    fn event(&self) -> &Event;
49
50    /// Sets the MSI routing for this irqfd's GSI.
51    ///
52    /// `address` and `data` are the x86 MSI address and data values that the
53    /// kernel will use when injecting the interrupt into the guest.
54    fn set_msi(&self, address: u64, data: u32) -> anyhow::Result<()>;
55
56    /// Clears the MSI routing for this irqfd's GSI.
57    ///
58    /// The irqfd remains registered but interrupt delivery is disabled until
59    /// a new route is configured via [`set_msi`](IrqFdRoute::set_msi).
60    fn clear_msi(&self) -> anyhow::Result<()>;
61
62    /// Masks the route.
63    ///
64    /// While masked, interrupts arriving on the event are not injected into
65    /// the guest. The caller should use [`consume_pending`](IrqFdRoute::consume_pending)
66    /// to check whether an interrupt arrived while masked and store the
67    /// result in the MSI-X PBA. On unmask, the caller should deliver any
68    /// pending interrupt from the PBA before re-enabling the route.
69    fn mask(&self) -> anyhow::Result<()>;
70
71    /// Unmasks the route and re-enables interrupt injection.
72    fn unmask(&self) -> anyhow::Result<()>;
73
74    /// Drains the pending interrupt state and returns whether an interrupt
75    /// was pending.
76    ///
77    /// This atomically reads and clears the event's counter. The caller
78    /// should store the result in the MSI-X PBA (Pending Bit Array).
79    /// Repeated calls after the first drain will return `false` until a
80    /// new interrupt arrives, so the caller must persist the pending state
81    /// externally (e.g., in the MSI-X emulator's PBA bits).
82    fn consume_pending(&self) -> bool {
83        self.event().try_wait()
84    }
85}