1use std::sync::Arc;
5use std::sync::atomic::{AtomicBool, Ordering};
6
7use kvm_ioctls::VmFd;
8use vmm_sys_util::eventfd::EventFd;
9
10use crate::Vm;
11use crate::logger::{IncMetric, METRICS};
12use crate::snapshot::Persist;
13
14#[derive(Debug, thiserror::Error, displaydoc::Display)]
15pub enum InterruptError {
17 Allocator(#[from] vm_allocator::Error),
19 Io(#[from] std::io::Error),
21 FamStruct(#[from] vmm_sys_util::fam::Error),
23 Kvm(#[from] kvm_ioctls::Error),
25 InvalidVectorIndex(usize),
27}
28
29#[derive(Copy, Clone, Debug, Default)]
31pub struct MsixVectorConfig {
32 pub high_addr: u32,
34 pub low_addr: u32,
36 pub data: u32,
38 pub devid: u32,
40}
41
42#[derive(Debug)]
44pub struct MsixVector {
45 pub gsi: u32,
47 pub event_fd: EventFd,
49 pub enabled: AtomicBool,
51}
52
53impl MsixVector {
54 pub fn new(gsi: u32, enabled: bool) -> Result<MsixVector, InterruptError> {
56 Ok(MsixVector {
57 gsi,
58 event_fd: EventFd::new(libc::EFD_NONBLOCK)?,
59 enabled: AtomicBool::new(enabled),
60 })
61 }
62}
63
64impl MsixVector {
65 pub fn enable(&self, vmfd: &VmFd) -> Result<(), InterruptError> {
67 if !self.enabled.load(Ordering::Acquire) {
68 vmfd.register_irqfd(&self.event_fd, self.gsi)?;
69 self.enabled.store(true, Ordering::Release);
70 }
71
72 Ok(())
73 }
74
75 pub fn disable(&self, vmfd: &VmFd) -> Result<(), InterruptError> {
77 if self.enabled.load(Ordering::Acquire) {
78 vmfd.unregister_irqfd(&self.event_fd, self.gsi)?;
79 self.enabled.store(false, Ordering::Release);
80 }
81
82 Ok(())
83 }
84}
85
86#[derive(Debug)]
87pub struct MsixVectorGroup {
89 pub vm: Arc<Vm>,
92 pub vectors: Vec<MsixVector>,
94}
95
96impl MsixVectorGroup {
97 pub fn num_vectors(&self) -> u16 {
99 u16::try_from(self.vectors.len()).unwrap()
102 }
103
104 pub fn enable(&self) -> Result<(), InterruptError> {
106 for route in &self.vectors {
107 route.enable(&self.vm.common.fd)?;
108 }
109
110 Ok(())
111 }
112
113 pub fn disable(&self) -> Result<(), InterruptError> {
115 for route in &self.vectors {
116 route.disable(&self.vm.common.fd)?;
117 }
118
119 Ok(())
120 }
121
122 pub fn trigger(&self, index: usize) -> Result<(), InterruptError> {
124 self.notifier(index)
125 .ok_or(InterruptError::InvalidVectorIndex(index))?
126 .write(1)?;
127 METRICS.interrupts.triggers.inc();
128 Ok(())
129 }
130
131 pub fn notifier(&self, index: usize) -> Option<&EventFd> {
134 self.vectors.get(index).map(|route| &route.event_fd)
135 }
136
137 pub fn update(
139 &self,
140 index: usize,
141 msi_config: MsixVectorConfig,
142 masked: bool,
143 set_gsi: bool,
144 ) -> Result<(), InterruptError> {
145 if let Some(vector) = self.vectors.get(index) {
146 METRICS.interrupts.config_updates.inc();
147 if masked {
151 vector.disable(&self.vm.common.fd)?;
152 }
153
154 self.vm.register_msi(vector, masked, msi_config)?;
155 if set_gsi {
156 self.vm
157 .set_gsi_routes()
158 .map_err(|err| std::io::Error::other(format!("MSI-X update: {err}")))?
159 }
160
161 if !masked {
165 vector.enable(&self.vm.common.fd)?;
166 }
167
168 return Ok(());
169 }
170
171 Err(InterruptError::InvalidVectorIndex(index))
172 }
173}
174
175impl<'a> Persist<'a> for MsixVectorGroup {
176 type State = Vec<u32>;
177 type ConstructorArgs = Arc<Vm>;
178 type Error = InterruptError;
179
180 fn save(&self) -> Self::State {
181 self.vectors.iter().map(|route| route.gsi).collect()
185 }
186
187 fn restore(
188 constructor_args: Self::ConstructorArgs,
189 state: &Self::State,
190 ) -> std::result::Result<Self, Self::Error> {
191 let mut vectors = Vec::with_capacity(state.len());
192
193 for gsi in state {
194 vectors.push(MsixVector::new(*gsi, false)?);
195 }
196
197 Ok(MsixVectorGroup {
198 vm: constructor_args,
199 vectors,
200 })
201 }
202}