Skip to content

Commit 95e652e

Browse files
committed
Manage x86 legacy interrupts based on KVM
Implement InterruptSourceGroup trait to manage x86 legacy interruts. On x86 platforms, pin-based device interrupts connecting to the master PIC, the slave PIC and IOAPICs are named as legacy interrupts. For legacy interrupts, the interrupt routing logic are manged by the PICs/IOAPICs and the interrupt group logic only takes responsibility to enable/disable the interrupts. Signed-off-by: Liu Jiang <gerry@linux.alibaba.com> Signed-off-by: Bin Zha <zhabin@linux.alibaba.com>
1 parent 4e80190 commit 95e652e

File tree

3 files changed

+184
-74
lines changed

3 files changed

+184
-74
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ vm-memory = { git = "https://github.com/rust-vmm/vm-memory" }
1616

1717
[features]
1818
kvm_irq = ["kvm-ioctls", "kvm-bindings"]
19+
legacy_irq = []

src/interrupt/kvm/legacy_irq.rs

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright (C) 2019 Alibaba Cloud. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Manage virtual device's legacy interrupts based on Linux KVM framework.
5+
//!
6+
//! On x86 platforms, legacy interrupts are those managed by the Master PIC, the slave PIC and
7+
//! IOAPICs.
8+
9+
use super::*;
10+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
11+
use kvm_bindings::{KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE};
12+
use std::sync::atomic::{AtomicUsize, Ordering};
13+
14+
pub(super) struct LegacyIrq {
15+
base: u32,
16+
vmfd: Arc<VmFd>,
17+
irqfd: EventFd,
18+
status: AtomicUsize,
19+
}
20+
21+
impl LegacyIrq {
22+
#[allow(clippy::new_ret_no_self)]
23+
pub(super) fn new(
24+
base: InterruptIndex,
25+
count: InterruptIndex,
26+
vmfd: Arc<VmFd>,
27+
_routes: Arc<KvmIrqRouting>,
28+
) -> Result<Self> {
29+
if count != 1 {
30+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
31+
}
32+
Ok(LegacyIrq {
33+
base,
34+
vmfd,
35+
irqfd: EventFd::new(0)?,
36+
status: AtomicUsize::new(0),
37+
})
38+
}
39+
40+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
41+
fn add_legacy_entry(
42+
gsi: u32,
43+
chip: u32,
44+
pin: u32,
45+
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
46+
) -> Result<()> {
47+
let mut entry = kvm_irq_routing_entry {
48+
gsi,
49+
type_: KVM_IRQ_ROUTING_IRQCHIP,
50+
..Default::default()
51+
};
52+
// Safe because we are initializing all fields of the `irqchip` struct.
53+
unsafe {
54+
entry.u.irqchip.irqchip = chip;
55+
entry.u.irqchip.pin = pin;
56+
}
57+
routes.insert(hash_key(&entry), entry);
58+
59+
Ok(())
60+
}
61+
62+
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
63+
/// Build routings for IRQs connected to the master PIC, the slave PIC or the first IOAPIC.
64+
pub(super) fn initialize_legacy(
65+
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
66+
) -> Result<()> {
67+
// Build routings for the master PIC
68+
for i in 0..8 {
69+
if i != 2 {
70+
Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_MASTER, i, routes)?;
71+
}
72+
}
73+
74+
// Build routings for the slave PIC
75+
for i in 8..16 {
76+
Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_SLAVE, i - 8, routes)?;
77+
}
78+
79+
// Build routings for the first IOAPIC
80+
for i in 0..24 {
81+
if i == 0 {
82+
Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, 2, routes)?;
83+
} else if i != 2 {
84+
Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, i, routes)?;
85+
};
86+
}
87+
88+
Ok(())
89+
}
90+
91+
#[cfg(any(target_arch = "arm", target_arch = "arm64"))]
92+
pub(super) fn initialize_legacy(
93+
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
94+
) -> Result<()> {
95+
//TODO
96+
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
97+
}
98+
}
99+
100+
impl InterruptSourceGroup for LegacyIrq {
101+
fn interrupt_type(&self) -> InterruptSourceType {
102+
InterruptSourceType::LegacyIrq
103+
}
104+
105+
fn len(&self) -> u32 {
106+
1
107+
}
108+
109+
fn base(&self) -> u32 {
110+
self.base
111+
}
112+
113+
fn irqfd(&self, index: InterruptIndex) -> Option<&EventFd> {
114+
if index != 0 {
115+
None
116+
} else {
117+
Some(&self.irqfd)
118+
}
119+
}
120+
121+
fn flags(&self, index: InterruptIndex) -> u32 {
122+
if index == 0 {
123+
self.status.load(Ordering::SeqCst) as u32
124+
} else {
125+
0
126+
}
127+
}
128+
129+
fn enable(&self, configs: &[InterruptSourceConfig]) -> Result<()> {
130+
if configs.len() != 1 {
131+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
132+
}
133+
// The IRQ routings for legacy IRQs have been configured during
134+
// KvmIrqManager::initialize(), so only need to register irqfd to the KVM driver.
135+
self.vmfd.register_irqfd(&self.irqfd, self.base)
136+
}
137+
138+
fn disable(&self) -> Result<()> {
139+
self.vmfd.unregister_irqfd(&self.irqfd, self.base)
140+
}
141+
142+
fn update(&self, index: InterruptIndex, _config: &InterruptSourceConfig) -> Result<()> {
143+
// For legacy interrupts, the routing configuration is managed by the PIC/IOAPIC interrupt
144+
// controller drivers, so nothing to do here.
145+
if index != 0 {
146+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
147+
}
148+
Ok(())
149+
}
150+
151+
fn trigger(&self, index: InterruptIndex, flags: u32) -> Result<()> {
152+
if index != 0 {
153+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
154+
}
155+
// Set interrupt status bits before writing to the irqfd.
156+
self.status.fetch_or(flags as usize, Ordering::SeqCst);
157+
self.irqfd.write(1)
158+
}
159+
160+
fn ack(&self, index: InterruptIndex, flags: u32) -> Result<()> {
161+
if index != 0 {
162+
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
163+
}
164+
// Clear interrupt status bits.
165+
self.status.fetch_and(!(flags as usize), Ordering::SeqCst);
166+
Ok(())
167+
}
168+
}

src/interrupt/kvm/mod.rs

Lines changed: 15 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,16 @@
1010
use std::collections::HashMap;
1111
use std::sync::{Arc, Mutex};
1212

13-
use kvm_bindings::{kvm_irq_routing, kvm_irq_routing_entry};
14-
#[cfg(all(
15-
feature = "legacy_irq",
16-
any(target_arch = "x86", target_arch = "x86_64")
17-
))]
18-
use kvm_bindings::{
19-
KVM_IRQCHIP_IOAPIC, KVM_IRQCHIP_PIC_MASTER, KVM_IRQCHIP_PIC_SLAVE, KVM_IRQ_ROUTING_IRQCHIP,
20-
};
13+
use kvm_bindings::{kvm_irq_routing, kvm_irq_routing_entry, KVM_IRQ_ROUTING_IRQCHIP};
2114
use kvm_ioctls::VmFd;
2215

2316
use super::*;
2417

18+
#[cfg(feature = "legacy_irq")]
19+
mod legacy_irq;
20+
#[cfg(feature = "legacy_irq")]
21+
use self::legacy_irq::LegacyIrq;
22+
2523
/// Structure to manage interrupt sources for a virtual machine based on the Linux KVM framework.
2624
///
2725
/// The KVM framework provides methods to inject interrupts into the target virtual machines,
@@ -50,8 +48,6 @@ impl KvmIrqManager {
5048
}
5149

5250
/// Prepare the interrupt manager for generating interrupts into the target VM.
53-
///
54-
/// On x86 platforms, this will set up IRQ routings for legacy IRQs.
5551
pub fn initialize(&self) -> Result<()> {
5652
// Safe to unwrap because there's no legal way to break the mutex.
5753
let mgr = self.mgr.lock().unwrap();
@@ -96,14 +92,14 @@ impl KvmIrqManagerObj {
9692
base: InterruptIndex,
9793
count: u32,
9894
) -> Result<Arc<Box<dyn InterruptSourceGroup>>> {
99-
let group = match ty {
95+
let group: Arc<Box<dyn InterruptSourceGroup>> = match ty {
10096
#[cfg(feature = "legacy_irq")]
101-
InterruptSourceType::LegacyIrq => {
102-
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
103-
return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
104-
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
105-
LegacyIrq::new(base, count, self.vmfd.clone(), self.routes.clone())?
106-
}
97+
InterruptSourceType::LegacyIrq => Arc::new(Box::new(LegacyIrq::new(
98+
base,
99+
count,
100+
self.vmfd.clone(),
101+
self.routes.clone(),
102+
)?)),
107103
#[cfg(feature = "pci_msi_irq")]
108104
InterruptSourceType::MsiIrq => {
109105
PciMsiIrq::new(base, count, self.vmfd.clone(), self.routes.clone())?
@@ -150,11 +146,8 @@ impl KvmIrqRouting {
150146
#[allow(unused_mut)]
151147
let mut routes = self.routes.lock().unwrap();
152148

153-
#[cfg(all(
154-
feature = "legacy_irq",
155-
any(target_arch = "x86", target_arch = "x86_64")
156-
))]
157-
self.initialize_legacy(&mut *routes)?;
149+
#[cfg(feature = "legacy_irq")]
150+
LegacyIrq::initialize_legacy(&mut *routes)?;
158151

159152
self.set_routing(&*routes)
160153
}
@@ -222,56 +215,4 @@ impl KvmIrqRouting {
222215
let _ = routes.insert(hash_key(entry), *entry);
223216
self.set_routing(&routes)
224217
}
225-
226-
#[cfg(feature = "legacy_irq")]
227-
pub(super) fn add_legacy_entry(
228-
gsi: u32,
229-
chip: u32,
230-
pin: u32,
231-
routes: &mut HashMap<u64, kvm_irq_routing_entry>,
232-
) -> Result<()> {
233-
let mut entry = kvm_irq_routing_entry {
234-
gsi,
235-
type_: KVM_IRQ_ROUTING_IRQCHIP,
236-
..Default::default()
237-
};
238-
// Safe because we are initializing all fields of the `irqchip` struct.
239-
unsafe {
240-
entry.u.irqchip.irqchip = chip;
241-
entry.u.irqchip.pin = pin;
242-
}
243-
routes.insert(hash_key(&entry), entry);
244-
245-
Ok(())
246-
}
247-
248-
#[cfg(all(
249-
feature = "legacy_irq",
250-
any(target_arch = "x86", target_arch = "x86_64")
251-
))]
252-
/// Build routings for IRQs connected to the master PIC, the slave PIC or the first IOAPIC.
253-
fn initialize_legacy(&self, routes: &mut HashMap<u64, kvm_irq_routing_entry>) -> Result<()> {
254-
// Build routings for the master PIC
255-
for i in 0..8 {
256-
if i != 2 {
257-
Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_MASTER, i, routes)?;
258-
}
259-
}
260-
261-
// Build routings for the slave PIC
262-
for i in 8..16 {
263-
Self::add_legacy_entry(i, KVM_IRQCHIP_PIC_SLAVE, i - 8, routes)?;
264-
}
265-
266-
// Build routings for the first IOAPIC
267-
for i in 0..24 {
268-
if i == 0 {
269-
Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, 2, routes)?;
270-
} else if i != 2 {
271-
Self::add_legacy_entry(i, KVM_IRQCHIP_IOAPIC, i, routes)?;
272-
};
273-
}
274-
275-
Ok(())
276-
}
277218
}

0 commit comments

Comments
 (0)