Skip to content

Commit a5ec189

Browse files
cagatay-ymkroening
andcommitted
feat(pci): MSI-X support
cloud-hypervisor only supports MSI-X interrupts for PCI devices, so support for MSI-X is needed to support running on it. Additionally, MSI-X can allow us to set separate interrupt handlers for the configuration change interrupts and queue updates (even per-queue handlers once we have multiple queues) in the future. MSI-X is not working correctly or at all on all platforms, so it is exposed as an optional feature. Co-authored-by: Martin Kröning <mkroening@posteo.net>
1 parent ab66435 commit a5ec189

7 files changed

Lines changed: 195 additions & 100 deletions

File tree

.github/workflows/ci.yml

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,28 +42,28 @@ jobs:
4242
- run: rustup component add clippy
4343
- name: cargo hack clippy (x86_64)
4444
run: |
45-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net
46-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net --features pci
47-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,rtl8139 --features tcp,virtio-net
45+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net,msix
46+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net,msix --features pci
47+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,rtl8139,msix --features tcp,virtio-net
4848
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,rtl8139 --features pci,tcp,virtio-net
49-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,virtio-net --features tcp,rtl8139
50-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,virtio-net --features pci,tcp,rtl8139
49+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,virtio-net,msix --features tcp,rtl8139
50+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target x86_64-unknown-none --exclude-features gem-net,virtio-net,msix --features pci,tcp,rtl8139
5151
- name: cargo hack clippy (aarch64)
5252
run: |
53-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net
54-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net --features pci
55-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,rtl8139 --features tcp,virtio-net
56-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,rtl8139 --features pci,tcp,virtio-net
57-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,virtio-net --features tcp,rtl8139
58-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,virtio-net --features pci,tcp,rtl8139
53+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net,msix
54+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net,msix --features pci
55+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,rtl8139,msix --features tcp,virtio-net
56+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,rtl8139,msix --features pci,tcp,virtio-net
57+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,virtio-net,msix --features tcp,rtl8139
58+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target aarch64-unknown-none-softfloat --exclude-features gem-net,virtio-net,msix --features pci,tcp,rtl8139
5959
- name: cargo hack clippy (riscv64)
6060
run: |
61-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net
62-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net --features pci
63-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features gem-net,rtl8139 --features tcp,virtio-net
64-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features gem-net,rtl8139 --features pci,tcp,virtio-net
65-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features rtl8139,virtio-net --features tcp,gem-net
66-
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features rtl8139,virtio-net --features pci,tcp,gem-net
61+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net,msix
62+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features dhcpv4,dns,gem-net,net,rtl8139,virtio-net,msix --features pci
63+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features gem-net,rtl8139,msix --features tcp,virtio-net
64+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features gem-net,rtl8139,msix --features pci,tcp,virtio-net
65+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features rtl8139,virtio-net,msix --features tcp,gem-net
66+
cargo hack clippy --package hermit-kernel --each-feature --no-dev-deps --target riscv64gc-unknown-none-elf --exclude-features rtl8139,virtio-net,msix --features pci,tcp,gem-net
6767
6868
format:
6969
name: Format

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ pci = ["virtio?/pci"]
118118
## [PCI ID]: https://pci-ids.ucw.cz/
119119
pci-ids = ["dep:pci-ids"]
120120

121+
## Causes the drivers to use _MSI-X (Message Signaled Interrupt)_ when
122+
## the PCI device and the driver (currently only virtio-net) supports it.
123+
## It is only supported on x86_64 and is known to not work with all virtual
124+
## devices.
125+
msix = ["volatile/derive"]
126+
121127
## Enables semihosting support.
122128
##
123129
## Semihosting allows communicating with the host for

src/drivers/net/virtio/mmio.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl VirtioNetDriver<Uninit> {
3939
notif_cfg,
4040
inner: Uninit,
4141
num_vqs: 0,
42-
irq,
42+
irq: Some(irq),
4343
checksums: ChecksumCapabilities::default(),
4444
})
4545
}

src/drivers/net/virtio/mod.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ use self::error::VirtioNetError;
3333
use crate::config::VIRTIO_MAX_QUEUE_SIZE;
3434
use crate::drivers::net::virtio::constants::BUFF_PER_PACKET;
3535
use crate::drivers::net::{NetworkDriver, mtu};
36+
#[cfg(feature = "msix")]
37+
use crate::drivers::pci::MsixTableEntry;
3638
use crate::drivers::virtio::ControlRegisters;
3739
#[cfg(not(feature = "pci"))]
3840
use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
@@ -241,11 +243,13 @@ pub(crate) struct VirtioNetDriver<T = Init> {
241243
pub(super) com_cfg: ComCfg,
242244
pub(super) isr_stat: IsrStatus,
243245
pub(super) notif_cfg: NotifCfg,
246+
#[cfg(feature = "msix")]
247+
pub(super) msix_table: Option<VolatileRef<'static, [MsixTableEntry]>>,
244248

245249
pub(super) inner: T,
246250

247251
pub(super) num_vqs: u16,
248-
pub(super) irq: InterruptLine,
252+
pub(super) irq: Option<InterruptLine>,
249253
pub(super) checksums: ChecksumCapabilities,
250254
}
251255

@@ -485,7 +489,7 @@ impl smoltcp::phy::Device for VirtioNetDriver {
485489

486490
impl Driver for VirtioNetDriver<Init> {
487491
fn get_interrupt_number(&self) -> InterruptLine {
488-
self.irq
492+
self.irq.unwrap()
489493
}
490494

491495
fn get_name(&self) -> &'static str {
@@ -741,11 +745,34 @@ impl VirtioNetDriver<Uninit> {
741745
}
742746
debug!("{:?}", self.checksums);
743747

748+
#[cfg(feature = "msix")]
749+
if let Some(msix_table) = self.msix_table.as_mut() {
750+
warn!(
751+
"Setting up message signaled interrupts. MSI-X is known to not work on all platforms (e.g. QEMU with TAP)."
752+
);
753+
// Chosen arbitatrily. Ideally does not clash with another interrupt line.
754+
const MSIX_VECTOR: u8 = 112;
755+
let msix_entry = unsafe {
756+
msix_table
757+
.as_mut_ptr()
758+
.map(|table| table.get_unchecked_mut(0))
759+
};
760+
MsixTableEntry::configure(msix_entry, MSIX_VECTOR);
761+
self.com_cfg.select_vq(0).unwrap().set_msix_table_index(0);
762+
self.irq = Some(MSIX_VECTOR);
763+
};
764+
765+
if self.irq.is_none() {
766+
warn!("No interrupt methods found for virtio-net.");
767+
}
768+
744769
Ok(VirtioNetDriver {
745770
dev_cfg: self.dev_cfg,
746771
com_cfg: self.com_cfg,
747772
isr_stat: self.isr_stat,
748773
notif_cfg: self.notif_cfg,
774+
#[cfg(feature = "msix")]
775+
msix_table: self.msix_table,
749776
inner,
750777
num_vqs: self.num_vqs,
751778
irq: self.irq,

src/drivers/net/virtio/pci.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ impl VirtioNetDriver<Uninit> {
3636
notif_cfg,
3737
isr_cfg,
3838
dev_cfg_list,
39+
#[cfg(feature = "msix")]
40+
msix_table,
3941
..
4042
} = caps_coll;
4143

@@ -44,14 +46,18 @@ impl VirtioNetDriver<Uninit> {
4446
return Err(error::VirtioNetError::NoDevCfg(device_id));
4547
};
4648

49+
let irq = device.get_irq();
50+
4751
Ok(VirtioNetDriver {
4852
dev_cfg,
4953
com_cfg,
5054
isr_stat: isr_cfg,
5155
notif_cfg,
56+
#[cfg(feature = "msix")]
57+
msix_table,
5258
inner: Uninit,
5359
num_vqs: 0,
54-
irq: device.get_irq().unwrap(),
60+
irq,
5561
checksums: ChecksumCapabilities::default(),
5662
})
5763
}

src/drivers/pci.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,49 @@ pub(crate) fn init() {
537537
});
538538
}
539539

540+
/// MSI-X Table entry.
541+
#[cfg(feature = "msix")]
542+
#[repr(C)]
543+
#[derive(volatile::VolatileFieldAccess)]
544+
pub(crate) struct MsixTableEntry {
545+
/// Message Address
546+
message_address: u32,
547+
548+
/// Message Upper Address
549+
message_upper_address: u32,
550+
551+
/// Message Data
552+
message_data: u32,
553+
554+
/// Vector Control
555+
vector_control: u32,
556+
}
557+
558+
#[cfg(feature = "msix")]
559+
impl MsixTableEntry {
560+
#[cfg(target_arch = "x86_64")]
561+
pub fn configure(msix_entry: volatile::VolatilePtr<'_, Self>, vector: u8) {
562+
use MsixTableEntryVolatileFieldAccess;
563+
use bit_field::BitField;
564+
565+
// Mask the entry because "[s]oftware must not modify the Address, Data, or Steering Tag fields
566+
// of an entry while it is unmasked." (PCIe spec. 6.1.4.2)
567+
msix_entry
568+
.vector_control()
569+
.update(|mut control| *control.set_bit(0, true));
570+
571+
msix_entry
572+
.message_address()
573+
.update(|mut addr_low| *addr_low.set_bits(20..32, 0xfee));
574+
msix_entry
575+
.message_data()
576+
.update(|mut data| *data.set_bits(0..8, u32::from(vector) + 32));
577+
msix_entry
578+
.vector_control()
579+
.update(|mut control| *control.set_bit(0, false));
580+
}
581+
}
582+
540583
/// A module containing PCI specific errors
541584
///
542585
/// Errors include...

0 commit comments

Comments
 (0)