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