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::sync::atomic::AtomicUsize; 6 use std::sync::atomic::Ordering; 7 use std::sync::Arc; 8 9 #[cfg(target_arch = "x86_64")] 10 use base::error; 11 use base::Event; 12 use serde::Deserialize; 13 use serde::Serialize; 14 use sync::Mutex; 15 16 use super::INTERRUPT_STATUS_CONFIG_CHANGED; 17 use super::INTERRUPT_STATUS_USED_RING; 18 use super::VIRTIO_MSI_NO_VECTOR; 19 #[cfg(target_arch = "x86_64")] 20 use crate::acpi::PmWakeupEvent; 21 use crate::irq_event::IrqEdgeEvent; 22 use crate::irq_event::IrqLevelEvent; 23 use crate::pci::MsixConfig; 24 25 struct TransportPci { 26 irq_evt_lvl: IrqLevelEvent, 27 msix_config: Option<Arc<Mutex<MsixConfig>>>, 28 config_msix_vector: u16, 29 } 30 31 enum Transport { 32 Pci { 33 pci: TransportPci, 34 }, 35 Mmio { 36 irq_evt_edge: IrqEdgeEvent, 37 }, 38 VhostUser { 39 call_evt: Event, 40 signal_config_changed_fn: Box<dyn Fn() + Send + Sync>, 41 }, 42 } 43 44 struct InterruptInner { 45 interrupt_status: AtomicUsize, 46 transport: Transport, 47 async_intr_status: bool, 48 #[cfg(target_arch = "x86_64")] 49 wakeup_event: Option<PmWakeupEvent>, 50 } 51 52 impl InterruptInner { 53 /// Add `interrupt_status_mask` to any existing interrupt status. 54 /// 55 /// Returns `true` if the interrupt should be triggered after this update. update_interrupt_status(&self, interrupt_status_mask: u32) -> bool56 fn update_interrupt_status(&self, interrupt_status_mask: u32) -> bool { 57 // Set bit in ISR and inject the interrupt if it was not already pending. 58 // Don't need to inject the interrupt if the guest hasn't processed it. 59 // In hypervisors where interrupt_status is updated asynchronously, inject the 60 // interrupt even if the previous interrupt appears to be already pending. 61 self.interrupt_status 62 .fetch_or(interrupt_status_mask as usize, Ordering::SeqCst) 63 == 0 64 || self.async_intr_status 65 } 66 } 67 68 #[derive(Clone)] 69 pub struct Interrupt { 70 inner: Arc<InterruptInner>, 71 } 72 73 #[derive(Serialize, Deserialize)] 74 pub struct InterruptSnapshot { 75 interrupt_status: usize, 76 } 77 78 impl Interrupt { 79 /// Writes to the irqfd to VMM to deliver virtual interrupt to the guest. 80 /// 81 /// If MSI-X is enabled in this device, MSI-X interrupt is preferred. 82 /// Write to the irqfd to VMM to deliver virtual interrupt to the guest signal(&self, vector: u16, interrupt_status_mask: u32)83 pub fn signal(&self, vector: u16, interrupt_status_mask: u32) { 84 #[cfg(target_arch = "x86_64")] 85 if let Some(wakeup_event) = self.inner.wakeup_event.as_ref() { 86 if let Err(e) = wakeup_event.trigger_wakeup() { 87 error!("Wakeup trigger failed {:?}", e); 88 } 89 } 90 match &self.inner.transport { 91 Transport::Pci { pci } => { 92 // Don't need to set ISR for MSI-X interrupts 93 if let Some(msix_config) = &pci.msix_config { 94 let mut msix_config = msix_config.lock(); 95 if msix_config.enabled() { 96 if vector != VIRTIO_MSI_NO_VECTOR { 97 msix_config.trigger(vector); 98 } 99 return; 100 } 101 } 102 103 if self.inner.update_interrupt_status(interrupt_status_mask) { 104 pci.irq_evt_lvl.trigger().unwrap(); 105 } 106 } 107 Transport::Mmio { irq_evt_edge } => { 108 if self.inner.update_interrupt_status(interrupt_status_mask) { 109 irq_evt_edge.trigger().unwrap(); 110 } 111 } 112 Transport::VhostUser { call_evt, .. } => { 113 // TODO(b/187487351): To avoid sending unnecessary events, we might want to support 114 // interrupt status. For this purpose, we need a mechanism to share interrupt status 115 // between the vmm and the device process. 116 call_evt.signal().unwrap(); 117 } 118 } 119 } 120 121 /// Notify the driver that buffers have been placed in the used queue. signal_used_queue(&self, vector: u16)122 pub fn signal_used_queue(&self, vector: u16) { 123 self.signal(vector, INTERRUPT_STATUS_USED_RING) 124 } 125 126 /// Notify the driver that the device configuration has changed. signal_config_changed(&self)127 pub fn signal_config_changed(&self) { 128 match &self.inner.as_ref().transport { 129 Transport::Pci { pci } => { 130 self.signal(pci.config_msix_vector, INTERRUPT_STATUS_CONFIG_CHANGED) 131 } 132 Transport::Mmio { .. } => { 133 self.signal(VIRTIO_MSI_NO_VECTOR, INTERRUPT_STATUS_CONFIG_CHANGED) 134 } 135 Transport::VhostUser { 136 signal_config_changed_fn, 137 .. 138 } => signal_config_changed_fn(), 139 } 140 } 141 142 /// Get the event to signal resampling is needed if it exists. get_resample_evt(&self) -> Option<&Event>143 pub fn get_resample_evt(&self) -> Option<&Event> { 144 match &self.inner.as_ref().transport { 145 Transport::Pci { pci } => Some(pci.irq_evt_lvl.get_resample()), 146 _ => None, 147 } 148 } 149 150 /// Reads the status and writes to the interrupt event. Doesn't read the resample event, it 151 /// assumes the resample has been requested. do_interrupt_resample(&self)152 pub fn do_interrupt_resample(&self) { 153 if self.inner.interrupt_status.load(Ordering::SeqCst) != 0 { 154 match &self.inner.as_ref().transport { 155 Transport::Pci { pci } => pci.irq_evt_lvl.trigger().unwrap(), 156 _ => panic!("do_interrupt_resample() not supported"), 157 } 158 } 159 } 160 } 161 162 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>, ) -> Interrupt163 pub fn new( 164 irq_evt_lvl: IrqLevelEvent, 165 msix_config: Option<Arc<Mutex<MsixConfig>>>, 166 config_msix_vector: u16, 167 #[cfg(target_arch = "x86_64")] wakeup_event: Option<PmWakeupEvent>, 168 ) -> Interrupt { 169 Interrupt { 170 inner: Arc::new(InterruptInner { 171 interrupt_status: AtomicUsize::new(0), 172 async_intr_status: false, 173 transport: Transport::Pci { 174 pci: TransportPci { 175 irq_evt_lvl, 176 msix_config, 177 config_msix_vector, 178 }, 179 }, 180 #[cfg(target_arch = "x86_64")] 181 wakeup_event, 182 }), 183 } 184 } 185 186 /// Create a new `Interrupt`, restoring internal state to match `snapshot`. 187 /// 188 /// 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>, ) -> Interrupt189 pub fn new_from_snapshot( 190 irq_evt_lvl: IrqLevelEvent, 191 msix_config: Option<Arc<Mutex<MsixConfig>>>, 192 config_msix_vector: u16, 193 snapshot: InterruptSnapshot, 194 #[cfg(target_arch = "x86_64")] wakeup_event: Option<PmWakeupEvent>, 195 ) -> Interrupt { 196 Interrupt { 197 inner: Arc::new(InterruptInner { 198 interrupt_status: AtomicUsize::new(snapshot.interrupt_status), 199 async_intr_status: false, 200 transport: Transport::Pci { 201 pci: TransportPci { 202 irq_evt_lvl, 203 msix_config, 204 config_msix_vector, 205 }, 206 }, 207 #[cfg(target_arch = "x86_64")] 208 wakeup_event, 209 }), 210 } 211 } 212 new_mmio(irq_evt_edge: IrqEdgeEvent, async_intr_status: bool) -> Interrupt213 pub fn new_mmio(irq_evt_edge: IrqEdgeEvent, async_intr_status: bool) -> Interrupt { 214 Interrupt { 215 inner: Arc::new(InterruptInner { 216 interrupt_status: AtomicUsize::new(0), 217 transport: Transport::Mmio { irq_evt_edge }, 218 async_intr_status, 219 #[cfg(target_arch = "x86_64")] 220 wakeup_event: None, 221 }), 222 } 223 } 224 225 /// Create an `Interrupt` wrapping a vhost-user vring call event and function that sends a 226 /// VHOST_USER_BACKEND_CONFIG_CHANGE_MSG to the frontend. new_vhost_user( call_evt: Event, signal_config_changed_fn: Box<dyn Fn() + Send + Sync>, ) -> Interrupt227 pub fn new_vhost_user( 228 call_evt: Event, 229 signal_config_changed_fn: Box<dyn Fn() + Send + Sync>, 230 ) -> Interrupt { 231 Interrupt { 232 inner: Arc::new(InterruptInner { 233 interrupt_status: AtomicUsize::new(0), 234 transport: Transport::VhostUser { 235 call_evt, 236 signal_config_changed_fn, 237 }, 238 async_intr_status: false, 239 #[cfg(target_arch = "x86_64")] 240 wakeup_event: None, 241 }), 242 } 243 } 244 245 #[cfg(test)] new_for_test() -> Interrupt246 pub fn new_for_test() -> Interrupt { 247 Interrupt::new( 248 IrqLevelEvent::new().unwrap(), 249 None, 250 VIRTIO_MSI_NO_VECTOR, 251 #[cfg(target_arch = "x86_64")] 252 None, 253 ) 254 } 255 256 #[cfg(test)] new_for_test_with_msix() -> Interrupt257 pub fn new_for_test_with_msix() -> Interrupt { 258 let (_, unused_config_tube) = base::Tube::pair().unwrap(); 259 let msix_vectors = 2; 260 let msix_cfg = MsixConfig::new( 261 msix_vectors, 262 unused_config_tube, 263 0, 264 "test_device".to_owned(), 265 ); 266 267 Interrupt::new( 268 IrqLevelEvent::new().unwrap(), 269 Some(Arc::new(Mutex::new(msix_cfg))), 270 msix_vectors, 271 #[cfg(target_arch = "x86_64")] 272 None, 273 ) 274 } 275 276 /// Get a reference to the interrupt event. get_interrupt_evt(&self) -> &Event277 pub fn get_interrupt_evt(&self) -> &Event { 278 match &self.inner.as_ref().transport { 279 Transport::Pci { pci } => pci.irq_evt_lvl.get_trigger(), 280 Transport::Mmio { irq_evt_edge } => irq_evt_edge.get_trigger(), 281 Transport::VhostUser { call_evt, .. } => call_evt, 282 } 283 } 284 285 /// Handle interrupt resampling event, reading the value from the event and doing the resample. interrupt_resample(&self)286 pub fn interrupt_resample(&self) { 287 match &self.inner.as_ref().transport { 288 Transport::Pci { pci } => { 289 pci.irq_evt_lvl.clear_resample(); 290 self.do_interrupt_resample(); 291 } 292 _ => panic!("interrupt_resample() not supported"), 293 } 294 } 295 296 /// Get a reference to the msix configuration get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>>297 pub fn get_msix_config(&self) -> &Option<Arc<Mutex<MsixConfig>>> { 298 match &self.inner.as_ref().transport { 299 Transport::Pci { pci } => &pci.msix_config, 300 _ => &None, 301 } 302 } 303 304 /// Reads the current value of the interrupt status. read_interrupt_status(&self) -> u8305 pub fn read_interrupt_status(&self) -> u8 { 306 self.inner.interrupt_status.load(Ordering::SeqCst) as u8 307 } 308 309 /// Reads the current value of the interrupt status and resets it to 0. read_and_reset_interrupt_status(&self) -> u8310 pub fn read_and_reset_interrupt_status(&self) -> u8 { 311 self.inner.interrupt_status.swap(0, Ordering::SeqCst) as u8 312 } 313 314 /// Clear the bits set in `mask` in the interrupt status. clear_interrupt_status_bits(&self, mask: u8)315 pub fn clear_interrupt_status_bits(&self, mask: u8) { 316 self.inner 317 .interrupt_status 318 .fetch_and(!(mask as usize), Ordering::SeqCst); 319 } 320 321 /// Snapshot internal state. Can be restored with with `Interrupt::new_from_snapshot`. snapshot(&self) -> InterruptSnapshot322 pub fn snapshot(&self) -> InterruptSnapshot { 323 InterruptSnapshot { 324 interrupt_status: self.inner.interrupt_status.load(Ordering::SeqCst), 325 } 326 } 327 328 #[cfg(target_arch = "x86_64")] set_wakeup_event_active(&self, active: bool)329 pub fn set_wakeup_event_active(&self, active: bool) { 330 if let Some(wakeup_event) = self.inner.wakeup_event.as_ref() { 331 wakeup_event.set_active(active); 332 } 333 } 334 } 335