Skip to content

Commit d4e6f53

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 <[email protected]>
1 parent 52a9252 commit d4e6f53

File tree

9 files changed

+160
-70
lines changed

9 files changed

+160
-70
lines changed

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::{NR_IRQ, SPI_BASE};
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_BASE) * 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(NR_IRQ);
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/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::{NR_IRQ, SPI_BASE};
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_BASE) * 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(NR_IRQ);
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: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,19 @@ 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 legacy SPI in the GIC
83+
pub const SPI_BASE: u32 = 32;
84+
/// First usable interrupt on aarch64 (corresponds to SPI #32)
85+
pub const IRQ_BASE: u32 = 0;
86+
/// The highest usable SPI on aarch64 is 127 - 32 = 95
87+
pub const IRQ_MAX: u32 = 95;
88+
/// First GSI after legacy interrupts which will be used by MSI
89+
pub const GSI_BASE: u32 = IRQ_MAX + 1;
90+
/// The highest available GSI in KVM (KVM_MAX_IRQ_ROUTES=4096)
91+
pub const GSI_MAX: u32 = 4095;
9292

9393
/// The start of the memory area reserved for MMIO 32-bit accesses.
9494
/// 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use aarch64::{
2727
layout::MEM_64BIT_DEVICES_START, layout::MMIO32_MEM_SIZE, layout::MMIO32_MEM_START,
2828
layout::PCI_MMCONFIG_SIZE, layout::PCI_MMCONFIG_START,
2929
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,
30+
layout::SPI_BASE, layout::SYSTEM_MEM_SIZE, layout::SYSTEM_MEM_START, load_kernel,
3131
};
3232

3333
/// Module for x86_64 related functionality.
@@ -53,6 +53,11 @@ pub use crate::arch::x86_64::{
5353
load_kernel,
5454
};
5555

56+
/// Total count of legacy interrupt lines available (IRQs)
57+
pub const NR_IRQ: u32 = IRQ_MAX - IRQ_BASE + 1;
58+
/// Total count of MSI GSIs available
59+
pub const NR_GSI: u32 = GSI_MAX - GSI_BASE + 1;
60+
5661
/// Types of devices that can get attached to this platform.
5762
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, Serialize, Deserialize)]
5863
pub enum DeviceType {

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ 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).
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.
2527
/// First usable IRQ ID for virtio device interrupts on x86_64.
2628
pub const IRQ_BASE: u32 = 5;
2729
/// Last usable IRQ ID for virtio device interrupts on x86_64.
2830
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;
31+
/// First GSI after legacy interrupts which will be used by MSI
32+
pub const GSI_BASE: u32 = IRQ_MAX + 1;
33+
/// The highest available GSI in KVM (KVM_MAX_IRQ_ROUTES=4096)
34+
pub const GSI_MAX: u32 = 4095;
3535

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

src/vmm/src/device_manager/mmio.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ fn add_virtio_aml(
8484
len: u64,
8585
irq: u32,
8686
) -> Result<(), aml::AmlError> {
87-
let dev_id = irq - crate::arch::GSI_BASE;
87+
let dev_id = irq - crate::arch::IRQ_BASE;
8888
debug!(
8989
"acpi: Building AML for VirtIO device _SB_.V{:03}. memory range: {:#010x}:{} irq: {}",
9090
dev_id, addr, len, irq
@@ -156,7 +156,7 @@ 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 irq = match resource_allocator.allocate_irq(irq_count)?[..] {
160160
[] => None,
161161
[irq] => Some(irq),
162162
_ => return Err(MmioError::InvalidIrqConfig),
@@ -276,11 +276,11 @@ 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 irq = vm.resource_allocator().allocate_irq(1)?;
280280
MMIODeviceInfo {
281281
addr: SERIAL_MEM_START,
282282
len: MMIO_LEN,
283-
irq: Some(gsi[0]),
283+
irq: Some(irq[0]),
284284
}
285285
};
286286

@@ -335,11 +335,11 @@ 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 irq = vm.resource_allocator().allocate_irq(1)?;
339339
MMIODeviceInfo {
340340
addr: RTC_MEM_START,
341341
len: MMIO_LEN,
342-
irq: Some(gsi[0]),
342+
irq: Some(irq[0]),
343343
}
344344
};
345345

@@ -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::IRQ_BASE));
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::IRQ_BASE));
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::IRQ_BASE..=crate::arch::IRQ_MAX {
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::IRQ_BASE,
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::IRQ_BASE);
766766
}
767767

768768
#[test]

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: 76 additions & 6 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,6 +43,7 @@ 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 {
46+
irq_allocator: IdAllocator::new(arch::IRQ_BASE, arch::IRQ_MAX).unwrap(),
4447
gsi_allocator: IdAllocator::new(arch::GSI_BASE, arch::GSI_MAX).unwrap(),
4548
mmio32_memory: AddressAllocator::new(
4649
arch::MEM_32BIT_DEVICES_START,
@@ -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,10 +194,47 @@ mod tests {
167194
use vm_allocator::AllocPolicy;
168195

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

173-
const MAX_IRQS: u32 = arch::GSI_MAX - arch::GSI_BASE + 1;
200+
#[test]
201+
fn test_allocate_irq() {
202+
let mut allocator = ResourceAllocator::new();
203+
// asking for 0 IRQs should return us an empty vector
204+
assert_eq!(allocator.allocate_irq(0), Ok(vec![]));
205+
// We cannot allocate more GSIs than available
206+
assert_eq!(
207+
allocator.allocate_irq(NR_IRQ + 1),
208+
Err(vm_allocator::Error::ResourceNotAvailable)
209+
);
210+
// But allocating all of them at once should work
211+
assert_eq!(
212+
allocator.allocate_irq(NR_IRQ),
213+
Ok((arch::IRQ_BASE..=arch::IRQ_MAX).collect::<Vec<_>>())
214+
);
215+
// And now we ran out of GSIs
216+
assert_eq!(
217+
allocator.allocate_irq(1),
218+
Err(vm_allocator::Error::ResourceNotAvailable)
219+
);
220+
// But we should be able to ask for 0 GSIs
221+
assert_eq!(allocator.allocate_irq(0), Ok(vec![]));
222+
223+
let mut allocator = ResourceAllocator::new();
224+
// We should be able to allocate 1 GSI
225+
assert_eq!(allocator.allocate_irq(1), Ok(vec![arch::IRQ_BASE]));
226+
// We can't allocate MAX_IRQS any more
227+
assert_eq!(
228+
allocator.allocate_irq(NR_IRQ),
229+
Err(vm_allocator::Error::ResourceNotAvailable)
230+
);
231+
// We can allocate another one and it should be the second available
232+
assert_eq!(allocator.allocate_irq(1), Ok(vec![arch::IRQ_BASE + 1]));
233+
// Let's allocate the rest in a loop
234+
for i in arch::IRQ_BASE + 2..=arch::IRQ_MAX {
235+
assert_eq!(allocator.allocate_irq(1), Ok(vec![i]));
236+
}
237+
}
174238

175239
#[test]
176240
fn test_allocate_gsi() {
@@ -179,12 +243,12 @@ mod tests {
179243
assert_eq!(allocator.allocate_gsi(0), Ok(vec![]));
180244
// We cannot allocate more GSIs than available
181245
assert_eq!(
182-
allocator.allocate_gsi(MAX_IRQS + 1),
246+
allocator.allocate_gsi(NR_GSI),
183247
Err(vm_allocator::Error::ResourceNotAvailable)
184248
);
185249
// But allocating all of them at once should work
186250
assert_eq!(
187-
allocator.allocate_gsi(MAX_IRQS),
251+
allocator.allocate_gsi(NR_GSI),
188252
Ok((arch::GSI_BASE..=arch::GSI_MAX).collect::<Vec<_>>())
189253
);
190254
// And now we ran out of GSIs
@@ -200,7 +264,7 @@ mod tests {
200264
assert_eq!(allocator.allocate_gsi(1), Ok(vec![arch::GSI_BASE]));
201265
// We can't allocate MAX_IRQS any more
202266
assert_eq!(
203-
allocator.allocate_gsi(MAX_IRQS),
267+
allocator.allocate_gsi(NR_GSI),
204268
Err(vm_allocator::Error::ResourceNotAvailable)
205269
);
206270
// We can allocate another one and it should be the second available
@@ -221,10 +285,14 @@ mod tests {
221285
#[test]
222286
fn test_save_restore() {
223287
let mut allocator0 = ResourceAllocator::new();
288+
let irq_0 = allocator0.allocate_irq(1).unwrap()[0];
289+
assert_eq!(irq_0, IRQ_BASE);
224290
let gsi_0 = allocator0.allocate_gsi(1).unwrap()[0];
225291
assert_eq!(gsi_0, GSI_BASE);
226292

227293
let mut allocator1 = clone_allocator(&allocator0);
294+
let irq_1 = allocator1.allocate_irq(1).unwrap()[0];
295+
assert_eq!(irq_1, IRQ_BASE + 1);
228296
let gsi_1 = allocator1.allocate_gsi(1).unwrap()[0];
229297
assert_eq!(gsi_1, GSI_BASE + 1);
230298
let mmio32_mem = allocator1
@@ -251,6 +319,8 @@ mod tests {
251319
.allocate_system_memory(0x42, 1, AllocPolicy::ExactMatch(system_mem))
252320
.unwrap_err();
253321

322+
let irq_2 = allocator2.allocate_irq(1).unwrap()[0];
323+
assert_eq!(irq_2, IRQ_BASE + 2);
254324
let gsi_2 = allocator2.allocate_gsi(1).unwrap()[0];
255325
assert_eq!(gsi_2, GSI_BASE + 2);
256326
let mmio32_mem = allocator1

0 commit comments

Comments
 (0)