Skip to content

Commit cf4d4cb

Browse files
Manciukicbchalios
authored andcommitted
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 GSI", and create a new range for "MSI GSI" that goes up to the kvm theoretical maximum of 4096 lines. Signed-off-by: Riccardo Mancini <[email protected]> Signed-off-by: Babis Chalios <[email protected]>
1 parent 441a900 commit cf4d4cb

File tree

14 files changed

+232
-132
lines changed

14 files changed

+232
-132
lines changed

src/vmm/src/arch/aarch64/gic/gicv2/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ impl GICv2 {
135135
// On arm there are 3 types of interrupts: SGI (0-15), PPI (16-31), SPI (32-1020).
136136
// SPIs are used to signal interrupts from various peripherals accessible across
137137
// the whole system so these are the ones that we increment when adding a new virtio device.
138-
// KVM_DEV_ARM_VGIC_GRP_NR_IRQS sets the highest SPI number. Consequently, we will have a
139-
// total of `super::layout::IRQ_MAX - 32` usable SPIs in our microVM.
140-
let nr_irqs: u32 = super::layout::IRQ_MAX;
138+
// KVM_DEV_ARM_VGIC_GRP_NR_IRQS sets the number of interrupts (SGI, PPI, and SPI).
139+
// Consequently, we need to add 32 to the number of SPIs ("legacy GSI").
140+
let nr_irqs: u32 = crate::arch::GSI_LEGACY_NUM + super::layout::SPI_START;
141141
let nr_irqs_ptr = &nr_irqs as *const u32;
142142
Self::set_device_attribute(
143143
gic_device.device_fd(),

src/vmm/src/arch/aarch64/gic/gicv2/regs/dist_regs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use kvm_ioctls::DeviceFd;
88

99
use crate::arch::aarch64::gic::GicError;
1010
use crate::arch::aarch64::gic::regs::{GicRegState, MmioReg, SimpleReg, VgicRegEngine};
11-
use crate::arch::{IRQ_BASE, IRQ_MAX};
11+
use crate::arch::{GSI_LEGACY_NUM, SPI_START};
1212

1313
// Distributor registers as detailed at page 75 from
1414
// https://developer.arm.com/documentation/ihi0048/latest/.
@@ -62,9 +62,9 @@ impl MmioReg for SharedIrqReg {
6262
// read-as-zero/write-ignore (RAZ/WI) policy.
6363
// The first part of a shared-irq register, the one corresponding to the
6464
// SGI and PPI IRQs (0-32) is RAZ/WI, so we skip it.
65-
let start = self.offset + u64::from(IRQ_BASE) * u64::from(self.bits_per_irq) / 8;
65+
let start = self.offset + u64::from(SPI_START) * u64::from(self.bits_per_irq) / 8;
6666

67-
let size_in_bits = u64::from(self.bits_per_irq) * u64::from(IRQ_MAX - IRQ_BASE);
67+
let size_in_bits = u64::from(self.bits_per_irq) * u64::from(GSI_LEGACY_NUM);
6868
let mut size_in_bytes = size_in_bits / 8;
6969
if size_in_bits % 8 > 0 {
7070
size_in_bytes += 1;

src/vmm/src/arch/aarch64/gic/gicv3/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,9 @@ impl GICv3 {
184184
// On arm there are 3 types of interrupts: SGI (0-15), PPI (16-31), SPI (32-1020).
185185
// SPIs are used to signal interrupts from various peripherals accessible across
186186
// the whole system so these are the ones that we increment when adding a new virtio device.
187-
// KVM_DEV_ARM_VGIC_GRP_NR_IRQS sets the highest SPI number. Consequently, we will have a
188-
// total of `super::layout::IRQ_MAX - 32` usable SPIs in our microVM.
189-
let nr_irqs: u32 = super::layout::IRQ_MAX;
187+
// KVM_DEV_ARM_VGIC_GRP_NR_IRQS sets the number of interrupts (SGI, PPI, and SPI).
188+
// Consequently, we need to add 32 to the number of SPIs ("legacy GSI").
189+
let nr_irqs: u32 = crate::arch::GSI_LEGACY_NUM + super::layout::SPI_START;
190190
let nr_irqs_ptr = &nr_irqs as *const u32;
191191
Self::set_device_attribute(
192192
gic_device.device_fd(),

src/vmm/src/arch/aarch64/gic/gicv3/regs/dist_regs.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use kvm_ioctls::DeviceFd;
88

99
use crate::arch::aarch64::gic::GicError;
1010
use crate::arch::aarch64::gic::regs::{GicRegState, MmioReg, SimpleReg, VgicRegEngine};
11-
use crate::arch::{IRQ_BASE, IRQ_MAX};
11+
use crate::arch::{GSI_LEGACY_NUM, SPI_START};
1212

1313
// Distributor registers as detailed at page 456 from
1414
// https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_architecture_specification.pdf.
@@ -64,9 +64,9 @@ impl MmioReg for SharedIrqReg {
6464
// read-as-zero/write-ignore (RAZ/WI) policy.
6565
// The first part of a shared-irq register, the one corresponding to the
6666
// SGI and PPI IRQs (0-32) is RAZ/WI, so we skip it.
67-
let start = self.offset + u64::from(IRQ_BASE) * u64::from(self.bits_per_irq) / 8;
67+
let start = self.offset + u64::from(SPI_START) * u64::from(self.bits_per_irq) / 8;
6868

69-
let size_in_bits = u64::from(self.bits_per_irq) * u64::from(IRQ_MAX - IRQ_BASE);
69+
let size_in_bits = u64::from(self.bits_per_irq) * u64::from(GSI_LEGACY_NUM);
7070
let mut size_in_bytes = size_in_bits / 8;
7171
if size_in_bits % 8 > 0 {
7272
size_in_bytes += 1;

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,25 @@ pub const FDT_MAX_SIZE: usize = 0x20_0000;
7676
// * bigger than 32
7777
// * less than 1023 and
7878
// * a multiple of 32.
79-
/// The highest usable SPI on aarch64.
80-
pub const IRQ_MAX: u32 = 128;
81-
82-
/// First usable interrupt on aarch64.
83-
pub const IRQ_BASE: u32 = 32;
84-
85-
// The Linux kernel automatically shifts the GSI by 32 if it is an SPI,
86-
// allowing us to start numbering from 0 instead of 32.
87-
/// The first usable GSI on aarch64.
88-
pub const GSI_BASE: u32 = 0;
89-
90-
/// The maximum usable GSI on aarch64.
91-
pub const GSI_MAX: u32 = IRQ_MAX - IRQ_BASE - 1;
79+
// The first 32 SPIs are reserved, but KVM already shifts the gsi we
80+
// pass, so we go from 0 to 95 for legacy gsis ("irq") and the remaining
81+
// we use for MSI.
82+
/// Offset of first SPI in the GIC
83+
pub const SPI_START: u32 = 32;
84+
/// Last possible SPI in the GIC (128 total SPIs)
85+
pub const SPI_END: u32 = 127;
86+
/// First usable GSI id on aarch64 (corresponds to SPI #32).
87+
pub const GSI_LEGACY_START: u32 = 0;
88+
/// There are 128 SPIs available, but the first 32 are reserved
89+
pub const GSI_LEGACY_NUM: u32 = SPI_END - SPI_START + 1;
90+
/// Last available GSI
91+
pub const GSI_LEGACY_END: u32 = GSI_LEGACY_START + GSI_LEGACY_NUM - 1;
92+
/// First GSI used by MSI after legacy GSI
93+
pub const GSI_MSI_START: u32 = GSI_LEGACY_END + 1;
94+
/// The highest available GSI in KVM (KVM_MAX_IRQ_ROUTES=4096)
95+
pub const GSI_MSI_END: u32 = 4095;
96+
/// Number of GSI available for MSI.
97+
pub const GSI_MSI_NUM: u32 = GSI_MSI_END - GSI_MSI_START + 1;
9298

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

src/vmm/src/arch/mod.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@ 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::GSI_BASE,
25-
layout::GSI_MAX, layout::IRQ_BASE, layout::IRQ_MAX, layout::MEM_32BIT_DEVICES_SIZE,
24+
initrd_load_addr, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE,
25+
layout::GSI_LEGACY_END, layout::GSI_LEGACY_NUM, layout::GSI_LEGACY_START, layout::GSI_MSI_END,
26+
layout::GSI_MSI_NUM, layout::GSI_MSI_START, layout::MEM_32BIT_DEVICES_SIZE,
2627
layout::MEM_32BIT_DEVICES_START, layout::MEM_64BIT_DEVICES_SIZE,
2728
layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE, layout::MMIO32_MEM_START,
2829
layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
2930
layout::PCI_MMIO_CONFIG_SIZE_PER_SEGMENT, layout::RTC_MEM_START, layout::SERIAL_MEM_START,
30-
layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
31+
layout::SPI_START, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
3132
};
3233

3334
/// Module for x86_64 related functionality.
@@ -45,7 +46,8 @@ pub use x86_64::vm::{ArchVm, ArchVmError, VmState};
4546
pub use crate::arch::x86_64::{
4647
ConfigurationError, arch_memory_regions, configure_system_for_boot, get_kernel_start,
4748
initrd_load_addr, layout::APIC_ADDR, layout::BOOT_DEVICE_MEM_START, layout::CMDLINE_MAX_SIZE,
48-
layout::GSI_BASE, layout::GSI_MAX, layout::IOAPIC_ADDR, layout::IRQ_BASE, layout::IRQ_MAX,
49+
layout::GSI_LEGACY_END, layout::GSI_LEGACY_NUM, layout::GSI_LEGACY_START, layout::GSI_MSI_END,
50+
layout::GSI_MSI_NUM, layout::GSI_MSI_START, layout::IOAPIC_ADDR,
4951
layout::MEM_32BIT_DEVICES_SIZE, layout::MEM_32BIT_DEVICES_START,
5052
layout::MEM_64BIT_DEVICES_SIZE, layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE,
5153
layout::MMIO32_MEM_START, layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,

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

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,21 @@ pub const CMDLINE_MAX_SIZE: usize = 2048;
2121
/// Start of the high memory.
2222
pub const HIMEM_START: u64 = 0x0010_0000; // 1 MB.
2323

24-
// Typically, on x86 systems 24 IRQs are used (0-23).
25-
/// First usable IRQ ID for virtio device interrupts on x86_64.
26-
pub const IRQ_BASE: u32 = 5;
27-
/// Last usable IRQ ID for virtio device interrupts on x86_64.
28-
pub const IRQ_MAX: u32 = 23;
29-
30-
/// The first usable GSI on x86_64 is the same as the first usable IRQ ID.
31-
pub const GSI_BASE: u32 = IRQ_BASE;
32-
33-
/// The maximum usable GSI on x86_64 is the same as the last usable IRQ ID.
34-
pub const GSI_MAX: u32 = IRQ_MAX;
24+
// Typically, on x86 systems 24 IRQs are used for legacy devices (0-23).
25+
// However, the first 5 are reserved.
26+
// We allocate the remaining GSIs to MSIs.
27+
/// First usable GSI for legacy interrupts (IRQ) on x86_64.
28+
pub const GSI_LEGACY_START: u32 = 5;
29+
/// Last usable GSI for legacy interrupts (IRQ) on x86_64.
30+
pub const GSI_LEGACY_END: u32 = 23;
31+
/// Number of legacy GSI (IRQ) available on x86_64.
32+
pub const GSI_LEGACY_NUM: u32 = GSI_LEGACY_END - GSI_LEGACY_START + 1;
33+
/// First GSI used by MSI after legacy GSI.
34+
pub const GSI_MSI_START: u32 = GSI_LEGACY_END + 1;
35+
/// The highest available GSI in KVM (KVM_MAX_IRQ_ROUTES=4096).
36+
pub const GSI_MSI_END: u32 = 4095;
37+
/// Number of GSI available for MSI.
38+
pub const GSI_MSI_NUM: u32 = GSI_MSI_END - GSI_MSI_START + 1;
3539

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

src/vmm/src/arch/x86_64/mptable.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use libc::c_char;
1313
use log::debug;
1414
use vm_allocator::AllocPolicy;
1515

16-
use crate::arch::IRQ_MAX;
16+
use crate::arch::GSI_LEGACY_END;
1717
use crate::arch::x86_64::generated::mpspec;
1818
use crate::vstate::memory::{
1919
Address, ByteValued, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap,
@@ -109,7 +109,7 @@ fn compute_mp_size(num_cpus: u8) -> usize {
109109
+ mem::size_of::<mpspec::mpc_cpu>() * (num_cpus as usize)
110110
+ mem::size_of::<mpspec::mpc_ioapic>()
111111
+ mem::size_of::<mpspec::mpc_bus>()
112-
+ mem::size_of::<mpspec::mpc_intsrc>() * (IRQ_MAX as usize + 1)
112+
+ mem::size_of::<mpspec::mpc_intsrc>() * (GSI_LEGACY_END as usize + 1)
113113
+ mem::size_of::<mpspec::mpc_lintsrc>() * 2
114114
}
115115

@@ -225,7 +225,7 @@ pub fn setup_mptable(
225225
mp_num_entries += 1;
226226
}
227227
// Per kvm_setup_default_irq_routing() in kernel
228-
for i in 0..=u8::try_from(IRQ_MAX).map_err(|_| MptableError::TooManyIrqs)? {
228+
for i in 0..=u8::try_from(GSI_LEGACY_END).map_err(|_| MptableError::TooManyIrqs)? {
229229
let size = mem::size_of::<mpspec::mpc_intsrc>() as u64;
230230
let mpc_intsrc = mpspec::mpc_intsrc {
231231
type_: mpspec::MP_INTSRC.try_into().unwrap(),
@@ -406,7 +406,7 @@ mod tests {
406406
// ISA Bus
407407
+ 1
408408
// IRQ
409-
+ u16::try_from(IRQ_MAX).unwrap() + 1
409+
+ u16::try_from(GSI_LEGACY_END).unwrap() + 1
410410
// Interrupt source ExtINT
411411
+ 1
412412
// Interrupt source NMI

src/vmm/src/device_manager/acpi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl Aml for ACPIDeviceManager {
6464
// We know that the maximum IRQ number fits in a u8. We have up to
6565
// 32 IRQs in x86 and up to 128 in
6666
// ARM (look into
67-
// `vmm::crate::arch::layout::IRQ_MAX`)
67+
// `vmm::crate::arch::layout::GSI_LEGACY_END`)
6868
#[allow(clippy::cast_possible_truncation)]
6969
&aml::Equal::new(&aml::Arg(0), &(vmgenid.gsi as u8)),
7070
vec![&aml::Notify::new(

src/vmm/src/device_manager/mmio.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,12 @@ fn add_virtio_aml(
8282
dsdt_data: &mut Vec<u8>,
8383
addr: u64,
8484
len: u64,
85-
irq: u32,
85+
gsi: u32,
8686
) -> Result<(), aml::AmlError> {
87-
let dev_id = irq - crate::arch::GSI_BASE;
87+
let dev_id = gsi - crate::arch::GSI_LEGACY_START;
8888
debug!(
89-
"acpi: Building AML for VirtIO device _SB_.V{:03}. memory range: {:#010x}:{} irq: {}",
90-
dev_id, addr, len, irq
89+
"acpi: Building AML for VirtIO device _SB_.V{:03}. memory range: {:#010x}:{} gsi: {}",
90+
dev_id, addr, len, gsi
9191
);
9292
aml::Device::new(
9393
format!("V{:03}", dev_id).as_str().try_into()?,
@@ -103,7 +103,7 @@ fn add_virtio_aml(
103103
addr.try_into().unwrap(),
104104
len.try_into().unwrap(),
105105
),
106-
&aml::Interrupt::new(true, true, false, false, irq),
106+
&aml::Interrupt::new(true, true, false, false, gsi),
107107
]),
108108
)?,
109109
],
@@ -156,9 +156,9 @@ impl MMIODeviceManager {
156156
resource_allocator: &mut ResourceAllocator,
157157
irq_count: u32,
158158
) -> Result<MMIODeviceInfo, MmioError> {
159-
let irq = match resource_allocator.allocate_gsi(irq_count)?[..] {
159+
let gsi = match resource_allocator.allocate_gsi_legacy(irq_count)?[..] {
160160
[] => None,
161-
[irq] => Some(irq),
161+
[gsi] => Some(gsi),
162162
_ => return Err(MmioError::InvalidIrqConfig),
163163
};
164164

@@ -169,7 +169,7 @@ impl MMIODeviceManager {
169169
AllocPolicy::FirstMatch,
170170
)?,
171171
len: MMIO_LEN,
172-
irq,
172+
irq: gsi,
173173
};
174174
Ok(device_info)
175175
}
@@ -183,7 +183,7 @@ impl MMIODeviceManager {
183183
) -> Result<(), MmioError> {
184184
// Our virtio devices are currently hardcoded to use a single IRQ.
185185
// Validate that requirement.
186-
let irq = device.resources.irq.ok_or(MmioError::InvalidIrqConfig)?;
186+
let gsi = device.resources.irq.ok_or(MmioError::InvalidIrqConfig)?;
187187
let identifier;
188188
{
189189
let mmio_device = device.inner.lock().expect("Poisoned lock");
@@ -197,7 +197,7 @@ impl MMIODeviceManager {
197197
.register_ioevent(queue_evt, &io_addr, u32::try_from(i).unwrap())
198198
.map_err(MmioError::RegisterIoEvent)?;
199199
}
200-
vm.register_irq(&mmio_device.interrupt.irq_evt, irq)
200+
vm.register_irq(&mmio_device.interrupt.irq_evt, gsi)
201201
.map_err(MmioError::RegisterIrqFd)?;
202202
}
203203

@@ -276,7 +276,7 @@ impl MMIODeviceManager {
276276
let device_info = if let Some(device_info) = device_info_opt {
277277
device_info
278278
} else {
279-
let gsi = vm.resource_allocator().allocate_gsi(1)?;
279+
let gsi = vm.resource_allocator().allocate_gsi_legacy(1)?;
280280
MMIODeviceInfo {
281281
addr: SERIAL_MEM_START,
282282
len: MMIO_LEN,
@@ -335,7 +335,7 @@ impl MMIODeviceManager {
335335
let device_info = if let Some(device_info) = device_info_opt {
336336
device_info
337337
} else {
338-
let gsi = vm.resource_allocator().allocate_gsi(1)?;
338+
let gsi = vm.resource_allocator().allocate_gsi_legacy(1)?;
339339
MMIODeviceInfo {
340340
addr: RTC_MEM_START,
341341
len: MMIO_LEN,
@@ -612,15 +612,15 @@ pub(crate) mod tests {
612612
let dev = device_manager.get_virtio_device(0, "dummy").unwrap();
613613
assert_eq!(dev.resources.addr, arch::MEM_32BIT_DEVICES_START);
614614
assert_eq!(dev.resources.len, MMIO_LEN);
615-
assert_eq!(dev.resources.irq, Some(arch::GSI_BASE));
615+
assert_eq!(dev.resources.irq, Some(arch::GSI_LEGACY_START));
616616

617617
device_manager
618618
.for_each_virtio_device(|virtio_type, device_id, mmio_device| {
619619
assert_eq!(*virtio_type, 0);
620620
assert_eq!(device_id, "dummy");
621621
assert_eq!(mmio_device.resources.addr, arch::MEM_32BIT_DEVICES_START);
622622
assert_eq!(mmio_device.resources.len, MMIO_LEN);
623-
assert_eq!(mmio_device.resources.irq, Some(arch::GSI_BASE));
623+
assert_eq!(mmio_device.resources.irq, Some(arch::GSI_LEGACY_START));
624624
Ok::<(), ()>(())
625625
})
626626
.unwrap();
@@ -643,7 +643,7 @@ pub(crate) mod tests {
643643
#[cfg(target_arch = "aarch64")]
644644
vm.setup_irqchip(1).unwrap();
645645

646-
for _i in crate::arch::GSI_BASE..=crate::arch::GSI_MAX {
646+
for _i in crate::arch::GSI_LEGACY_START..=crate::arch::GSI_LEGACY_END {
647647
device_manager
648648
.register_virtio_test_device(
649649
&vm,
@@ -711,7 +711,7 @@ pub(crate) mod tests {
711711
.addr
712712
);
713713
assert_eq!(
714-
crate::arch::GSI_BASE,
714+
crate::arch::GSI_LEGACY_START,
715715
device_manager.virtio_devices[&(type_id, id)]
716716
.resources
717717
.irq
@@ -762,7 +762,7 @@ pub(crate) mod tests {
762762
let device_info = device_manager
763763
.allocate_mmio_resources(&mut resource_allocator, 1)
764764
.unwrap();
765-
assert_eq!(device_info.irq.unwrap(), crate::arch::GSI_BASE);
765+
assert_eq!(device_info.irq.unwrap(), crate::arch::GSI_LEGACY_START);
766766
}
767767

768768
#[test]

0 commit comments

Comments
 (0)