xref: /aosp_15_r20/external/crosvm/devices/src/virtio/interrupt.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2019 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::fmt;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::atomic::AtomicUsize;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::atomic::Ordering;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::Arc;
9*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
10*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Instant;
11*bb4ee6a4SAndroid Build Coastguard Worker 
12*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
13*bb4ee6a4SAndroid Build Coastguard Worker use base::error;
14*bb4ee6a4SAndroid Build Coastguard Worker use base::Event;
15*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
16*bb4ee6a4SAndroid Build Coastguard Worker use metrics::log_metric;
17*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
18*bb4ee6a4SAndroid Build Coastguard Worker use metrics::MetricEventType;
19*bb4ee6a4SAndroid Build Coastguard Worker use serde::Deserialize;
20*bb4ee6a4SAndroid Build Coastguard Worker use serde::Serialize;
21*bb4ee6a4SAndroid Build Coastguard Worker use sync::Mutex;
22*bb4ee6a4SAndroid Build Coastguard Worker 
23*bb4ee6a4SAndroid Build Coastguard Worker use super::INTERRUPT_STATUS_CONFIG_CHANGED;
24*bb4ee6a4SAndroid Build Coastguard Worker use super::INTERRUPT_STATUS_USED_RING;
25*bb4ee6a4SAndroid Build Coastguard Worker use super::VIRTIO_MSI_NO_VECTOR;
26*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
27*bb4ee6a4SAndroid Build Coastguard Worker use crate::acpi::PmWakeupEvent;
28*bb4ee6a4SAndroid Build Coastguard Worker use crate::irq_event::IrqEdgeEvent;
29*bb4ee6a4SAndroid Build Coastguard Worker use crate::irq_event::IrqLevelEvent;
30*bb4ee6a4SAndroid Build Coastguard Worker use crate::pci::MsixConfig;
31*bb4ee6a4SAndroid Build Coastguard Worker 
32*bb4ee6a4SAndroid Build Coastguard Worker struct TransportPci {
33*bb4ee6a4SAndroid Build Coastguard Worker     irq_evt_lvl: IrqLevelEvent,
34*bb4ee6a4SAndroid Build Coastguard Worker     msix_config: Option<Arc<Mutex<MsixConfig>>>,
35*bb4ee6a4SAndroid Build Coastguard Worker     config_msix_vector: u16,
36*bb4ee6a4SAndroid Build Coastguard Worker }
37*bb4ee6a4SAndroid Build Coastguard Worker 
38*bb4ee6a4SAndroid Build Coastguard Worker enum Transport {
39*bb4ee6a4SAndroid Build Coastguard Worker     Pci {
40*bb4ee6a4SAndroid Build Coastguard Worker         pci: TransportPci,
41*bb4ee6a4SAndroid Build Coastguard Worker     },
42*bb4ee6a4SAndroid Build Coastguard Worker     Mmio {
43*bb4ee6a4SAndroid Build Coastguard Worker         irq_evt_edge: IrqEdgeEvent,
44*bb4ee6a4SAndroid Build Coastguard Worker     },
45*bb4ee6a4SAndroid Build Coastguard Worker     VhostUser {
46*bb4ee6a4SAndroid Build Coastguard Worker         call_evt: Event,
47*bb4ee6a4SAndroid Build Coastguard Worker         signal_config_changed_fn: Box<dyn Fn() + Send + Sync>,
48*bb4ee6a4SAndroid Build Coastguard Worker     },
49*bb4ee6a4SAndroid Build Coastguard Worker }
50*bb4ee6a4SAndroid Build Coastguard Worker 
51*bb4ee6a4SAndroid Build Coastguard Worker struct InterruptInner {
52*bb4ee6a4SAndroid Build Coastguard Worker     interrupt_status: AtomicUsize,
53*bb4ee6a4SAndroid Build Coastguard Worker     transport: Transport,
54*bb4ee6a4SAndroid Build Coastguard Worker     async_intr_status: bool,
55*bb4ee6a4SAndroid Build Coastguard Worker     pm_state: Arc<Mutex<PmState>>,
56*bb4ee6a4SAndroid Build Coastguard Worker }
57*bb4ee6a4SAndroid Build Coastguard Worker 
58*bb4ee6a4SAndroid Build Coastguard Worker impl InterruptInner {
59*bb4ee6a4SAndroid Build Coastguard Worker     /// Add `interrupt_status_mask` to any existing interrupt status.
60*bb4ee6a4SAndroid Build Coastguard Worker     ///
61*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns `true` if the interrupt should be triggered after this update.
update_interrupt_status(&self, interrupt_status_mask: u32) -> bool62*bb4ee6a4SAndroid Build Coastguard Worker     fn update_interrupt_status(&self, interrupt_status_mask: u32) -> bool {
63*bb4ee6a4SAndroid Build Coastguard Worker         // Set bit in ISR and inject the interrupt if it was not already pending.
64*bb4ee6a4SAndroid Build Coastguard Worker         // Don't need to inject the interrupt if the guest hasn't processed it.
65*bb4ee6a4SAndroid Build Coastguard Worker         // In hypervisors where interrupt_status is updated asynchronously, inject the
66*bb4ee6a4SAndroid Build Coastguard Worker         // interrupt even if the previous interrupt appears to be already pending.
67*bb4ee6a4SAndroid Build Coastguard Worker         self.interrupt_status
68*bb4ee6a4SAndroid Build Coastguard Worker             .fetch_or(interrupt_status_mask as usize, Ordering::SeqCst)
69*bb4ee6a4SAndroid Build Coastguard Worker             == 0
70*bb4ee6a4SAndroid Build Coastguard Worker             || self.async_intr_status
71*bb4ee6a4SAndroid Build Coastguard Worker     }
72*bb4ee6a4SAndroid Build Coastguard Worker }
73*bb4ee6a4SAndroid Build Coastguard Worker 
74*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone)]
75*bb4ee6a4SAndroid Build Coastguard Worker pub struct Interrupt {
76*bb4ee6a4SAndroid Build Coastguard Worker     inner: Arc<InterruptInner>,
77*bb4ee6a4SAndroid Build Coastguard Worker }
78*bb4ee6a4SAndroid Build Coastguard Worker 
79*bb4ee6a4SAndroid Build Coastguard Worker impl fmt::Debug for Interrupt {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80*bb4ee6a4SAndroid Build Coastguard Worker     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81*bb4ee6a4SAndroid Build Coastguard Worker         write!(f, "Interrupt")
82*bb4ee6a4SAndroid Build Coastguard Worker     }
83*bb4ee6a4SAndroid Build Coastguard Worker }
84*bb4ee6a4SAndroid Build Coastguard Worker 
85*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Serialize, Deserialize)]
86*bb4ee6a4SAndroid Build Coastguard Worker pub struct InterruptSnapshot {
87*bb4ee6a4SAndroid Build Coastguard Worker     interrupt_status: usize,
88*bb4ee6a4SAndroid Build Coastguard Worker }
89*bb4ee6a4SAndroid Build Coastguard Worker 
90*bb4ee6a4SAndroid Build Coastguard Worker impl Interrupt {
91*bb4ee6a4SAndroid Build Coastguard Worker     /// Writes to the irqfd to VMM to deliver virtual interrupt to the guest.
92*bb4ee6a4SAndroid Build Coastguard Worker     ///
93*bb4ee6a4SAndroid Build Coastguard Worker     /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
94*bb4ee6a4SAndroid Build Coastguard Worker     /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
signal(&self, vector: u16, interrupt_status_mask: u32)95*bb4ee6a4SAndroid Build Coastguard Worker     pub fn signal(&self, vector: u16, interrupt_status_mask: u32) {
96*bb4ee6a4SAndroid Build Coastguard Worker         if self
97*bb4ee6a4SAndroid Build Coastguard Worker             .inner
98*bb4ee6a4SAndroid Build Coastguard Worker             .pm_state
99*bb4ee6a4SAndroid Build Coastguard Worker             .lock()
100*bb4ee6a4SAndroid Build Coastguard Worker             .handle_interrupt(vector, interrupt_status_mask)
101*bb4ee6a4SAndroid Build Coastguard Worker         {
102*bb4ee6a4SAndroid Build Coastguard Worker             return;
103*bb4ee6a4SAndroid Build Coastguard Worker         }
104*bb4ee6a4SAndroid Build Coastguard Worker 
105*bb4ee6a4SAndroid Build Coastguard Worker         match &self.inner.transport {
106*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Pci { pci } => {
107*bb4ee6a4SAndroid Build Coastguard Worker                 // Don't need to set ISR for MSI-X interrupts
108*bb4ee6a4SAndroid Build Coastguard Worker                 if let Some(msix_config) = &pci.msix_config {
109*bb4ee6a4SAndroid Build Coastguard Worker                     let mut msix_config = msix_config.lock();
110*bb4ee6a4SAndroid Build Coastguard Worker                     if msix_config.enabled() {
111*bb4ee6a4SAndroid Build Coastguard Worker                         if vector != VIRTIO_MSI_NO_VECTOR {
112*bb4ee6a4SAndroid Build Coastguard Worker                             msix_config.trigger(vector);
113*bb4ee6a4SAndroid Build Coastguard Worker                         }
114*bb4ee6a4SAndroid Build Coastguard Worker                         return;
115*bb4ee6a4SAndroid Build Coastguard Worker                     }
116*bb4ee6a4SAndroid Build Coastguard Worker                 }
117*bb4ee6a4SAndroid Build Coastguard Worker 
118*bb4ee6a4SAndroid Build Coastguard Worker                 if self.inner.update_interrupt_status(interrupt_status_mask) {
119*bb4ee6a4SAndroid Build Coastguard Worker                     pci.irq_evt_lvl.trigger().unwrap();
120*bb4ee6a4SAndroid Build Coastguard Worker                 }
121*bb4ee6a4SAndroid Build Coastguard Worker             }
122*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Mmio { irq_evt_edge } => {
123*bb4ee6a4SAndroid Build Coastguard Worker                 if self.inner.update_interrupt_status(interrupt_status_mask) {
124*bb4ee6a4SAndroid Build Coastguard Worker                     irq_evt_edge.trigger().unwrap();
125*bb4ee6a4SAndroid Build Coastguard Worker                 }
126*bb4ee6a4SAndroid Build Coastguard Worker             }
127*bb4ee6a4SAndroid Build Coastguard Worker             Transport::VhostUser { call_evt, .. } => {
128*bb4ee6a4SAndroid Build Coastguard Worker                 // TODO(b/187487351): To avoid sending unnecessary events, we might want to support
129*bb4ee6a4SAndroid Build Coastguard Worker                 // interrupt status. For this purpose, we need a mechanism to share interrupt status
130*bb4ee6a4SAndroid Build Coastguard Worker                 // between the vmm and the device process.
131*bb4ee6a4SAndroid Build Coastguard Worker                 call_evt.signal().unwrap();
132*bb4ee6a4SAndroid Build Coastguard Worker             }
133*bb4ee6a4SAndroid Build Coastguard Worker         }
134*bb4ee6a4SAndroid Build Coastguard Worker     }
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker     /// Notify the driver that buffers have been placed in the used queue.
signal_used_queue(&self, vector: u16)137*bb4ee6a4SAndroid Build Coastguard Worker     pub fn signal_used_queue(&self, vector: u16) {
138*bb4ee6a4SAndroid Build Coastguard Worker         self.signal(vector, INTERRUPT_STATUS_USED_RING)
139*bb4ee6a4SAndroid Build Coastguard Worker     }
140*bb4ee6a4SAndroid Build Coastguard Worker 
141*bb4ee6a4SAndroid Build Coastguard Worker     /// Notify the driver that the device configuration has changed.
signal_config_changed(&self)142*bb4ee6a4SAndroid Build Coastguard Worker     pub fn signal_config_changed(&self) {
143*bb4ee6a4SAndroid Build Coastguard Worker         match &self.inner.as_ref().transport {
144*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Pci { pci } => {
145*bb4ee6a4SAndroid Build Coastguard Worker                 self.signal(pci.config_msix_vector, INTERRUPT_STATUS_CONFIG_CHANGED)
146*bb4ee6a4SAndroid Build Coastguard Worker             }
147*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Mmio { .. } => {
148*bb4ee6a4SAndroid Build Coastguard Worker                 self.signal(VIRTIO_MSI_NO_VECTOR, INTERRUPT_STATUS_CONFIG_CHANGED)
149*bb4ee6a4SAndroid Build Coastguard Worker             }
150*bb4ee6a4SAndroid Build Coastguard Worker             Transport::VhostUser {
151*bb4ee6a4SAndroid Build Coastguard Worker                 signal_config_changed_fn,
152*bb4ee6a4SAndroid Build Coastguard Worker                 ..
153*bb4ee6a4SAndroid Build Coastguard Worker             } => signal_config_changed_fn(),
154*bb4ee6a4SAndroid Build Coastguard Worker         }
155*bb4ee6a4SAndroid Build Coastguard Worker     }
156*bb4ee6a4SAndroid Build Coastguard Worker 
157*bb4ee6a4SAndroid Build Coastguard Worker     /// Get the event to signal resampling is needed if it exists.
get_resample_evt(&self) -> Option<&Event>158*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_resample_evt(&self) -> Option<&Event> {
159*bb4ee6a4SAndroid Build Coastguard Worker         match &self.inner.as_ref().transport {
160*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Pci { pci } => Some(pci.irq_evt_lvl.get_resample()),
161*bb4ee6a4SAndroid Build Coastguard Worker             _ => None,
162*bb4ee6a4SAndroid Build Coastguard Worker         }
163*bb4ee6a4SAndroid Build Coastguard Worker     }
164*bb4ee6a4SAndroid Build Coastguard Worker 
165*bb4ee6a4SAndroid Build Coastguard Worker     /// Reads the status and writes to the interrupt event. Doesn't read the resample event, it
166*bb4ee6a4SAndroid Build Coastguard Worker     /// assumes the resample has been requested.
do_interrupt_resample(&self)167*bb4ee6a4SAndroid Build Coastguard Worker     pub fn do_interrupt_resample(&self) {
168*bb4ee6a4SAndroid Build Coastguard Worker         if self.inner.interrupt_status.load(Ordering::SeqCst) != 0 {
169*bb4ee6a4SAndroid Build Coastguard Worker             match &self.inner.as_ref().transport {
170*bb4ee6a4SAndroid Build Coastguard Worker                 Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(),
171*bb4ee6a4SAndroid Build Coastguard Worker                 _ => panic!("do_interrupt_resample() not supported"),
172*bb4ee6a4SAndroid Build Coastguard Worker             }
173*bb4ee6a4SAndroid Build Coastguard Worker         }
174*bb4ee6a4SAndroid Build Coastguard Worker     }
175*bb4ee6a4SAndroid Build Coastguard Worker }
176*bb4ee6a4SAndroid Build Coastguard Worker 
177*bb4ee6a4SAndroid Build Coastguard Worker impl Interrupt {
new( irq_evt_lvl: IrqLevelEvent, msix_config: Option<Arc<Mutex<MsixConfig>>>, config_msix_vector: u16, #[cfg(target_arch = "x86_64")] wakeup_event: Option<(PmWakeupEvent, MetricEventType)>, ) -> Interrupt178*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(
179*bb4ee6a4SAndroid Build Coastguard Worker         irq_evt_lvl: IrqLevelEvent,
180*bb4ee6a4SAndroid Build Coastguard Worker         msix_config: Option<Arc<Mutex<MsixConfig>>>,
181*bb4ee6a4SAndroid Build Coastguard Worker         config_msix_vector: u16,
182*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(target_arch = "x86_64")] wakeup_event: Option<(PmWakeupEvent, MetricEventType)>,
183*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Interrupt {
184*bb4ee6a4SAndroid Build Coastguard Worker         Interrupt {
185*bb4ee6a4SAndroid Build Coastguard Worker             inner: Arc::new(InterruptInner {
186*bb4ee6a4SAndroid Build Coastguard Worker                 interrupt_status: AtomicUsize::new(0),
187*bb4ee6a4SAndroid Build Coastguard Worker                 async_intr_status: false,
188*bb4ee6a4SAndroid Build Coastguard Worker                 transport: Transport::Pci {
189*bb4ee6a4SAndroid Build Coastguard Worker                     pci: TransportPci {
190*bb4ee6a4SAndroid Build Coastguard Worker                         irq_evt_lvl,
191*bb4ee6a4SAndroid Build Coastguard Worker                         msix_config,
192*bb4ee6a4SAndroid Build Coastguard Worker                         config_msix_vector,
193*bb4ee6a4SAndroid Build Coastguard Worker                     },
194*bb4ee6a4SAndroid Build Coastguard Worker                 },
195*bb4ee6a4SAndroid Build Coastguard Worker                 pm_state: PmState::new(
196*bb4ee6a4SAndroid Build Coastguard Worker                     #[cfg(target_arch = "x86_64")]
197*bb4ee6a4SAndroid Build Coastguard Worker                     wakeup_event,
198*bb4ee6a4SAndroid Build Coastguard Worker                 ),
199*bb4ee6a4SAndroid Build Coastguard Worker             }),
200*bb4ee6a4SAndroid Build Coastguard Worker         }
201*bb4ee6a4SAndroid Build Coastguard Worker     }
202*bb4ee6a4SAndroid Build Coastguard Worker 
203*bb4ee6a4SAndroid Build Coastguard Worker     /// Create a new `Interrupt`, restoring internal state to match `snapshot`.
204*bb4ee6a4SAndroid Build Coastguard Worker     ///
205*bb4ee6a4SAndroid Build Coastguard Worker     /// The other arguments are assumed to be snapshot'd and restore'd elsewhere.
new_from_snapshot( irq_evt_lvl: IrqLevelEvent, msix_config: Option<Arc<Mutex<MsixConfig>>>, config_msix_vector: u16, snapshot: InterruptSnapshot, #[cfg(target_arch = "x86_64")] wakeup_event: Option<(PmWakeupEvent, MetricEventType)>, ) -> Interrupt206*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new_from_snapshot(
207*bb4ee6a4SAndroid Build Coastguard Worker         irq_evt_lvl: IrqLevelEvent,
208*bb4ee6a4SAndroid Build Coastguard Worker         msix_config: Option<Arc<Mutex<MsixConfig>>>,
209*bb4ee6a4SAndroid Build Coastguard Worker         config_msix_vector: u16,
210*bb4ee6a4SAndroid Build Coastguard Worker         snapshot: InterruptSnapshot,
211*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(target_arch = "x86_64")] wakeup_event: Option<(PmWakeupEvent, MetricEventType)>,
212*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Interrupt {
213*bb4ee6a4SAndroid Build Coastguard Worker         Interrupt {
214*bb4ee6a4SAndroid Build Coastguard Worker             inner: Arc::new(InterruptInner {
215*bb4ee6a4SAndroid Build Coastguard Worker                 interrupt_status: AtomicUsize::new(snapshot.interrupt_status),
216*bb4ee6a4SAndroid Build Coastguard Worker                 async_intr_status: false,
217*bb4ee6a4SAndroid Build Coastguard Worker                 transport: Transport::Pci {
218*bb4ee6a4SAndroid Build Coastguard Worker                     pci: TransportPci {
219*bb4ee6a4SAndroid Build Coastguard Worker                         irq_evt_lvl,
220*bb4ee6a4SAndroid Build Coastguard Worker                         msix_config,
221*bb4ee6a4SAndroid Build Coastguard Worker                         config_msix_vector,
222*bb4ee6a4SAndroid Build Coastguard Worker                     },
223*bb4ee6a4SAndroid Build Coastguard Worker                 },
224*bb4ee6a4SAndroid Build Coastguard Worker                 pm_state: PmState::new(
225*bb4ee6a4SAndroid Build Coastguard Worker                     #[cfg(target_arch = "x86_64")]
226*bb4ee6a4SAndroid Build Coastguard Worker                     wakeup_event,
227*bb4ee6a4SAndroid Build Coastguard Worker                 ),
228*bb4ee6a4SAndroid Build Coastguard Worker             }),
229*bb4ee6a4SAndroid Build Coastguard Worker         }
230*bb4ee6a4SAndroid Build Coastguard Worker     }
231*bb4ee6a4SAndroid Build Coastguard Worker 
new_mmio(irq_evt_edge: IrqEdgeEvent, async_intr_status: bool) -> Interrupt232*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new_mmio(irq_evt_edge: IrqEdgeEvent, async_intr_status: bool) -> Interrupt {
233*bb4ee6a4SAndroid Build Coastguard Worker         Interrupt {
234*bb4ee6a4SAndroid Build Coastguard Worker             inner: Arc::new(InterruptInner {
235*bb4ee6a4SAndroid Build Coastguard Worker                 interrupt_status: AtomicUsize::new(0),
236*bb4ee6a4SAndroid Build Coastguard Worker                 transport: Transport::Mmio { irq_evt_edge },
237*bb4ee6a4SAndroid Build Coastguard Worker                 async_intr_status,
238*bb4ee6a4SAndroid Build Coastguard Worker                 pm_state: PmState::new(
239*bb4ee6a4SAndroid Build Coastguard Worker                     #[cfg(target_arch = "x86_64")]
240*bb4ee6a4SAndroid Build Coastguard Worker                     None,
241*bb4ee6a4SAndroid Build Coastguard Worker                 ),
242*bb4ee6a4SAndroid Build Coastguard Worker             }),
243*bb4ee6a4SAndroid Build Coastguard Worker         }
244*bb4ee6a4SAndroid Build Coastguard Worker     }
245*bb4ee6a4SAndroid Build Coastguard Worker 
246*bb4ee6a4SAndroid Build Coastguard Worker     /// Create an `Interrupt` wrapping a vhost-user vring call event and function that sends a
247*bb4ee6a4SAndroid Build Coastguard Worker     /// VHOST_USER_BACKEND_CONFIG_CHANGE_MSG to the frontend.
new_vhost_user( call_evt: Event, signal_config_changed_fn: Box<dyn Fn() + Send + Sync>, ) -> Interrupt248*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new_vhost_user(
249*bb4ee6a4SAndroid Build Coastguard Worker         call_evt: Event,
250*bb4ee6a4SAndroid Build Coastguard Worker         signal_config_changed_fn: Box<dyn Fn() + Send + Sync>,
251*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Interrupt {
252*bb4ee6a4SAndroid Build Coastguard Worker         Interrupt {
253*bb4ee6a4SAndroid Build Coastguard Worker             inner: Arc::new(InterruptInner {
254*bb4ee6a4SAndroid Build Coastguard Worker                 interrupt_status: AtomicUsize::new(0),
255*bb4ee6a4SAndroid Build Coastguard Worker                 transport: Transport::VhostUser {
256*bb4ee6a4SAndroid Build Coastguard Worker                     call_evt,
257*bb4ee6a4SAndroid Build Coastguard Worker                     signal_config_changed_fn,
258*bb4ee6a4SAndroid Build Coastguard Worker                 },
259*bb4ee6a4SAndroid Build Coastguard Worker                 async_intr_status: false,
260*bb4ee6a4SAndroid Build Coastguard Worker                 pm_state: PmState::new(
261*bb4ee6a4SAndroid Build Coastguard Worker                     #[cfg(target_arch = "x86_64")]
262*bb4ee6a4SAndroid Build Coastguard Worker                     None,
263*bb4ee6a4SAndroid Build Coastguard Worker                 ),
264*bb4ee6a4SAndroid Build Coastguard Worker             }),
265*bb4ee6a4SAndroid Build Coastguard Worker         }
266*bb4ee6a4SAndroid Build Coastguard Worker     }
267*bb4ee6a4SAndroid Build Coastguard Worker 
268*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(test)]
new_for_test() -> Interrupt269*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new_for_test() -> Interrupt {
270*bb4ee6a4SAndroid Build Coastguard Worker         Interrupt::new(
271*bb4ee6a4SAndroid Build Coastguard Worker             IrqLevelEvent::new().unwrap(),
272*bb4ee6a4SAndroid Build Coastguard Worker             None,
273*bb4ee6a4SAndroid Build Coastguard Worker             VIRTIO_MSI_NO_VECTOR,
274*bb4ee6a4SAndroid Build Coastguard Worker             #[cfg(target_arch = "x86_64")]
275*bb4ee6a4SAndroid Build Coastguard Worker             None,
276*bb4ee6a4SAndroid Build Coastguard Worker         )
277*bb4ee6a4SAndroid Build Coastguard Worker     }
278*bb4ee6a4SAndroid Build Coastguard Worker 
279*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(test)]
new_for_test_with_msix() -> Interrupt280*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new_for_test_with_msix() -> Interrupt {
281*bb4ee6a4SAndroid Build Coastguard Worker         let (_, unused_config_tube) = base::Tube::pair().unwrap();
282*bb4ee6a4SAndroid Build Coastguard Worker         let msix_vectors = 2;
283*bb4ee6a4SAndroid Build Coastguard Worker         let msix_cfg = MsixConfig::new(
284*bb4ee6a4SAndroid Build Coastguard Worker             msix_vectors,
285*bb4ee6a4SAndroid Build Coastguard Worker             unused_config_tube,
286*bb4ee6a4SAndroid Build Coastguard Worker             0,
287*bb4ee6a4SAndroid Build Coastguard Worker             "test_device".to_owned(),
288*bb4ee6a4SAndroid Build Coastguard Worker         );
289*bb4ee6a4SAndroid Build Coastguard Worker 
290*bb4ee6a4SAndroid Build Coastguard Worker         Interrupt::new(
291*bb4ee6a4SAndroid Build Coastguard Worker             IrqLevelEvent::new().unwrap(),
292*bb4ee6a4SAndroid Build Coastguard Worker             Some(Arc::new(Mutex::new(msix_cfg))),
293*bb4ee6a4SAndroid Build Coastguard Worker             msix_vectors,
294*bb4ee6a4SAndroid Build Coastguard Worker             #[cfg(target_arch = "x86_64")]
295*bb4ee6a4SAndroid Build Coastguard Worker             None,
296*bb4ee6a4SAndroid Build Coastguard Worker         )
297*bb4ee6a4SAndroid Build Coastguard Worker     }
298*bb4ee6a4SAndroid Build Coastguard Worker 
299*bb4ee6a4SAndroid Build Coastguard Worker     /// Get a reference to the interrupt event.
get_interrupt_evt(&self) -> &Event300*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_interrupt_evt(&self) -> &Event {
301*bb4ee6a4SAndroid Build Coastguard Worker         match &self.inner.as_ref().transport {
302*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Pci { pci } => pci.irq_evt_lvl.get_trigger(),
303*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Mmio { irq_evt_edge } => irq_evt_edge.get_trigger(),
304*bb4ee6a4SAndroid Build Coastguard Worker             Transport::VhostUser { call_evt, .. } => call_evt,
305*bb4ee6a4SAndroid Build Coastguard Worker         }
306*bb4ee6a4SAndroid Build Coastguard Worker     }
307*bb4ee6a4SAndroid Build Coastguard Worker 
308*bb4ee6a4SAndroid Build Coastguard Worker     /// Handle interrupt resampling event, reading the value from the event and doing the resample.
interrupt_resample(&self)309*bb4ee6a4SAndroid Build Coastguard Worker     pub fn interrupt_resample(&self) {
310*bb4ee6a4SAndroid Build Coastguard Worker         match &self.inner.as_ref().transport {
311*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Pci { pci } => {
312*bb4ee6a4SAndroid Build Coastguard Worker                 pci.irq_evt_lvl.clear_resample();
313*bb4ee6a4SAndroid Build Coastguard Worker                 self.do_interrupt_resample();
314*bb4ee6a4SAndroid Build Coastguard Worker             }
315*bb4ee6a4SAndroid Build Coastguard Worker             _ => panic!("interrupt_resample() not supported"),
316*bb4ee6a4SAndroid Build Coastguard Worker         }
317*bb4ee6a4SAndroid Build Coastguard Worker     }
318*bb4ee6a4SAndroid Build Coastguard Worker 
319*bb4ee6a4SAndroid Build Coastguard Worker     /// Get a reference to the msix configuration
get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>>320*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> {
321*bb4ee6a4SAndroid Build Coastguard Worker         match &self.inner.as_ref().transport {
322*bb4ee6a4SAndroid Build Coastguard Worker             Transport::Pci { pci } => &pci.msix_config,
323*bb4ee6a4SAndroid Build Coastguard Worker             _ => &None,
324*bb4ee6a4SAndroid Build Coastguard Worker         }
325*bb4ee6a4SAndroid Build Coastguard Worker     }
326*bb4ee6a4SAndroid Build Coastguard Worker 
327*bb4ee6a4SAndroid Build Coastguard Worker     /// Reads the current value of the interrupt status.
read_interrupt_status(&self) -> u8328*bb4ee6a4SAndroid Build Coastguard Worker     pub fn read_interrupt_status(&self) -> u8 {
329*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.interrupt_status.load(Ordering::SeqCst) as u8
330*bb4ee6a4SAndroid Build Coastguard Worker     }
331*bb4ee6a4SAndroid Build Coastguard Worker 
332*bb4ee6a4SAndroid Build Coastguard Worker     /// Reads the current value of the interrupt status and resets it to 0.
read_and_reset_interrupt_status(&self) -> u8333*bb4ee6a4SAndroid Build Coastguard Worker     pub fn read_and_reset_interrupt_status(&self) -> u8 {
334*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.interrupt_status.swap(0, Ordering::SeqCst) as u8
335*bb4ee6a4SAndroid Build Coastguard Worker     }
336*bb4ee6a4SAndroid Build Coastguard Worker 
337*bb4ee6a4SAndroid Build Coastguard Worker     /// Clear the bits set in `mask` in the interrupt status.
clear_interrupt_status_bits(&self, mask: u8)338*bb4ee6a4SAndroid Build Coastguard Worker     pub fn clear_interrupt_status_bits(&self, mask: u8) {
339*bb4ee6a4SAndroid Build Coastguard Worker         self.inner
340*bb4ee6a4SAndroid Build Coastguard Worker             .interrupt_status
341*bb4ee6a4SAndroid Build Coastguard Worker             .fetch_and(!(mask as usize), Ordering::SeqCst);
342*bb4ee6a4SAndroid Build Coastguard Worker     }
343*bb4ee6a4SAndroid Build Coastguard Worker 
344*bb4ee6a4SAndroid Build Coastguard Worker     /// Snapshot internal state. Can be restored with with `Interrupt::new_from_snapshot`.
snapshot(&self) -> InterruptSnapshot345*bb4ee6a4SAndroid Build Coastguard Worker     pub fn snapshot(&self) -> InterruptSnapshot {
346*bb4ee6a4SAndroid Build Coastguard Worker         InterruptSnapshot {
347*bb4ee6a4SAndroid Build Coastguard Worker             interrupt_status: self.inner.interrupt_status.load(Ordering::SeqCst),
348*bb4ee6a4SAndroid Build Coastguard Worker         }
349*bb4ee6a4SAndroid Build Coastguard Worker     }
350*bb4ee6a4SAndroid Build Coastguard Worker 
set_suspended(&self, suspended: bool)351*bb4ee6a4SAndroid Build Coastguard Worker     pub fn set_suspended(&self, suspended: bool) {
352*bb4ee6a4SAndroid Build Coastguard Worker         let retrigger_evts = self.inner.pm_state.lock().set_suspended(suspended);
353*bb4ee6a4SAndroid Build Coastguard Worker         for (vector, interrupt_status_mask) in retrigger_evts.into_iter() {
354*bb4ee6a4SAndroid Build Coastguard Worker             self.signal(vector, interrupt_status_mask);
355*bb4ee6a4SAndroid Build Coastguard Worker         }
356*bb4ee6a4SAndroid Build Coastguard Worker     }
357*bb4ee6a4SAndroid Build Coastguard Worker 
358*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(target_arch = "x86_64")]
set_wakeup_event_active(&self, active: bool)359*bb4ee6a4SAndroid Build Coastguard Worker     pub fn set_wakeup_event_active(&self, active: bool) {
360*bb4ee6a4SAndroid Build Coastguard Worker         self.inner.pm_state.lock().set_wakeup_event_active(active);
361*bb4ee6a4SAndroid Build Coastguard Worker     }
362*bb4ee6a4SAndroid Build Coastguard Worker }
363*bb4ee6a4SAndroid Build Coastguard Worker 
364*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
365*bb4ee6a4SAndroid Build Coastguard Worker struct WakeupState {
366*bb4ee6a4SAndroid Build Coastguard Worker     wakeup_event: PmWakeupEvent,
367*bb4ee6a4SAndroid Build Coastguard Worker     wakeup_enabled: bool,
368*bb4ee6a4SAndroid Build Coastguard Worker     armed_time: Instant,
369*bb4ee6a4SAndroid Build Coastguard Worker     metrics_event: MetricEventType,
370*bb4ee6a4SAndroid Build Coastguard Worker     wakeup_clear_evt: Option<Event>,
371*bb4ee6a4SAndroid Build Coastguard Worker }
372*bb4ee6a4SAndroid Build Coastguard Worker 
373*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(target_arch = "x86_64")]
374*bb4ee6a4SAndroid Build Coastguard Worker impl WakeupState {
new(wakeup_event: Option<(PmWakeupEvent, MetricEventType)>) -> Option<Self>375*bb4ee6a4SAndroid Build Coastguard Worker     fn new(wakeup_event: Option<(PmWakeupEvent, MetricEventType)>) -> Option<Self> {
376*bb4ee6a4SAndroid Build Coastguard Worker         wakeup_event.map(|(wakeup_event, metrics_event)| Self {
377*bb4ee6a4SAndroid Build Coastguard Worker             wakeup_event,
378*bb4ee6a4SAndroid Build Coastguard Worker             wakeup_enabled: false,
379*bb4ee6a4SAndroid Build Coastguard Worker             // Not actually armed, but simpler than wrapping with an Option.
380*bb4ee6a4SAndroid Build Coastguard Worker             armed_time: Instant::now(),
381*bb4ee6a4SAndroid Build Coastguard Worker             metrics_event,
382*bb4ee6a4SAndroid Build Coastguard Worker             wakeup_clear_evt: None,
383*bb4ee6a4SAndroid Build Coastguard Worker         })
384*bb4ee6a4SAndroid Build Coastguard Worker     }
385*bb4ee6a4SAndroid Build Coastguard Worker 
trigger_wakeup(&mut self)386*bb4ee6a4SAndroid Build Coastguard Worker     fn trigger_wakeup(&mut self) {
387*bb4ee6a4SAndroid Build Coastguard Worker         if self.wakeup_clear_evt.is_some() {
388*bb4ee6a4SAndroid Build Coastguard Worker             return;
389*bb4ee6a4SAndroid Build Coastguard Worker         }
390*bb4ee6a4SAndroid Build Coastguard Worker 
391*bb4ee6a4SAndroid Build Coastguard Worker         let elapsed = self.armed_time.elapsed().as_millis();
392*bb4ee6a4SAndroid Build Coastguard Worker         log_metric(
393*bb4ee6a4SAndroid Build Coastguard Worker             self.metrics_event.clone(),
394*bb4ee6a4SAndroid Build Coastguard Worker             elapsed.try_into().unwrap_or(i64::MAX),
395*bb4ee6a4SAndroid Build Coastguard Worker         );
396*bb4ee6a4SAndroid Build Coastguard Worker 
397*bb4ee6a4SAndroid Build Coastguard Worker         match self.wakeup_event.trigger_wakeup() {
398*bb4ee6a4SAndroid Build Coastguard Worker             Ok(clear_evt) => self.wakeup_clear_evt = clear_evt,
399*bb4ee6a4SAndroid Build Coastguard Worker             Err(err) => error!("Wakeup trigger failed {:?}", err),
400*bb4ee6a4SAndroid Build Coastguard Worker         }
401*bb4ee6a4SAndroid Build Coastguard Worker     }
402*bb4ee6a4SAndroid Build Coastguard Worker }
403*bb4ee6a4SAndroid Build Coastguard Worker 
404*bb4ee6a4SAndroid Build Coastguard Worker // Power management state of the interrupt.
405*bb4ee6a4SAndroid Build Coastguard Worker struct PmState {
406*bb4ee6a4SAndroid Build Coastguard Worker     // Whether or not the virtio device that owns this interrupt is suspended. A
407*bb4ee6a4SAndroid Build Coastguard Worker     // suspended virtio device MUST NOT send notifications (i.e. interrupts) to the
408*bb4ee6a4SAndroid Build Coastguard Worker     // driver.
409*bb4ee6a4SAndroid Build Coastguard Worker     suspended: bool,
410*bb4ee6a4SAndroid Build Coastguard Worker     // The queue of interrupts that the virtio device has generated while suspended.
411*bb4ee6a4SAndroid Build Coastguard Worker     // These are deferred and sent in order when the device is un-suspended.
412*bb4ee6a4SAndroid Build Coastguard Worker     pending_signals: Vec<(u16, u32)>,
413*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(target_arch = "x86_64")]
414*bb4ee6a4SAndroid Build Coastguard Worker     wakeup_state: Option<WakeupState>,
415*bb4ee6a4SAndroid Build Coastguard Worker }
416*bb4ee6a4SAndroid Build Coastguard Worker 
417*bb4ee6a4SAndroid Build Coastguard Worker impl PmState {
new( #[cfg(target_arch = "x86_64")] wakeup_event: Option<(PmWakeupEvent, MetricEventType)>, ) -> Arc<Mutex<Self>>418*bb4ee6a4SAndroid Build Coastguard Worker     fn new(
419*bb4ee6a4SAndroid Build Coastguard Worker         #[cfg(target_arch = "x86_64")] wakeup_event: Option<(PmWakeupEvent, MetricEventType)>,
420*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Arc<Mutex<Self>> {
421*bb4ee6a4SAndroid Build Coastguard Worker         Arc::new(Mutex::new(Self {
422*bb4ee6a4SAndroid Build Coastguard Worker             suspended: false,
423*bb4ee6a4SAndroid Build Coastguard Worker             pending_signals: Vec::new(),
424*bb4ee6a4SAndroid Build Coastguard Worker             #[cfg(target_arch = "x86_64")]
425*bb4ee6a4SAndroid Build Coastguard Worker             wakeup_state: WakeupState::new(wakeup_event),
426*bb4ee6a4SAndroid Build Coastguard Worker         }))
427*bb4ee6a4SAndroid Build Coastguard Worker     }
428*bb4ee6a4SAndroid Build Coastguard Worker 
handle_interrupt(&mut self, vector: u16, mask: u32) -> bool429*bb4ee6a4SAndroid Build Coastguard Worker     fn handle_interrupt(&mut self, vector: u16, mask: u32) -> bool {
430*bb4ee6a4SAndroid Build Coastguard Worker         if self.suspended {
431*bb4ee6a4SAndroid Build Coastguard Worker             self.pending_signals.push((vector, mask));
432*bb4ee6a4SAndroid Build Coastguard Worker             #[cfg(target_arch = "x86_64")]
433*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(wakeup_state) = self.wakeup_state.as_mut() {
434*bb4ee6a4SAndroid Build Coastguard Worker                 if wakeup_state.wakeup_enabled {
435*bb4ee6a4SAndroid Build Coastguard Worker                     wakeup_state.trigger_wakeup();
436*bb4ee6a4SAndroid Build Coastguard Worker                 }
437*bb4ee6a4SAndroid Build Coastguard Worker             }
438*bb4ee6a4SAndroid Build Coastguard Worker         }
439*bb4ee6a4SAndroid Build Coastguard Worker         self.suspended
440*bb4ee6a4SAndroid Build Coastguard Worker     }
441*bb4ee6a4SAndroid Build Coastguard Worker 
set_suspended(&mut self, suspended: bool) -> Vec<(u16, u32)>442*bb4ee6a4SAndroid Build Coastguard Worker     fn set_suspended(&mut self, suspended: bool) -> Vec<(u16, u32)> {
443*bb4ee6a4SAndroid Build Coastguard Worker         self.suspended = suspended;
444*bb4ee6a4SAndroid Build Coastguard Worker         std::mem::take(&mut self.pending_signals)
445*bb4ee6a4SAndroid Build Coastguard Worker     }
446*bb4ee6a4SAndroid Build Coastguard Worker 
447*bb4ee6a4SAndroid Build Coastguard Worker     #[cfg(target_arch = "x86_64")]
set_wakeup_event_active(&mut self, active: bool)448*bb4ee6a4SAndroid Build Coastguard Worker     fn set_wakeup_event_active(&mut self, active: bool) {
449*bb4ee6a4SAndroid Build Coastguard Worker         let Some(wakeup_state) = self.wakeup_state.as_mut() else {
450*bb4ee6a4SAndroid Build Coastguard Worker             return;
451*bb4ee6a4SAndroid Build Coastguard Worker         };
452*bb4ee6a4SAndroid Build Coastguard Worker 
453*bb4ee6a4SAndroid Build Coastguard Worker         wakeup_state.wakeup_enabled = active;
454*bb4ee6a4SAndroid Build Coastguard Worker         if active {
455*bb4ee6a4SAndroid Build Coastguard Worker             wakeup_state.armed_time = Instant::now();
456*bb4ee6a4SAndroid Build Coastguard Worker             if !self.pending_signals.is_empty() {
457*bb4ee6a4SAndroid Build Coastguard Worker                 wakeup_state.trigger_wakeup();
458*bb4ee6a4SAndroid Build Coastguard Worker             }
459*bb4ee6a4SAndroid Build Coastguard Worker         } else if let Some(clear_evt) = wakeup_state.wakeup_clear_evt.take() {
460*bb4ee6a4SAndroid Build Coastguard Worker             if let Err(e) = clear_evt.signal() {
461*bb4ee6a4SAndroid Build Coastguard Worker                 error!("failed to signal clear event {}", e);
462*bb4ee6a4SAndroid Build Coastguard Worker             }
463*bb4ee6a4SAndroid Build Coastguard Worker         }
464*bb4ee6a4SAndroid Build Coastguard Worker     }
465*bb4ee6a4SAndroid Build Coastguard Worker }
466