Skip to content

Commit 5559e26

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 021d785 commit 5559e26

File tree

3 files changed

+154
-0
lines changed

3 files changed

+154
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ vm-memory = { git = "https://github.com/rust-vmm/vm-memory" }
1818
kvm_irq = ["kvm-ioctls", "kvm-bindings"]
1919
legacy_irq = []
2020
msi_irq = []
21+
pci_msi_irq = ["msi_irq"]

src/interrupt/kvm_irq/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ use self::legacy_irq::LegacyIrq;
3636
#[cfg(feature = "msi_irq")]
3737
mod msi_irq;
3838

39+
#[cfg(feature = "pci_msi_irq")]
40+
mod pci_msi_irq;
41+
#[cfg(feature = "pci_msi_irq")]
42+
use self::pci_msi_irq::PciMsiIrq;
43+
3944
/// Structure to manage interrupt sources for a virtual machine based on the Linux KVM framework.
4045
///
4146
/// The KVM framework provides methods to inject interrupts into the target virtual machines,
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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_irq::{create_msi_routing_entries, new_msi_routing_entry, MsiConfig};
12+
use super::*;
13+
use std::os::unix::io::AsRawFd;
14+
15+
pub(super) struct PciMsiIrq {
16+
base: InterruptIndex,
17+
count: InterruptIndex,
18+
vmfd: Arc<VmFd>,
19+
irq_routing: Arc<KvmIrqRouting>,
20+
msi_configs: Vec<MsiConfig>,
21+
}
22+
23+
impl PciMsiIrq {
24+
#[allow(clippy::new_ret_no_self)]
25+
pub(super) fn new(
26+
base: InterruptIndex,
27+
count: InterruptIndex,
28+
vmfd: Arc<VmFd>,
29+
irq_routing: Arc<KvmIrqRouting>,
30+
) -> Result<Arc<Box<dyn InterruptSourceGroup>>> {
31+
if count > MAX_MSI_IRQS_PER_DEVICE || 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(Arc::new(Box::new(PciMsiIrq {
41+
base,
42+
count,
43+
vmfd,
44+
irq_routing,
45+
msi_configs,
46+
})))
47+
}
48+
}
49+
50+
impl InterruptSourceGroup for PciMsiIrq {
51+
fn get_type(&self) -> InterruptSourceType {
52+
InterruptSourceType::MsiIrq
53+
}
54+
55+
fn len(&self) -> u32 {
56+
self.count
57+
}
58+
59+
fn get_base(&self) -> u32 {
60+
self.base
61+
}
62+
63+
fn get_irqfd(&self, index: InterruptIndex) -> Option<&EventFd> {
64+
if index >= self.count {
65+
None
66+
} else {
67+
let msi_config = &self.msi_configs[index as usize];
68+
Some(&msi_config.irqfd)
69+
}
70+
}
71+
72+
fn enable(&self, configs: &[InterruptSourceConfig]) -> Result<()> {
73+
if configs.len() != self.count as usize {
74+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
75+
}
76+
77+
// First add IRQ routings for all the MSI interrupts.
78+
let entries = create_msi_routing_entries(self.base, configs)?;
79+
self.irq_routing.add(&entries)?;
80+
81+
// Then register irqfds to the KVM module.
82+
for i in 0..self.count {
83+
let irqfd = self.msi_configs[i as usize].irqfd.as_raw_fd();
84+
self.vmfd.register_irqfd(irqfd, self.base + i)?;
85+
}
86+
87+
Ok(())
88+
}
89+
90+
fn disable(&self) -> Result<()> {
91+
// First unregister all irqfds, so it won't trigger anymore.
92+
for i in 0..self.count {
93+
let irqfd = self.msi_configs[i as usize].irqfd.as_raw_fd();
94+
self.vmfd.unregister_irqfd(irqfd, self.base + i)?;
95+
}
96+
97+
// Then tear down the IRQ routings for all the MSI interrupts.
98+
let mut entries = Vec::with_capacity(self.count as usize);
99+
for i in 0..self.count {
100+
// Safe to unwrap because there's no legal way to break the mutex.
101+
let msicfg = self.msi_configs[i as usize].config.lock().unwrap();
102+
let entry = new_msi_routing_entry(self.base + i, &*msicfg);
103+
entries.push(entry);
104+
}
105+
self.irq_routing.remove(&entries)?;
106+
107+
Ok(())
108+
}
109+
110+
#[allow(irrefutable_let_patterns)]
111+
fn modify(&self, index: InterruptIndex, config: &InterruptSourceConfig) -> Result<()> {
112+
if index >= self.count {
113+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
114+
}
115+
116+
if let InterruptSourceConfig::MsiIrq(ref cfg) = config {
117+
// Safe to unwrap because there's no legal way to break the mutex.
118+
let entry = {
119+
let mut msicfg = self.msi_configs[index as usize].config.lock().unwrap();
120+
msicfg.high_addr = cfg.high_addr;
121+
msicfg.low_addr = cfg.low_addr;
122+
msicfg.data = cfg.data;
123+
new_msi_routing_entry(self.base + index, &*msicfg)
124+
};
125+
self.irq_routing.modify(&entry)
126+
} else {
127+
Err(std::io::Error::from_raw_os_error(libc::EINVAL))
128+
}
129+
}
130+
131+
fn trigger(&self, index: InterruptIndex, flags: u32) -> Result<()> {
132+
// Assume that the caller will maintain the interrupt states and only call this function
133+
// when suitable.
134+
if index >= self.count || flags != 0 {
135+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
136+
}
137+
let msi_config = &self.msi_configs[index as usize];
138+
msi_config.irqfd.write(1)
139+
}
140+
141+
fn ack(&self, index: InterruptIndex, flags: u32) -> Result<()> {
142+
// It's a noop to acknowledge an edge triggered MSI interrupts.
143+
if index >= self.count || flags != 0 {
144+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
145+
}
146+
Ok(())
147+
}
148+
}

0 commit comments

Comments
 (0)