Skip to content

Commit ffedba1

Browse files
ramyak-mehraandreeaflorescu
authored andcommitted
Implementing IRQAllocator in vmm
We needed IRQAllocator for adding support for multiple devices. IRQ_NUM for each platform is now passed down through VMConfig. default value are used. Signed-off-by: Ramyak mehra <rmehra_be19@thapar.edu>
1 parent 1f6cdc6 commit ffedba1

File tree

4 files changed

+198
-36
lines changed

4 files changed

+198
-36
lines changed

src/vm-vcpu-ref/src/x86_64/mptable.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub enum Error {
7373
WriteMpcLintsrc,
7474
/// Failure to write MP table header.
7575
WriteMpcTable,
76+
/// IRQ num overflow.
77+
IRQOverflowed,
7678
}
7779

7880
/// Specialized `Result` type for operations on the MP Table.
@@ -129,11 +131,11 @@ fn mpf_intel_compute_checksum(v: &mpspec::mpf_intel) -> u8 {
129131
///
130132
/// ```rust
131133
/// use vm_memory::{GuestAddress, GuestMemoryMmap};
132-
/// use vm_vcpu_ref::x86_64::mptable::{MpTable, Result};
134+
/// use vm_vcpu_ref::x86_64::mptable::{MpTable, Result, IRQ_MAX};
133135
///
134136
/// fn write_mptable() -> Result<()> {
135137
/// let num_cpus = 4;
136-
/// let mptable = MpTable::new(num_cpus)?;
138+
/// let mptable = MpTable::new(num_cpus, IRQ_MAX)?;
137139
/// let mem: GuestMemoryMmap =
138140
/// GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 1024 << 20)]).unwrap();
139141
/// mptable.write(&mem)
@@ -148,15 +150,12 @@ pub struct MpTable {
148150

149151
impl MpTable {
150152
/// Creates a new MP Table that can hold `cpu_num`.
151-
pub fn new(cpu_num: u8) -> Result<MpTable> {
153+
pub fn new(cpu_num: u8, max_irq: u8) -> Result<MpTable> {
152154
if cpu_num > MAX_SUPPORTED_CPUS {
153155
return Err(Error::TooManyCpus);
154156
}
155-
156-
Ok(MpTable {
157-
irq_num: IRQ_MAX + 1,
158-
cpu_num,
159-
})
157+
let irq_num = max_irq.checked_add(1).ok_or(Error::IRQOverflowed)?;
158+
Ok(MpTable { irq_num, cpu_num })
160159
}
161160

162161
// Returns the size of this MP table based on its configuration.
@@ -365,21 +364,21 @@ mod tests {
365364
fn test_bounds_check() {
366365
// Test mptable fits in guest memory.
367366
let num_cpus = 4;
368-
let mptable = MpTable::new(num_cpus).unwrap();
367+
let mptable = MpTable::new(num_cpus, IRQ_MAX).unwrap();
369368
let mem: GuestMemoryMmap =
370369
GuestMemoryMmap::from_ranges(&[(GuestAddress(MPTABLE_START), mptable.size())]).unwrap();
371370
mptable.write(&mem).unwrap();
372371

373372
// Test mptable does not fit in guest memory.
374373
let num_cpus = 5;
375-
let mptable = MpTable::new(num_cpus).unwrap();
374+
let mptable = MpTable::new(num_cpus, IRQ_MAX).unwrap();
376375
assert_eq!(mptable.write(&mem).unwrap_err(), Error::NotEnoughMemory);
377376
}
378377

379378
#[test]
380379
fn test_mpf_intel_checksum() {
381380
let num_cpus = 1;
382-
let mptable = MpTable::new(num_cpus).unwrap();
381+
let mptable = MpTable::new(num_cpus, IRQ_MAX).unwrap();
383382
let mem: GuestMemoryMmap =
384383
GuestMemoryMmap::from_ranges(&[(GuestAddress(MPTABLE_START), mptable.size())]).unwrap();
385384

@@ -396,7 +395,7 @@ mod tests {
396395
#[test]
397396
fn test_mpc_table_checksum() {
398397
let num_cpus = 4;
399-
let mptable = MpTable::new(num_cpus).unwrap();
398+
let mptable = MpTable::new(num_cpus, IRQ_MAX).unwrap();
400399
let mem: GuestMemoryMmap =
401400
GuestMemoryMmap::from_ranges(&[(GuestAddress(MPTABLE_START), mptable.size())]).unwrap();
402401

@@ -431,7 +430,7 @@ mod tests {
431430
GuestMemoryMmap::from_ranges(&[(GuestAddress(MPTABLE_START), 1024 << 20)]).unwrap();
432431

433432
for i in 0..MAX_SUPPORTED_CPUS {
434-
MpTable::new(i).unwrap().write(&mem).unwrap();
433+
MpTable::new(i, IRQ_MAX).unwrap().write(&mem).unwrap();
435434

436435
let mpf_intel: MpfIntel = mem.read_obj(GuestAddress(MPTABLE_START)).unwrap();
437436
let mpc_offset = GuestAddress(u64::from(mpf_intel.0.physptr));
@@ -462,7 +461,17 @@ mod tests {
462461
fn test_cpu_entry_count_max() {
463462
let cpus = MAX_SUPPORTED_CPUS + 1;
464463

465-
let result = MpTable::new(cpus).unwrap_err();
464+
let result = MpTable::new(cpus, IRQ_MAX).unwrap_err();
466465
assert_eq!(result, Error::TooManyCpus);
467466
}
467+
#[test]
468+
fn test_irq_num() {
469+
let mptable = MpTable::new(MAX_SUPPORTED_CPUS, IRQ_MAX).unwrap();
470+
assert_eq!(mptable.irq_num, IRQ_MAX + 1)
471+
}
472+
#[test]
473+
fn test_max_irq_overflow() {
474+
let result = MpTable::new(MAX_SUPPORTED_CPUS, u8::MAX);
475+
assert_eq!(result.unwrap_err(), Error::IRQOverflowed)
476+
}
468477
}

src/vm-vcpu/src/vm.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Copyright 2017 The Chromium OS Authors. All rights reserved.
33
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
44

5+
#[cfg(target_arch = "x86_64")]
6+
use std::convert::TryInto;
57
use std::io::{self, ErrorKind};
68
use std::sync::{Arc, Barrier, Mutex};
79
use std::thread::{self, JoinHandle};
@@ -27,19 +29,26 @@ use vm_vcpu_ref::aarch64::interrupts::{self, Gic, GicConfig, GicState};
2729
#[cfg(target_arch = "x86_64")]
2830
use vm_vcpu_ref::x86_64::mptable::{self, MpTable};
2931

32+
#[cfg(target_arch = "aarch64")]
33+
pub const MAX_IRQ: u32 = interrupts::MIN_NR_IRQS;
34+
#[cfg(target_arch = "x86_64")]
35+
pub const MAX_IRQ: u32 = mptable::IRQ_MAX as u32;
36+
3037
/// Defines the configuration of this VM.
3138
#[derive(Clone)]
3239
pub struct VmConfig {
3340
pub num_vcpus: u8,
3441
pub vcpus_config: VcpuConfigList,
42+
pub max_irq: u32,
3543
}
3644

3745
impl VmConfig {
3846
/// Creates a default `VmConfig` for `num_vcpus`.
39-
pub fn new(kvm: &Kvm, num_vcpus: u8) -> Result<Self> {
47+
pub fn new(kvm: &Kvm, num_vcpus: u8, max_irq: u32) -> Result<Self> {
4048
Ok(VmConfig {
4149
num_vcpus,
4250
vcpus_config: VcpuConfigList::new(kvm, num_vcpus).map_err(Error::CreateVmConfig)?,
51+
max_irq,
4352
})
4453
}
4554
}
@@ -148,6 +157,10 @@ pub enum Error {
148157
/// Failed to save the state of vCPUs.
149158
#[error("Failed to save the state of vCPUs: {0}")]
150159
SaveVcpuState(vcpu::Error),
160+
#[cfg(target_arch = "x86_64")]
161+
/// Invalid max IRQ value.
162+
#[error("Invalid maximum number of IRQ: {0}")]
163+
IRQMaxValue(u32),
151164
}
152165

153166
#[cfg(target_arch = "x86_64")]
@@ -229,8 +242,14 @@ impl<EH: 'static + ExitHandler + Send> KvmVm<EH> {
229242
let mut vm = Self::create_vm(kvm, vm_config, exit_handler, guest_memory)?;
230243

231244
#[cfg(target_arch = "x86_64")]
232-
MpTable::new(vm.config.num_vcpus)?.write(guest_memory)?;
233-
245+
{
246+
let max_irq: u8 = vm
247+
.config
248+
.max_irq
249+
.try_into()
250+
.map_err(|_| Error::IRQMaxValue(vm.config.max_irq))?;
251+
MpTable::new(vm.config.num_vcpus, max_irq)?.write(guest_memory)?;
252+
}
234253
#[cfg(target_arch = "x86_64")]
235254
vm.setup_irq_controller()?;
236255

@@ -314,6 +333,11 @@ impl<EH: 'static + ExitHandler + Send> KvmVm<EH> {
314333
self.gic.as_ref().expect("GIC is not set")
315334
}
316335

336+
/// Returns the max irq number independent of arch.
337+
pub fn max_irq(&self) -> u32 {
338+
self.config.max_irq
339+
}
340+
317341
// Create the kvm memory regions based on the configuration passed as `guest_memory`.
318342
fn configure_memory_regions<M: GuestMemory>(&self, guest_memory: &M, kvm: &Kvm) -> Result<()> {
319343
if guest_memory.num_regions() > kvm.get_nr_memslots() {
@@ -376,6 +400,7 @@ impl<EH: 'static + ExitHandler + Send> KvmVm<EH> {
376400
let gic = Gic::new(
377401
GicConfig {
378402
num_cpus: self.config.num_vcpus,
403+
num_irqs: self.config.max_irq,
379404
..Default::default()
380405
},
381406
&self.vm_fd(),
@@ -612,7 +637,7 @@ mod tests {
612637
guest_memory: &GuestMemoryMmap,
613638
num_vcpus: u8,
614639
) -> Result<KvmVm<WrappedExitHandler>> {
615-
let vm_config = VmConfig::new(kvm, num_vcpus).unwrap();
640+
let vm_config = VmConfig::new(kvm, num_vcpus, MAX_IRQ).unwrap();
616641
let io_manager = Arc::new(Mutex::new(IoManager::new()));
617642
let exit_handler = WrappedExitHandler::default();
618643
let vm = KvmVm::new(kvm, vm_config, guest_memory, exit_handler, io_manager)?;
@@ -641,6 +666,21 @@ mod tests {
641666
assert!(matches!(res, Err(Error::Mptable(_))));
642667
}
643668

669+
#[test]
670+
#[cfg(target_arch = "x86_64")]
671+
fn test_max_irq_overflow() {
672+
let num_vcpus = 1;
673+
let kvm = Kvm::new().unwrap();
674+
let guest_memory = default_memory();
675+
676+
let vm_config = VmConfig::new(&kvm, num_vcpus, u32::MAX).unwrap();
677+
let io_manager = Arc::new(Mutex::new(IoManager::new()));
678+
let exit_handler = WrappedExitHandler::default();
679+
let vm = KvmVm::new(&kvm, vm_config, &guest_memory, exit_handler, io_manager);
680+
681+
assert!(matches!(vm, Err(Error::IRQMaxValue(_))))
682+
}
683+
644684
#[test]
645685
fn test_failed_setup_memory() {
646686
let kvm = Kvm::new().unwrap();
@@ -661,7 +701,7 @@ mod tests {
661701
fn test_failed_irqchip_setup() {
662702
let kvm = Kvm::new().unwrap();
663703
let num_vcpus = 1;
664-
let vm_state = VmConfig::new(&kvm, num_vcpus).unwrap();
704+
let vm_state = VmConfig::new(&kvm, num_vcpus, MAX_IRQ).unwrap();
665705
let mut vm = KvmVm {
666706
vcpus: Vec::new(),
667707
vcpu_handles: Vec::new(),
@@ -670,7 +710,6 @@ mod tests {
670710
fd: Arc::new(kvm.create_vm().unwrap()),
671711
exit_handler: WrappedExitHandler::default(),
672712
vcpu_run_state: Arc::new(VcpuRunState::default()),
673-
674713
#[cfg(target_arch = "aarch64")]
675714
gic: None,
676715
};

src/vmm/src/irq_allocator.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3+
4+
use std::fmt;
5+
6+
#[derive(Debug, PartialEq)]
7+
pub enum Error {
8+
InvalidValue,
9+
MaxIrq,
10+
IRQOverflowed,
11+
}
12+
13+
pub type Result<T> = std::result::Result<T, Error>;
14+
15+
/// An irq allocator which gives next available irq.
16+
/// It is mainly used for non-legacy devices.
17+
// There are a few reserved irq's on x86_64. We just skip all the inital
18+
// reserved irq to make the implementaion simple. This could be later extended
19+
// to cater more complex scenario.
20+
#[derive(Debug)]
21+
pub struct IrqAllocator {
22+
// Tracks the last allocated irq
23+
last_used_irq: u32,
24+
last_irq: u32,
25+
}
26+
27+
impl IrqAllocator {
28+
pub fn new(last_used_irq: u32, last_irq: u32) -> Result<Self> {
29+
if last_used_irq >= last_irq {
30+
return Err(Error::InvalidValue);
31+
}
32+
Ok(IrqAllocator {
33+
last_used_irq,
34+
last_irq,
35+
})
36+
}
37+
38+
pub fn next_irq(&mut self) -> Result<u32> {
39+
self.last_used_irq
40+
.checked_add(1)
41+
.ok_or(Error::IRQOverflowed)
42+
.and_then(|irq| {
43+
if irq > self.last_irq {
44+
Err(Error::MaxIrq)
45+
} else {
46+
self.last_used_irq = irq;
47+
Ok(irq)
48+
}
49+
})
50+
}
51+
}
52+
53+
impl fmt::Display for Error {
54+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55+
let err = match self {
56+
Error::MaxIrq => "last_irq IRQ limit reached",
57+
Error::IRQOverflowed => "IRQ overflowed",
58+
Error::InvalidValue => {
59+
"Check the value of last_used and last_irq. las_used should be less than last_irq"
60+
}
61+
};
62+
write!(f, "{}", err) // user-facing output
63+
}
64+
}
65+
66+
#[cfg(test)]
67+
mod test {
68+
use super::{Error, IrqAllocator};
69+
#[test]
70+
fn test_new() {
71+
let irq_alloc = IrqAllocator::new(4, 10).unwrap();
72+
assert_eq!(irq_alloc.last_used_irq, 4);
73+
assert_eq!(irq_alloc.last_irq, 10);
74+
let irq_alloc = IrqAllocator::new(4, 4).unwrap_err();
75+
assert_eq!(irq_alloc, Error::InvalidValue);
76+
let irq_alloc = IrqAllocator::new(4, 3).unwrap_err();
77+
assert_eq!(irq_alloc, Error::InvalidValue);
78+
}
79+
#[test]
80+
fn test_next_irq() {
81+
let mut irq_alloc = IrqAllocator::new(4, 7).unwrap();
82+
assert_eq!(irq_alloc.next_irq(), Ok(5));
83+
84+
let _ = irq_alloc.next_irq();
85+
assert_eq!(irq_alloc.next_irq(), Ok(7));
86+
87+
assert_eq!(irq_alloc.next_irq(), Err(Error::MaxIrq));
88+
89+
let mut irq_alloc = IrqAllocator::new(u32::MAX - 1, u32::MAX).unwrap();
90+
assert_eq!(irq_alloc.next_irq(), Ok(u32::MAX));
91+
assert_eq!(irq_alloc.next_irq(), Err(Error::IRQOverflowed))
92+
}
93+
}

0 commit comments

Comments
 (0)