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