Skip to content

Commit 2c700c8

Browse files
committed
Implement infrastructure to manage interrupts by KVM
Implement infrastructure to manage interrupt sources based on Linux KVM kernel module. Signed-off-by: Liu Jiang <[email protected]> Signed-off-by: Bin Zha <[email protected]>
1 parent e0d25e1 commit 2c700c8

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ edition = "2018"
88

99
[dependencies]
1010
libc = ">=0.2.39"
11+
kvm-bindings = { version = "~0", optional = true }
12+
kvm-ioctls = { version = "~0", optional = true }
1113
vmm-sys-util = "~0"
1214

1315
[features]
1416
legacy-irq = []
1517
msi-irq = []
18+
kvm-irq = ["kvm-ioctls", "kvm-bindings"]

src/interrupt/kvm/mod.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright (C) 2019 Alibaba Cloud. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Manage virtual device's interrupts based on the Linux KVM framework.
5+
//!
6+
//! When updaing KVM IRQ routing by ioctl(KVM_SET_GSI_ROUTING), all interrupts of the virtual
7+
//! machine must be updated all together. The [KvmIrqRouting](struct.KvmIrqRouting.html)
8+
//! structure is to maintain the global interrupt routing table.
9+
10+
use std::collections::HashMap;
11+
use std::io::{Error, ErrorKind};
12+
use std::sync::{Arc, Mutex};
13+
14+
use kvm_bindings::{kvm_irq_routing, kvm_irq_routing_entry};
15+
use kvm_ioctls::VmFd;
16+
17+
use super::*;
18+
19+
/// Structure to manage interrupt sources for a virtual machine based on the Linux KVM framework.
20+
///
21+
/// The KVM framework provides methods to inject interrupts into the target virtual machines,
22+
/// which uses irqfd to notity the KVM kernel module for injecting interrupts. When the interrupt
23+
/// source, usually a virtual device backend in userspace, writes to the irqfd file descriptor,
24+
/// the KVM kernel module will inject a corresponding interrupt into the target VM according to
25+
/// the IRQ routing configuration.
26+
pub struct KvmIrqManager {
27+
mgr: Mutex<KvmIrqManagerObj>,
28+
}
29+
30+
impl KvmIrqManager {
31+
/// Create a new interrupt manager based on the Linux KVM framework.
32+
///
33+
/// # Arguments
34+
/// * `vmfd`: The KVM VM file descriptor, which will be used to access the KVM subsystem.
35+
pub fn new(vmfd: Arc<VmFd>) -> Self {
36+
let vmfd2 = vmfd.clone();
37+
KvmIrqManager {
38+
mgr: Mutex::new(KvmIrqManagerObj {
39+
vmfd,
40+
groups: HashMap::new(),
41+
routes: Arc::new(KvmIrqRouting::new(vmfd2)),
42+
}),
43+
}
44+
}
45+
46+
/// Prepare the interrupt manager for generating interrupts into the target VM.
47+
pub fn initialize(&self) -> Result<()> {
48+
// Safe to unwrap because there's no legal way to break the mutex.
49+
let mgr = self.mgr.lock().unwrap();
50+
mgr.initialize()
51+
}
52+
}
53+
54+
impl InterruptManager for KvmIrqManager {
55+
fn create_group(
56+
&self,
57+
ty: InterruptSourceType,
58+
base: InterruptIndex,
59+
count: u32,
60+
) -> Result<Arc<Box<dyn InterruptSourceGroup>>> {
61+
// Safe to unwrap because there's no legal way to break the mutex.
62+
let mut mgr = self.mgr.lock().unwrap();
63+
mgr.create_group(ty, base, count)
64+
}
65+
66+
fn destroy_group(&self, group: Arc<Box<dyn InterruptSourceGroup>>) -> Result<()> {
67+
// Safe to unwrap because there's no legal way to break the mutex.
68+
let mut mgr = self.mgr.lock().unwrap();
69+
mgr.destroy_group(group)
70+
}
71+
}
72+
73+
struct KvmIrqManagerObj {
74+
vmfd: Arc<VmFd>,
75+
routes: Arc<KvmIrqRouting>,
76+
groups: HashMap<InterruptIndex, Arc<Box<dyn InterruptSourceGroup>>>,
77+
}
78+
79+
impl KvmIrqManagerObj {
80+
fn initialize(&self) -> Result<()> {
81+
self.routes.initialize()?;
82+
Ok(())
83+
}
84+
85+
fn create_group(
86+
&mut self,
87+
ty: InterruptSourceType,
88+
base: InterruptIndex,
89+
count: u32,
90+
) -> Result<Arc<Box<dyn InterruptSourceGroup>>> {
91+
#[allow(unreachable_patterns)]
92+
let group: Arc<Box<dyn InterruptSourceGroup>> = match ty {
93+
_ => return Err(Error::from(ErrorKind::InvalidInput)),
94+
};
95+
96+
self.groups.insert(base, group.clone());
97+
98+
Ok(group)
99+
}
100+
101+
fn destroy_group(&mut self, group: Arc<Box<dyn InterruptSourceGroup>>) -> Result<()> {
102+
self.groups.remove(&group.base());
103+
Ok(())
104+
}
105+
}
106+
107+
// Use (entry.type, entry.gsi) as the hash key because entry.gsi can't uniquely identify an
108+
// interrupt source on x86 platforms. The PIC and IOAPIC may share the same GSI on x86 platforms.
109+
fn hash_key(entry: &kvm_irq_routing_entry) -> u64 {
110+
let type1 = match entry.type_ {
111+
#[cfg(feature = "kvm-legacy-irq")]
112+
kvm_bindings::KVM_IRQ_ROUTING_IRQCHIP => unsafe { entry.u.irqchip.irqchip },
113+
_ => 0u32,
114+
};
115+
(u64::from(type1) << 48 | u64::from(entry.type_) << 32) | u64::from(entry.gsi)
116+
}
117+
118+
pub(super) struct KvmIrqRouting {
119+
vm_fd: Arc<VmFd>,
120+
routes: Mutex<HashMap<u64, kvm_irq_routing_entry>>,
121+
}
122+
123+
impl KvmIrqRouting {
124+
pub(super) fn new(vm_fd: Arc<VmFd>) -> Self {
125+
KvmIrqRouting {
126+
vm_fd,
127+
routes: Mutex::new(HashMap::new()),
128+
}
129+
}
130+
131+
pub(super) fn initialize(&self) -> Result<()> {
132+
// Safe to unwrap because there's no legal way to break the mutex.
133+
#[allow(unused_mut)]
134+
let mut routes = self.routes.lock().unwrap();
135+
136+
self.set_routing(&*routes)
137+
}
138+
139+
fn set_routing(&self, routes: &HashMap<u64, kvm_irq_routing_entry>) -> Result<()> {
140+
// Allocate enough buffer memory.
141+
let elem_sz = std::mem::size_of::<kvm_irq_routing>();
142+
let total_sz = std::mem::size_of::<kvm_irq_routing_entry>() * routes.len() + elem_sz;
143+
let elem_cnt = (total_sz + elem_sz - 1) / elem_sz;
144+
let mut irq_routings = Vec::<kvm_irq_routing>::with_capacity(elem_cnt);
145+
irq_routings.resize_with(elem_cnt, Default::default);
146+
147+
// Prepare the irq_routing header.
148+
let mut irq_routing = &mut irq_routings[0];
149+
irq_routing.nr = routes.len() as u32;
150+
irq_routing.flags = 0;
151+
152+
// Safe because we have just allocated enough memory above.
153+
let irq_routing_entries = unsafe { irq_routing.entries.as_mut_slice(routes.len()) };
154+
for (idx, entry) in routes.values().enumerate() {
155+
irq_routing_entries[idx] = *entry;
156+
}
157+
158+
self.vm_fd
159+
.set_gsi_routing(irq_routing)
160+
.map_err(from_sys_util_errno)?;
161+
162+
Ok(())
163+
}
164+
}
165+
166+
/// Helper function convert from vmm_sys_util::errno::Error to std::io::Error.
167+
pub fn from_sys_util_errno(e: vmm_sys_util::errno::Error) -> std::io::Error {
168+
std::io::Error::from_raw_os_error(e.errno())
169+
}

src/interrupt/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,8 @@ pub trait InterruptSourceGroup: Send + Sync {
203203
Ok(())
204204
}
205205
}
206+
207+
#[cfg(feature = "kvm-irq")]
208+
mod kvm;
209+
#[cfg(feature = "kvm-irq")]
210+
pub use self::kvm::KvmIrqManager;

0 commit comments

Comments
 (0)