Skip to content

Commit b395de2

Browse files
committed
Manage PCI MSI/PCI MSI-x interrupts
Implement interrupt source driver to manage PCI MSI/MSI-x interrupts. Signed-off-by: Liu Jiang <[email protected]> Signed-off-by: Bin Zha <[email protected]>
1 parent 7263cd9 commit b395de2

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ msi-irq = []
1818
kvm-irq = ["kvm-ioctls", "kvm-bindings"]
1919
kvm-msi-generic = ["msi-irq", "kvm-irq"]
2020
kvm-legacy-irq = ["legacy-irq", "kvm-irq"]
21+
kvm-msi-irq = ["kvm-msi-generic"]

src/interrupt/kvm/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@ mod legacy_irq;
2222
use self::legacy_irq::LegacyIrq;
2323
#[cfg(feature = "kvm-msi-generic")]
2424
mod msi_generic;
25+
#[cfg(feature = "kvm-msi-irq")]
26+
mod msi_irq;
27+
#[cfg(feature = "kvm-msi-irq")]
28+
use self::msi_irq::MsiIrq;
2529

2630
/// Maximum number of global interrupt sources.
2731
pub const MAX_IRQS: InterruptIndex = 1024;
2832

33+
/// Default maximum number of Message Signaled Interrupts per device.
34+
pub const DEFAULT_MAX_MSI_IRQS_PER_DEVICE: InterruptIndex = 128;
35+
2936
/// Structure to manage interrupt sources for a virtual machine based on the Linux KVM framework.
3037
///
3138
/// The KVM framework provides methods to inject interrupts into the target virtual machines,
@@ -49,6 +56,7 @@ impl KvmIrqManager {
4956
vmfd,
5057
groups: HashMap::new(),
5158
routes: Arc::new(KvmIrqRouting::new(vmfd2)),
59+
max_msi_irqs: DEFAULT_MAX_MSI_IRQS_PER_DEVICE,
5260
}),
5361
}
5462
}
@@ -59,6 +67,12 @@ impl KvmIrqManager {
5967
let mgr = self.mgr.lock().unwrap();
6068
mgr.initialize()
6169
}
70+
71+
/// Set maximum supported MSI interrupts per device.
72+
pub fn set_max_msi_irqs(&self, max_msi_irqs: InterruptIndex) {
73+
let mut mgr = self.mgr.lock().unwrap();
74+
mgr.max_msi_irqs = max_msi_irqs;
75+
}
6276
}
6377

6478
impl InterruptManager for KvmIrqManager {
@@ -84,6 +98,7 @@ struct KvmIrqManagerObj {
8498
vmfd: Arc<VmFd>,
8599
routes: Arc<KvmIrqRouting>,
86100
groups: HashMap<InterruptIndex, Arc<Box<dyn InterruptSourceGroup>>>,
101+
max_msi_irqs: InterruptIndex,
87102
}
88103

89104
impl KvmIrqManagerObj {
@@ -107,6 +122,14 @@ impl KvmIrqManagerObj {
107122
self.vmfd.clone(),
108123
self.routes.clone(),
109124
)?)),
125+
#[cfg(feature = "kvm-msi-irq")]
126+
InterruptSourceType::MsiIrq => Arc::new(Box::new(MsiIrq::new(
127+
base,
128+
count,
129+
self.max_msi_irqs,
130+
self.vmfd.clone(),
131+
self.routes.clone(),
132+
)?)),
110133
_ => return Err(Error::from(ErrorKind::InvalidInput)),
111134
};
112135

src/interrupt/kvm/msi_irq.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright (C) 2019 Alibaba Cloud. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Manage virtual device's PCI MSI/PCI MSIx interrupts based on Linux KVM framework.
5+
//!
6+
//! To optimize for performance by avoiding unnecessary locking and state checking, we assume that
7+
//! the caller will take the responsibility to maintain the interrupt states and only issue valid
8+
//! requests to this driver. If the caller doesn't obey the contract, only the current virtual
9+
//! machine will be affected, it shouldn't break the host or other virtual machines.
10+
11+
use super::msi_generic::{create_msi_routing_entries, new_msi_routing_entry, MsiConfig};
12+
use super::*;
13+
14+
pub(super) struct MsiIrq {
15+
base: InterruptIndex,
16+
count: InterruptIndex,
17+
vmfd: Arc<VmFd>,
18+
irq_routing: Arc<KvmIrqRouting>,
19+
msi_configs: Vec<MsiConfig>,
20+
}
21+
22+
impl MsiIrq {
23+
#[allow(clippy::new_ret_no_self)]
24+
pub(super) fn new(
25+
base: InterruptIndex,
26+
count: InterruptIndex,
27+
max_msi_irqs: InterruptIndex,
28+
vmfd: Arc<VmFd>,
29+
irq_routing: Arc<KvmIrqRouting>,
30+
) -> Result<Self> {
31+
if count > max_msi_irqs || base >= MAX_IRQS || base + count > MAX_IRQS {
32+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
33+
}
34+
35+
let mut msi_configs = Vec::with_capacity(count as usize);
36+
for _ in 0..count {
37+
msi_configs.push(MsiConfig::new());
38+
}
39+
40+
Ok(MsiIrq {
41+
base,
42+
count,
43+
vmfd,
44+
irq_routing,
45+
msi_configs,
46+
})
47+
}
48+
}
49+
50+
impl InterruptSourceGroup for MsiIrq {
51+
fn interrupt_type(&self) -> InterruptSourceType {
52+
InterruptSourceType::MsiIrq
53+
}
54+
55+
fn len(&self) -> u32 {
56+
self.count
57+
}
58+
59+
fn base(&self) -> u32 {
60+
self.base
61+
}
62+
63+
fn enable(&self, configs: &[InterruptSourceConfig]) -> Result<()> {
64+
if configs.len() != self.count as usize {
65+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
66+
}
67+
68+
// First add IRQ routings for all the MSI interrupts.
69+
let entries = create_msi_routing_entries(self.base, configs)?;
70+
self.irq_routing.add(&entries)?;
71+
72+
// Then register irqfds to the KVM module.
73+
for i in 0..self.count {
74+
let irqfd = &self.msi_configs[i as usize].irqfd;
75+
self.vmfd
76+
.register_irqfd(irqfd, self.base + i)
77+
.map_err(from_sys_util_errno)?;
78+
}
79+
80+
Ok(())
81+
}
82+
83+
fn disable(&self) -> Result<()> {
84+
// First unregister all irqfds, so it won't trigger anymore.
85+
for i in 0..self.count {
86+
let irqfd = &self.msi_configs[i as usize].irqfd;
87+
self.vmfd
88+
.unregister_irqfd(irqfd, self.base + i)
89+
.map_err(from_sys_util_errno)?;
90+
}
91+
92+
// Then tear down the IRQ routings for all the MSI interrupts.
93+
let mut entries = Vec::with_capacity(self.count as usize);
94+
for i in 0..self.count {
95+
// Safe to unwrap because there's no legal way to break the mutex.
96+
let msicfg = self.msi_configs[i as usize].config.lock().unwrap();
97+
let entry = new_msi_routing_entry(self.base + i, &*msicfg);
98+
entries.push(entry);
99+
}
100+
self.irq_routing.remove(&entries)?;
101+
102+
Ok(())
103+
}
104+
105+
#[allow(irrefutable_let_patterns)]
106+
fn update(&self, index: InterruptIndex, config: &InterruptSourceConfig) -> Result<()> {
107+
if index >= self.count {
108+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
109+
}
110+
111+
if let InterruptSourceConfig::MsiIrq(ref cfg) = config {
112+
// Safe to unwrap because there's no legal way to break the mutex.
113+
let entry = {
114+
let mut msicfg = self.msi_configs[index as usize].config.lock().unwrap();
115+
msicfg.high_addr = cfg.high_addr;
116+
msicfg.low_addr = cfg.low_addr;
117+
msicfg.data = cfg.data;
118+
new_msi_routing_entry(self.base + index, &*msicfg)
119+
};
120+
self.irq_routing.modify(&entry)
121+
} else {
122+
Err(std::io::Error::from_raw_os_error(libc::EINVAL))
123+
}
124+
}
125+
126+
fn notifier(&self, index: InterruptIndex) -> Option<&EventFd> {
127+
if index >= self.count {
128+
None
129+
} else {
130+
let msi_config = &self.msi_configs[index as usize];
131+
Some(&msi_config.irqfd)
132+
}
133+
}
134+
135+
fn trigger(&self, index: InterruptIndex) -> Result<()> {
136+
// Assume that the caller will maintain the interrupt states and only call this function
137+
// when suitable.
138+
if index >= self.count {
139+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
140+
}
141+
let msi_config = &self.msi_configs[index as usize];
142+
msi_config.irqfd.write(1)
143+
}
144+
}

0 commit comments

Comments
 (0)