Skip to content

Commit 7f17889

Browse files
committed
fix(msi): allocate GSI for MSI and legacy IRQ from different ranges
Currently, we're limited to 24 GSI lines, which is too little for PCI devices. Keep the current ranges as "legacy IRQ", and create a new range for "GSI" that goes up to the kvm theoretical maximum of 4096 lines. Signed-off-by: Riccardo Mancini <mancio@amazon.com>
1 parent e8c599c commit 7f17889

File tree

8 files changed

+155
-50
lines changed

8 files changed

+155
-50
lines changed

src/vmm/src/arch/aarch64/layout.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,12 @@ pub const FDT_MAX_SIZE: usize = 0x20_0000;
7878
// * a multiple of 32.
7979
/// The highest usable SPI on aarch64.
8080
pub const IRQ_MAX: u32 = 128;
81-
8281
/// First usable interrupt on aarch64.
8382
pub const IRQ_BASE: u32 = 32;
83+
/// The highest available GSI in KVM (KVM_MAX_IRQ_ROUTES=4096)
84+
pub const GSI_MAX: u32 = 4095;
85+
/// First GSI after legacy IRQ on aarch64.
86+
pub const GSI_BASE: u32 = 129;
8487

8588
/// The start of the memory area reserved for MMIO 32-bit accesses.
8689
/// Below this address will reside the GIC, above this address will reside the MMIO devices.

src/vmm/src/arch/mod.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ pub use aarch64::vm::{ArchVm, ArchVmError, VmState};
2121
#[cfg(target_arch = "aarch64")]
2222
pub use aarch64::{
2323
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
24-
initrd_load_addr, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE, layout::IRQ_BASE,
25-
layout::IRQ_MAX, layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
26-
layout::MEM_64BIT_DEVICES_SIZE, layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE,
27-
layout::MMIO32_MEM_START, layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
24+
initrd_load_addr, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE, layout::GSI_BASE,
25+
layout::GSI_MAX, layout::IRQ_BASE, layout::IRQ_MAX, layout::MEM_32BIT_DEVICES_SIZE,
26+
layout::MEM_32BIT_DEVICES_START, layout::MEM_64BIT_DEVICES_SIZE,
27+
layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE, layout::MMIO32_MEM_START,
28+
layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
2829
layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, layout::RTC_MEM_START, layout::SERIAL_MEM_START,
2930
layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
3031
};
@@ -44,10 +45,10 @@ pub use x86_64::vm::{ArchVm, ArchVmError, VmState};
4445
pub use crate::arch::x86_64::{
4546
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
4647
initrd_load_addr, layout::APIC_ADDR, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE,
47-
layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX, layout::MEM_32BIT_DEVICES_SIZE,
48-
layout::MEM_32BIT_DEVICES_START, layout::MEM_64BIT_DEVICES_SIZE,
49-
layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE, layout::MMIO32_MEM_START,
50-
layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
48+
layout::GSI_BASE, layout::GSI_MAX, layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX,
49+
layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
50+
layout::MEM_64BIT_DEVICES_SIZE, layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE,
51+
layout::MMIO32_MEM_START, layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
5152
layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START,
5253
load_kernel,
5354
};

src/vmm/src/arch/x86_64/layout.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ pub const HIMEM_START: u64 = 0x0010_0000; // 1 MB.
2626
pub const IRQ_BASE: u32 = 5;
2727
/// Last usable IRQ ID for virtio device interrupts on x86_64.
2828
pub const IRQ_MAX: u32 = 23;
29+
/// First GSI after legacy interrupts/
30+
pub const GSI_BASE: u32 = 24;
31+
/// The highest available GSI in KVM (KVM_MAX_IRQ_ROUTES=4096)
32+
pub const GSI_MAX: u32 = 4095;
2933

3034
/// Address for the TSS setup.
3135
pub const KVM_TSS_ADDRESS: u64 = 0xfffb_d000;

src/vmm/src/device_manager/mmio.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ impl MMIODeviceManager {
157157
resource_allocator: &mut ResourceAllocator,
158158
irq_count: u32,
159159
) -> Result<MMIODeviceInfo, MmioError> {
160-
let irq = match resource_allocator.allocate_gsi(irq_count)?[..] {
160+
let irq = match resource_allocator.allocate_irq(irq_count)?[..] {
161161
[] => None,
162162
[irq] => NonZeroU32::new(irq),
163163
_ => return Err(MmioError::InvalidIrqConfig),
@@ -277,11 +277,11 @@ impl MMIODeviceManager {
277277
let device_info = if let Some(device_info) = device_info_opt {
278278
device_info
279279
} else {
280-
let gsi = vm.resource_allocator().allocate_gsi(1)?;
280+
let irq = vm.resource_allocator().allocate_irq(1)?;
281281
MMIODeviceInfo {
282282
addr: SERIAL_MEM_START,
283283
len: MMIO_LEN,
284-
irq: NonZeroU32::new(gsi[0]),
284+
irq: NonZeroU32::new(irq[0]),
285285
}
286286
};
287287

@@ -336,11 +336,11 @@ impl MMIODeviceManager {
336336
let device_info = if let Some(device_info) = device_info_opt {
337337
device_info
338338
} else {
339-
let gsi = vm.resource_allocator().allocate_gsi(1)?;
339+
let irq = vm.resource_allocator().allocate_irq(1)?;
340340
MMIODeviceInfo {
341341
addr: RTC_MEM_START,
342342
len: MMIO_LEN,
343-
irq: NonZeroU32::new(gsi[0]),
343+
irq: NonZeroU32::new(irq[0]),
344344
}
345345
};
346346

src/vmm/src/devices/acpi/vmgenid.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ impl VmGenId {
8888
mem: &GuestMemoryMmap,
8989
resource_allocator: &mut ResourceAllocator,
9090
) -> Result<Self, VmGenIdError> {
91-
let gsi = resource_allocator.allocate_gsi(1)?;
91+
let irq = resource_allocator.allocate_irq(1)?;
9292
// The generation ID needs to live in an 8-byte aligned buffer
9393
let addr = resource_allocator.allocate_system_memory(
9494
VMGENID_MEM_SIZE,
9595
8,
9696
vm_allocator::AllocPolicy::LastMatch,
9797
)?;
9898

99-
Self::from_parts(GuestAddress(addr), gsi[0], mem)
99+
Self::from_parts(GuestAddress(addr), irq[0], mem)
100100
}
101101

102102
// Create a 16-bytes random number

src/vmm/src/vstate/resources.rs

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use crate::snapshot::Persist;
1919
/// * Memory allocations in the MMIO address space
2020
#[derive(Debug, Clone, Serialize, Deserialize)]
2121
pub struct ResourceAllocator {
22-
/// Allocator for device interrupt lines
22+
/// Allocator for legacy device interrupt lines
23+
pub irq_allocator: IdAllocator,
24+
/// Allocator for PCI device GSIs
2325
pub gsi_allocator: IdAllocator,
2426
/// Allocator for memory in the 32-bit MMIO address space
2527
pub mmio32_memory: AddressAllocator,
@@ -41,7 +43,8 @@ impl ResourceAllocator {
4143
// It is fine for us to unwrap the following since we know we are passing valid ranges for
4244
// all allocators
4345
Self {
44-
gsi_allocator: IdAllocator::new(arch::IRQ_BASE, arch::IRQ_MAX).unwrap(),
46+
irq_allocator: IdAllocator::new(arch::IRQ_BASE, arch::IRQ_MAX).unwrap(),
47+
gsi_allocator: IdAllocator::new(arch::GSI_BASE, arch::GSI_MAX).unwrap(),
4548
mmio32_memory: AddressAllocator::new(
4649
arch::MEM_32BIT_DEVICES_START,
4750
arch::MEM_32BIT_DEVICES_SIZE,
@@ -57,6 +60,30 @@ impl ResourceAllocator {
5760
}
5861
}
5962

63+
/// Allocate a number of legacy IRQs
64+
///
65+
/// # Arguments
66+
///
67+
/// * `irq_count` - The number of legacy IRQs to allocate
68+
pub fn allocate_irq(&mut self, irq_count: u32) -> Result<Vec<u32>, vm_allocator::Error> {
69+
let mut irqs = Vec::with_capacity(irq_count as usize);
70+
71+
for _ in 0..irq_count {
72+
match self.irq_allocator.allocate_id() {
73+
Ok(irq) => irqs.push(irq),
74+
Err(err) => {
75+
// It is ok to unwrap here, we just allocated the GSI
76+
irqs.into_iter().for_each(|irq| {
77+
self.irq_allocator.free_id(irq).unwrap();
78+
});
79+
return Err(err);
80+
}
81+
}
82+
}
83+
84+
Ok(irqs)
85+
}
86+
6087
/// Allocate a number of GSIs
6188
///
6289
/// # Arguments
@@ -167,25 +194,66 @@ mod tests {
167194
use vm_allocator::AllocPolicy;
168195

169196
use super::ResourceAllocator;
170-
use crate::arch::{self, IRQ_BASE};
197+
use crate::arch::{self, GSI_BASE, IRQ_BASE};
171198
use crate::snapshot::{Persist, Snapshot};
172199

173200
const MAX_IRQS: u32 = arch::IRQ_MAX - arch::IRQ_BASE + 1;
174201

202+
#[test]
203+
fn test_allocate_irq() {
204+
let mut allocator = ResourceAllocator::new();
205+
// asking for 0 IRQs should return us an empty vector
206+
assert_eq!(allocator.allocate_irq(0), Ok(vec![]));
207+
// We cannot allocate more GSIs than available
208+
assert_eq!(
209+
allocator.allocate_irq(MAX_IRQS + 1),
210+
Err(vm_allocator::Error::ResourceNotAvailable)
211+
);
212+
// But allocating all of them at once should work
213+
assert_eq!(
214+
allocator.allocate_irq(MAX_IRQS),
215+
Ok((arch::IRQ_BASE..=arch::IRQ_MAX).collect::<Vec<_>>())
216+
);
217+
// And now we ran out of GSIs
218+
assert_eq!(
219+
allocator.allocate_irq(1),
220+
Err(vm_allocator::Error::ResourceNotAvailable)
221+
);
222+
// But we should be able to ask for 0 GSIs
223+
assert_eq!(allocator.allocate_irq(0), Ok(vec![]));
224+
225+
let mut allocator = ResourceAllocator::new();
226+
// We should be able to allocate 1 GSI
227+
assert_eq!(allocator.allocate_irq(1), Ok(vec![arch::IRQ_BASE]));
228+
// We can't allocate MAX_IRQS any more
229+
assert_eq!(
230+
allocator.allocate_irq(MAX_IRQS),
231+
Err(vm_allocator::Error::ResourceNotAvailable)
232+
);
233+
// We can allocate another one and it should be the second available
234+
assert_eq!(allocator.allocate_irq(1), Ok(vec![arch::IRQ_BASE + 1]));
235+
// Let's allocate the rest in a loop
236+
for i in arch::IRQ_BASE + 2..=arch::IRQ_MAX {
237+
assert_eq!(allocator.allocate_irq(1), Ok(vec![i]));
238+
}
239+
}
240+
241+
const MAX_GSIS: u32 = arch::GSI_MAX - arch::GSI_BASE + 1;
242+
175243
#[test]
176244
fn test_allocate_gsi() {
177245
let mut allocator = ResourceAllocator::new();
178246
// asking for 0 IRQs should return us an empty vector
179247
assert_eq!(allocator.allocate_gsi(0), Ok(vec![]));
180248
// We cannot allocate more GSIs than available
181249
assert_eq!(
182-
allocator.allocate_gsi(MAX_IRQS + 1),
250+
allocator.allocate_gsi(MAX_GSIS + 1),
183251
Err(vm_allocator::Error::ResourceNotAvailable)
184252
);
185253
// But allocating all of them at once should work
186254
assert_eq!(
187-
allocator.allocate_gsi(MAX_IRQS),
188-
Ok((arch::IRQ_BASE..=arch::IRQ_MAX).collect::<Vec<_>>())
255+
allocator.allocate_gsi(MAX_GSIS),
256+
Ok((arch::GSI_BASE..=arch::GSI_MAX).collect::<Vec<_>>())
189257
);
190258
// And now we ran out of GSIs
191259
assert_eq!(
@@ -197,16 +265,16 @@ mod tests {
197265

198266
let mut allocator = ResourceAllocator::new();
199267
// We should be able to allocate 1 GSI
200-
assert_eq!(allocator.allocate_gsi(1), Ok(vec![arch::IRQ_BASE]));
268+
assert_eq!(allocator.allocate_gsi(1), Ok(vec![arch::GSI_BASE]));
201269
// We can't allocate MAX_IRQS any more
202270
assert_eq!(
203-
allocator.allocate_gsi(MAX_IRQS),
271+
allocator.allocate_gsi(MAX_GSIS),
204272
Err(vm_allocator::Error::ResourceNotAvailable)
205273
);
206274
// We can allocate another one and it should be the second available
207-
assert_eq!(allocator.allocate_gsi(1), Ok(vec![arch::IRQ_BASE + 1]));
275+
assert_eq!(allocator.allocate_gsi(1), Ok(vec![arch::GSI_BASE + 1]));
208276
// Let's allocate the rest in a loop
209-
for i in arch::IRQ_BASE + 2..=arch::IRQ_MAX {
277+
for i in arch::GSI_BASE + 2..=arch::GSI_MAX {
210278
assert_eq!(allocator.allocate_gsi(1), Ok(vec![i]));
211279
}
212280
}
@@ -221,12 +289,16 @@ mod tests {
221289
#[test]
222290
fn test_save_restore() {
223291
let mut allocator0 = ResourceAllocator::new();
292+
let irq_0 = allocator0.allocate_irq(1).unwrap()[0];
293+
assert_eq!(irq_0, IRQ_BASE);
224294
let gsi_0 = allocator0.allocate_gsi(1).unwrap()[0];
225-
assert_eq!(gsi_0, IRQ_BASE);
295+
assert_eq!(gsi_0, GSI_BASE);
226296

227297
let mut allocator1 = clone_allocator(&allocator0);
298+
let irq_1 = allocator1.allocate_irq(1).unwrap()[0];
299+
assert_eq!(irq_1, IRQ_BASE + 1);
228300
let gsi_1 = allocator1.allocate_gsi(1).unwrap()[0];
229-
assert_eq!(gsi_1, IRQ_BASE + 1);
301+
assert_eq!(gsi_1, GSI_BASE + 1);
230302
let mmio32_mem = allocator1
231303
.allocate_32bit_mmio_memory(0x42, 1, AllocPolicy::FirstMatch)
232304
.unwrap();
@@ -251,8 +323,10 @@ mod tests {
251323
.allocate_system_memory(0x42, 1, AllocPolicy::ExactMatch(system_mem))
252324
.unwrap_err();
253325

326+
let irq_2 = allocator2.allocate_irq(1).unwrap()[0];
327+
assert_eq!(irq_2, IRQ_BASE + 2);
254328
let gsi_2 = allocator2.allocate_gsi(1).unwrap()[0];
255-
assert_eq!(gsi_2, IRQ_BASE + 2);
329+
assert_eq!(gsi_2, GSI_BASE + 2);
256330
let mmio32_mem = allocator1
257331
.allocate_32bit_mmio_memory(0x42, 1, AllocPolicy::FirstMatch)
258332
.unwrap();

src/vmm/src/vstate/vm.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ pub(crate) mod tests {
877877
}
878878

879879
for i in 0..4 {
880-
let gsi = crate::arch::IRQ_BASE + i;
880+
let gsi = crate::arch::GSI_BASE + i;
881881
let interrupts = vm.common.interrupts.lock().unwrap();
882882
let kvm_route = interrupts.get(&gsi).unwrap();
883883
assert!(kvm_route.masked);
@@ -894,7 +894,7 @@ pub(crate) mod tests {
894894
// Simply enabling the vectors should not update the registered IRQ routes
895895
msix_group.enable().unwrap();
896896
for i in 0..4 {
897-
let gsi = crate::arch::IRQ_BASE + i;
897+
let gsi = crate::arch::GSI_BASE + i;
898898
let interrupts = vm.common.interrupts.lock().unwrap();
899899
let kvm_route = interrupts.get(&gsi).unwrap();
900900
assert!(kvm_route.masked);
@@ -914,7 +914,7 @@ pub(crate) mod tests {
914914
.update(0, InterruptSourceConfig::MsiIrq(config), false, true)
915915
.unwrap();
916916
for i in 0..4 {
917-
let gsi = crate::arch::IRQ_BASE + i;
917+
let gsi = crate::arch::GSI_BASE + i;
918918
let interrupts = vm.common.interrupts.lock().unwrap();
919919
let kvm_route = interrupts.get(&gsi).unwrap();
920920
assert_eq!(kvm_route.masked, i != 0);

0 commit comments

Comments
 (0)