xref: /cloud-hypervisor/vmm/src/interrupt.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
1 // Copyright © 2019 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
4 //
5 
6 use devices::interrupt_controller::InterruptController;
7 use hypervisor::IrqRoutingEntry;
8 use std::collections::HashMap;
9 use std::io;
10 use std::sync::atomic::{AtomicBool, Ordering};
11 use std::sync::{Arc, Mutex};
12 use vm_allocator::SystemAllocator;
13 use vm_device::interrupt::{
14     InterruptIndex, InterruptManager, InterruptSourceConfig, InterruptSourceGroup,
15     LegacyIrqGroupConfig, MsiIrqGroupConfig,
16 };
17 use vmm_sys_util::eventfd::EventFd;
18 
19 /// Reuse std::io::Result to simplify interoperability among crates.
20 pub type Result<T> = std::io::Result<T>;
21 
22 struct InterruptRoute {
23     gsi: u32,
24     irq_fd: EventFd,
25     registered: AtomicBool,
26 }
27 
28 impl InterruptRoute {
29     pub fn new(allocator: &mut SystemAllocator) -> Result<Self> {
30         let irq_fd = EventFd::new(libc::EFD_NONBLOCK)?;
31         let gsi = allocator
32             .allocate_gsi()
33             .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed allocating new GSI"))?;
34 
35         Ok(InterruptRoute {
36             gsi,
37             irq_fd,
38             registered: AtomicBool::new(false),
39         })
40     }
41 
42     pub fn enable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
43         if !self.registered.load(Ordering::Acquire) {
44             vm.register_irqfd(&self.irq_fd, self.gsi).map_err(|e| {
45                 io::Error::new(
46                     io::ErrorKind::Other,
47                     format!("Failed registering irq_fd: {}", e),
48                 )
49             })?;
50 
51             // Update internals to track the irq_fd as "registered".
52             self.registered.store(true, Ordering::Release);
53         }
54 
55         Ok(())
56     }
57 
58     pub fn disable(&self, vm: &Arc<dyn hypervisor::Vm>) -> Result<()> {
59         if self.registered.load(Ordering::Acquire) {
60             vm.unregister_irqfd(&self.irq_fd, self.gsi).map_err(|e| {
61                 io::Error::new(
62                     io::ErrorKind::Other,
63                     format!("Failed unregistering irq_fd: {}", e),
64                 )
65             })?;
66 
67             // Update internals to track the irq_fd as "unregistered".
68             self.registered.store(false, Ordering::Release);
69         }
70 
71         Ok(())
72     }
73 
74     pub fn trigger(&self) -> Result<()> {
75         self.irq_fd.write(1)
76     }
77 
78     pub fn notifier(&self) -> Option<EventFd> {
79         Some(
80             self.irq_fd
81                 .try_clone()
82                 .expect("Failed cloning interrupt's EventFd"),
83         )
84     }
85 }
86 
87 pub struct RoutingEntry {
88     route: IrqRoutingEntry,
89     masked: bool,
90 }
91 
92 pub struct MsiInterruptGroup {
93     vm: Arc<dyn hypervisor::Vm>,
94     gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
95     irq_routes: HashMap<InterruptIndex, InterruptRoute>,
96 }
97 
98 impl MsiInterruptGroup {
99     fn set_gsi_routes(&self, routes: &HashMap<u32, RoutingEntry>) -> Result<()> {
100         let mut entry_vec: Vec<IrqRoutingEntry> = Vec::new();
101         for (_, entry) in routes.iter() {
102             if entry.masked {
103                 continue;
104             }
105 
106             entry_vec.push(entry.route);
107         }
108 
109         self.vm.set_gsi_routing(&entry_vec).map_err(|e| {
110             io::Error::new(
111                 io::ErrorKind::Other,
112                 format!("Failed setting GSI routing: {}", e),
113             )
114         })
115     }
116 }
117 
118 impl MsiInterruptGroup {
119     fn new(
120         vm: Arc<dyn hypervisor::Vm>,
121         gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
122         irq_routes: HashMap<InterruptIndex, InterruptRoute>,
123     ) -> Self {
124         MsiInterruptGroup {
125             vm,
126             gsi_msi_routes,
127             irq_routes,
128         }
129     }
130 }
131 
132 impl InterruptSourceGroup for MsiInterruptGroup {
133     fn enable(&self) -> Result<()> {
134         for (_, route) in self.irq_routes.iter() {
135             route.enable(&self.vm)?;
136         }
137 
138         Ok(())
139     }
140 
141     fn disable(&self) -> Result<()> {
142         for (_, route) in self.irq_routes.iter() {
143             route.disable(&self.vm)?;
144         }
145 
146         Ok(())
147     }
148 
149     fn trigger(&self, index: InterruptIndex) -> Result<()> {
150         if let Some(route) = self.irq_routes.get(&index) {
151             return route.trigger();
152         }
153 
154         Err(io::Error::new(
155             io::ErrorKind::Other,
156             format!("trigger: Invalid interrupt index {}", index),
157         ))
158     }
159 
160     fn notifier(&self, index: InterruptIndex) -> Option<EventFd> {
161         if let Some(route) = self.irq_routes.get(&index) {
162             return route.notifier();
163         }
164 
165         None
166     }
167 
168     fn update(
169         &self,
170         index: InterruptIndex,
171         config: InterruptSourceConfig,
172         masked: bool,
173     ) -> Result<()> {
174         if let Some(route) = self.irq_routes.get(&index) {
175             let entry = RoutingEntry {
176                 route: self.vm.make_routing_entry(route.gsi, &config),
177                 masked,
178             };
179             if masked {
180                 route.disable(&self.vm)?;
181             } else {
182                 route.enable(&self.vm)?;
183             }
184             let mut routes = self.gsi_msi_routes.lock().unwrap();
185             routes.insert(route.gsi, entry);
186             return self.set_gsi_routes(&routes);
187         }
188 
189         Err(io::Error::new(
190             io::ErrorKind::Other,
191             format!("update: Invalid interrupt index {}", index),
192         ))
193     }
194 }
195 
196 pub struct LegacyUserspaceInterruptGroup {
197     ioapic: Arc<Mutex<dyn InterruptController>>,
198     irq: u32,
199 }
200 
201 impl LegacyUserspaceInterruptGroup {
202     fn new(ioapic: Arc<Mutex<dyn InterruptController>>, irq: u32) -> Self {
203         LegacyUserspaceInterruptGroup { ioapic, irq }
204     }
205 }
206 
207 impl InterruptSourceGroup for LegacyUserspaceInterruptGroup {
208     fn trigger(&self, _index: InterruptIndex) -> Result<()> {
209         self.ioapic
210             .lock()
211             .unwrap()
212             .service_irq(self.irq as usize)
213             .map_err(|e| {
214                 io::Error::new(
215                     io::ErrorKind::Other,
216                     format!("failed to inject IRQ #{}: {:?}", self.irq, e),
217                 )
218             })
219     }
220 
221     fn update(
222         &self,
223         _index: InterruptIndex,
224         _config: InterruptSourceConfig,
225         _masked: bool,
226     ) -> Result<()> {
227         Ok(())
228     }
229 
230     fn notifier(&self, _index: InterruptIndex) -> Option<EventFd> {
231         self.ioapic.lock().unwrap().notifier(self.irq as usize)
232     }
233 }
234 
235 pub struct LegacyUserspaceInterruptManager {
236     ioapic: Arc<Mutex<dyn InterruptController>>,
237 }
238 
239 pub struct MsiInterruptManager {
240     allocator: Arc<Mutex<SystemAllocator>>,
241     vm: Arc<dyn hypervisor::Vm>,
242     gsi_msi_routes: Arc<Mutex<HashMap<u32, RoutingEntry>>>,
243 }
244 
245 impl LegacyUserspaceInterruptManager {
246     pub fn new(ioapic: Arc<Mutex<dyn InterruptController>>) -> Self {
247         LegacyUserspaceInterruptManager { ioapic }
248     }
249 }
250 
251 impl MsiInterruptManager {
252     pub fn new(allocator: Arc<Mutex<SystemAllocator>>, vm: Arc<dyn hypervisor::Vm>) -> Self {
253         // Create a shared list of GSI that can be shared through all PCI
254         // devices. This way, we can maintain the full list of used GSI,
255         // preventing one device from overriding interrupts setting from
256         // another one.
257         let gsi_msi_routes = Arc::new(Mutex::new(HashMap::new()));
258 
259         MsiInterruptManager {
260             allocator,
261             vm,
262             gsi_msi_routes,
263         }
264     }
265 }
266 
267 impl InterruptManager for LegacyUserspaceInterruptManager {
268     type GroupConfig = LegacyIrqGroupConfig;
269 
270     fn create_group(&self, config: Self::GroupConfig) -> Result<Arc<dyn InterruptSourceGroup>> {
271         Ok(Arc::new(LegacyUserspaceInterruptGroup::new(
272             self.ioapic.clone(),
273             config.irq,
274         )))
275     }
276 
277     fn destroy_group(&self, _group: Arc<dyn InterruptSourceGroup>) -> Result<()> {
278         Ok(())
279     }
280 }
281 
282 impl InterruptManager for MsiInterruptManager {
283     type GroupConfig = MsiIrqGroupConfig;
284 
285     fn create_group(&self, config: Self::GroupConfig) -> Result<Arc<dyn InterruptSourceGroup>> {
286         let mut allocator = self.allocator.lock().unwrap();
287         let mut irq_routes: HashMap<InterruptIndex, InterruptRoute> =
288             HashMap::with_capacity(config.count as usize);
289         for i in config.base..config.base + config.count {
290             irq_routes.insert(i, InterruptRoute::new(&mut allocator)?);
291         }
292 
293         Ok(Arc::new(MsiInterruptGroup::new(
294             self.vm.clone(),
295             self.gsi_msi_routes.clone(),
296             irq_routes,
297         )))
298     }
299 
300     fn destroy_group(&self, _group: Arc<dyn InterruptSourceGroup>) -> Result<()> {
301         Ok(())
302     }
303 }
304