Skip to content

Commit 56acd9b

Browse files
authored
perf(virtiofs): isolate virtio PCI interrupt vectors (#2129)
* perf(virtiofs): isolate virtio PCI interrupt vectors Allocate monotonic x86 PCI MSI/MSI-X vectors for interrupt-driven VirtIO transports instead of routing every device through vector 56. Rebind allocated descriptors to the local APIC edge flow, skip reserved vectors, and keep allocation fail-safe until a complete free_irq lifecycle exists. Defer vector allocation until IRQ setup so polling-only transports remain usable on architectures without PCI MSI support. Centralize PCI and MMIO IRQ registration in the transport layer, remove duplicate sysfs registration, and avoid descriptor lock recursion in the shared-action mismatch path. Keep PCI ISR acknowledgement owned by the hard IRQ path while retaining bridge-side acknowledgement for polling fallback. Add a deterministic parallel-read workload with immutable per-worker files, bounded low-impact start coordination, aligned wall/CPU measurement boundaries, exact EOF and checksum validation, and transcript regression coverage. Validated with make fmt, make kernel, host transcript tests, and a fresh-boot non-DAX VirtioFS mount/read smoke test across VirtIO filesystem, block, network, and console devices. Signed-off-by: longjin <longjin@dragonos.org> * fix(virtio): defer MMIO IRQ registration Return an explicit deferred IRQ token for MMIO transports so the global IRQ action is installed only after the concrete virtio device has completed construction. Keep the existing PCI setup path unchanged. Preserve per-device failure semantics: block, net, and console devices abort registration without panicking; pmem falls back to polling. Clean up the block worker, queues, transport, and allocated device ID when deferred IRQ installation fails. Retain virtio-net dispatch registration at the end of probe so interrupts are routed only after the network interface is ready. Signed-off-by: longjin <longjin@dragonos.org> --------- Signed-off-by: longjin <longjin@dragonos.org>
1 parent 4816406 commit 56acd9b

14 files changed

Lines changed: 793 additions & 102 deletions

File tree

kernel/src/arch/loongarch64/msi.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
use crate::driver::pci::pci_irq::TriggerMode;
2+
use crate::exception::IrqNumber;
3+
use system_error::SystemError;
4+
5+
pub fn arch_pci_msi_vector_alloc() -> Option<IrqNumber> {
6+
None
7+
}
8+
9+
pub fn arch_pci_msi_vector_setup(_vector: IrqNumber) -> Result<(), SystemError> {
10+
Err(SystemError::ENOSYS)
11+
}
212

313
/// 获得MSI Message Address
414
///

kernel/src/arch/riscv64/msi.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
use crate::driver::pci::pci_irq::TriggerMode;
2+
use crate::exception::IrqNumber;
3+
use system_error::SystemError;
4+
5+
pub fn arch_pci_msi_vector_alloc() -> Option<IrqNumber> {
6+
None
7+
}
8+
9+
pub fn arch_pci_msi_vector_setup(_vector: IrqNumber) -> Result<(), SystemError> {
10+
Err(SystemError::ENOSYS)
11+
}
212

313
/// @brief 获得MSI Message Address
414
/// @param processor 目标CPU ID号

kernel/src/arch/x86_64/msi.rs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,81 @@
1-
use crate::driver::pci::pci_irq::TriggerMode;
1+
use core::sync::atomic::{AtomicU32, Ordering};
2+
3+
use system_error::SystemError;
4+
5+
use crate::{
6+
arch::driver::apic::lapic_vector::local_apic_chip,
7+
driver::pci::pci_irq::TriggerMode,
8+
exception::{
9+
handle::edge_irq_handler, irqdata::IrqLineStatus, irqdesc::irq_desc_manager, IrqNumber,
10+
},
11+
};
12+
13+
const PCI_MSI_VECTOR_FIRST: u32 = 64;
14+
const PCI_MSI_VECTOR_INT80: u32 = 128;
15+
const PCI_MSI_VECTOR_LAST: u32 = 150;
16+
17+
struct PciMsiVectorAllocator {
18+
next: AtomicU32,
19+
}
20+
21+
impl PciMsiVectorAllocator {
22+
const fn new() -> Self {
23+
Self {
24+
next: AtomicU32::new(PCI_MSI_VECTOR_FIRST),
25+
}
26+
}
27+
28+
fn alloc(&self) -> Option<IrqNumber> {
29+
loop {
30+
let current = self.next.load(Ordering::Relaxed);
31+
if current > PCI_MSI_VECTOR_LAST {
32+
return None;
33+
}
34+
let next = if current == PCI_MSI_VECTOR_INT80 - 1 {
35+
PCI_MSI_VECTOR_INT80 + 1
36+
} else {
37+
current + 1
38+
};
39+
if self
40+
.next
41+
.compare_exchange_weak(current, next, Ordering::AcqRel, Ordering::Relaxed)
42+
.is_ok()
43+
{
44+
return Some(IrqNumber::new(current));
45+
}
46+
}
47+
}
48+
}
49+
50+
static PCI_MSI_VECTOR_ALLOCATOR: PciMsiVectorAllocator = PciMsiVectorAllocator::new();
51+
52+
/// Reserves a CPU vector from the x86 range owned by PCI MSI/MSI-X.
53+
///
54+
/// Vectors are intentionally not reused. DragonOS does not yet have a safe PCI `free_irq`
55+
/// lifecycle, so reuse could route a live MSI action to a different device.
56+
pub fn arch_pci_msi_vector_alloc() -> Option<IrqNumber> {
57+
PCI_MSI_VECTOR_ALLOCATOR.alloc()
58+
}
59+
60+
/// Rebinds an allocated vector from the blanket IOAPIC setup to message-interrupt semantics.
61+
pub fn arch_pci_msi_vector_setup(vector: IrqNumber) -> Result<(), SystemError> {
62+
if !(PCI_MSI_VECTOR_FIRST..=PCI_MSI_VECTOR_LAST).contains(&vector.data())
63+
|| vector.data() == PCI_MSI_VECTOR_INT80
64+
{
65+
return Err(SystemError::EINVAL);
66+
}
67+
let desc = irq_desc_manager()
68+
.lookup(vector)
69+
.ok_or(SystemError::EINVAL)?;
70+
let irq_data = desc.irq_data();
71+
let mut chip_info = irq_data.chip_info_write_irqsave();
72+
chip_info.set_chip(Some(local_apic_chip().clone()));
73+
chip_info.set_chip_data(None);
74+
drop(chip_info);
75+
desc.modify_status(IrqLineStatus::IRQ_LEVEL, IrqLineStatus::empty());
76+
desc.set_handler(edge_irq_handler());
77+
Ok(())
78+
}
279
/// @brief 获得MSI Message Address
380
/// @param processor 目标CPU ID号
481
/// @return MSI Message Address
@@ -17,3 +94,25 @@ pub fn arch_msi_message_data(vector: u16, _processor: u16, trigger: TriggerMode)
1794
TriggerMode::AssertLow => vector as u32 | 1 << 15,
1895
}
1996
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use super::{PciMsiVectorAllocator, PCI_MSI_VECTOR_LAST};
101+
102+
#[test]
103+
fn pci_msi_vector_allocator_skips_int80_and_exhausts() {
104+
let allocator = PciMsiVectorAllocator::new();
105+
let mut vectors = alloc::vec::Vec::new();
106+
while let Some(vector) = allocator.alloc() {
107+
vectors.push(vector.data());
108+
}
109+
110+
assert_eq!(vectors.first().copied(), Some(64));
111+
assert!(vectors.contains(&127));
112+
assert!(!vectors.contains(&128));
113+
assert!(vectors.contains(&129));
114+
assert_eq!(vectors.last().copied(), Some(PCI_MSI_VECTOR_LAST));
115+
assert_eq!(vectors.len(), 86);
116+
assert_eq!(allocator.alloc(), None);
117+
}
118+
}

kernel/src/driver/block/virtio_blk.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use crate::{
4242
},
4343
virtio::{
4444
sysfs::{virtio_bus, virtio_device_manager, virtio_driver_manager},
45-
transport::VirtIOTransport,
45+
transport::{DeferredVirtioIrq, VirtIOTransport},
4646
virtio_impl::HalImpl,
4747
VirtIODevice, VirtIODeviceIndex, VirtIODriver, VirtIODriverCommonData, VirtioDeviceId,
4848
VIRTIO_VENDOR_ID,
@@ -306,10 +306,21 @@ pub fn virtio_blk(
306306
dev_parent: Option<Arc<dyn Device>>,
307307
) {
308308
let device = VirtIOBlkDevice::new(transport, dev_id);
309-
if let Some(device) = device {
309+
if let Some((device, deferred_irq)) = device {
310310
if let Some(dev_parent) = dev_parent {
311311
device.set_dev_parent(Some(Arc::downgrade(&dev_parent)));
312312
}
313+
if let Some(deferred_irq) = deferred_irq {
314+
if let Err(err) = deferred_irq.install(device.dev_id().clone()) {
315+
error!(
316+
"VirtIOBlkDevice '{:?}' setup_irq failed: {:?}",
317+
device.dev_id(),
318+
err
319+
);
320+
device.abort_initialization();
321+
return;
322+
}
323+
}
313324
virtio_device_manager()
314325
.device_add(device.clone() as Arc<dyn VirtIODevice>)
315326
.expect("Add virtio blk failed");
@@ -408,12 +419,18 @@ unsafe impl Send for VirtIOBlkDevice {}
408419
unsafe impl Sync for VirtIOBlkDevice {}
409420

410421
impl VirtIOBlkDevice {
411-
pub fn new(transport: VirtIOTransport, dev_id: Arc<DeviceId>) -> Option<Arc<Self>> {
422+
pub(crate) fn new(
423+
transport: VirtIOTransport,
424+
dev_id: Arc<DeviceId>,
425+
) -> Option<(Arc<Self>, Option<DeferredVirtioIrq>)> {
412426
// 设置中断
413-
if let Err(err) = transport.setup_irq(dev_id.clone()) {
414-
error!("VirtIOBlkDevice '{dev_id:?}' setup_irq failed: {:?}", err);
415-
return None;
416-
}
427+
let irq_setup = match transport.setup_irq(dev_id.clone()) {
428+
Ok(setup) => setup,
429+
Err(err) => {
430+
error!("VirtIOBlkDevice '{dev_id:?}' setup_irq failed: {:?}", err);
431+
return None;
432+
}
433+
};
417434

418435
let devname = virtioblk_manager().alloc_id()?;
419436
let irq = Some(transport.irq());
@@ -490,13 +507,25 @@ impl VirtIOBlkDevice {
490507
return None;
491508
}
492509

493-
Some(dev)
510+
Some((dev, irq_setup.into_deferred()))
494511
}
495512

496513
fn inner(&self) -> SpinLockGuard<'_, InnerVirtIOBlkDevice> {
497514
self.inner.lock_irqsave()
498515
}
499516

517+
fn abort_initialization(self: &Arc<Self>) {
518+
let devname_id = self.blkdev_meta.devname.id();
519+
if let Err(err) = self.shutdown() {
520+
error!(
521+
"VirtIOBlkDevice '{:?}' initialization cleanup failed: {:?}",
522+
self.dev_id, err
523+
);
524+
return;
525+
}
526+
virtioblk_manager().free_id(devname_id);
527+
}
528+
500529
fn shutdown(self: &Arc<Self>) -> Result<(), SystemError> {
501530
let (bio_queue, bio_token_map, io_thread_pcb) = {
502531
let mut inner = self.inner();

kernel/src/driver/char/virtio_console.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
video::console::dummycon::dummy_console,
2323
virtio::{
2424
sysfs::{virtio_bus, virtio_device_manager, virtio_driver_manager},
25-
transport::VirtIOTransport,
25+
transport::{DeferredVirtioIrq, VirtIOTransport},
2626
virtio_drivers_error_to_system_error,
2727
virtio_impl::HalImpl,
2828
VirtIODevice, VirtIODeviceIndex, VirtIODriver, VirtIODriverCommonData, VirtioDeviceId,
@@ -307,16 +307,23 @@ pub fn virtio_console(
307307
dev_id,
308308
dev_parent.as_ref().map(|x| x.name())
309309
);
310-
let device = VirtIOConsoleDevice::new(transport, dev_id.clone());
311-
if device.is_none() {
310+
let Some((device, deferred_irq)) = VirtIOConsoleDevice::new(transport, dev_id.clone()) else {
312311
return;
313-
}
314-
315-
let device = device.unwrap();
312+
};
316313

317314
if let Some(dev_parent) = dev_parent {
318315
device.set_dev_parent(Some(Arc::downgrade(&dev_parent)));
319316
}
317+
if let Some(deferred_irq) = deferred_irq {
318+
if let Err(err) = deferred_irq.install(device.dev_id().clone()) {
319+
log::error!(
320+
"VirtIOConsoleDevice '{:?}' setup_irq failed: {:?}",
321+
device.dev_id(),
322+
err
323+
);
324+
return;
325+
}
326+
}
320327
virtio_device_manager()
321328
.device_add(device.clone() as Arc<dyn VirtIODevice>)
322329
.expect("Add virtio console failed");
@@ -353,15 +360,21 @@ impl Debug for VirtIOConsoleDevice {
353360
}
354361

355362
impl VirtIOConsoleDevice {
356-
pub fn new(transport: VirtIOTransport, dev_id: Arc<DeviceId>) -> Option<Arc<Self>> {
363+
pub(crate) fn new(
364+
transport: VirtIOTransport,
365+
dev_id: Arc<DeviceId>,
366+
) -> Option<(Arc<Self>, Option<DeferredVirtioIrq>)> {
357367
// 设置中断
358-
if let Err(err) = transport.setup_irq(dev_id.clone()) {
359-
log::error!(
360-
"VirtIOConsoleDevice '{dev_id:?}' setup_irq failed: {:?}",
361-
err
362-
);
363-
return None;
364-
}
368+
let irq_setup = match transport.setup_irq(dev_id.clone()) {
369+
Ok(setup) => setup,
370+
Err(err) => {
371+
log::error!(
372+
"VirtIOConsoleDevice '{dev_id:?}' setup_irq failed: {:?}",
373+
err
374+
);
375+
return None;
376+
}
377+
};
365378

366379
let irq = Some(transport.irq());
367380
let device_inner = DragonVirtIOConsole::new(transport);
@@ -394,7 +407,7 @@ impl VirtIOConsoleDevice {
394407
}),
395408
});
396409

397-
Some(dev)
410+
Some((dev, irq_setup.into_deferred()))
398411
}
399412

400413
fn inner(&self) -> SpinLockGuard<'_, InnerVirtIOConsoleDevice> {

kernel/src/driver/net/virtio_net.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::{
3737
virtio::{
3838
irq::virtio_irq_manager,
3939
sysfs::{virtio_bus, virtio_device_manager, virtio_driver_manager},
40-
transport::VirtIOTransport,
40+
transport::{DeferredVirtioIrq, VirtIOTransport},
4141
virtio_impl::HalImpl,
4242
VirtIODevice, VirtIODeviceIndex, VirtIODriver, VirtIODriverCommonData, VirtioDeviceId,
4343
VIRTIO_VENDOR_ID,
@@ -108,12 +108,18 @@ impl Debug for InnerVirtIONetDevice {
108108
}
109109

110110
impl VirtIONetDevice {
111-
pub fn new(transport: VirtIOTransport, dev_id: Arc<DeviceId>) -> Option<Arc<Self>> {
111+
pub(crate) fn new(
112+
transport: VirtIOTransport,
113+
dev_id: Arc<DeviceId>,
114+
) -> Option<(Arc<Self>, Option<DeferredVirtioIrq>)> {
112115
// 设置中断
113-
if let Err(err) = transport.setup_irq(dev_id.clone()) {
114-
error!("VirtIONetDevice '{dev_id:?}' setup_irq failed: {:?}", err);
115-
return None;
116-
}
116+
let irq_setup = match transport.setup_irq(dev_id.clone()) {
117+
Ok(setup) => setup,
118+
Err(err) => {
119+
error!("VirtIONetDevice '{dev_id:?}' setup_irq failed: {:?}", err);
120+
return None;
121+
}
122+
};
117123

118124
let irq_is_msix = transport.irq_is_msix();
119125
let driver_net: VirtIONet<HalImpl, VirtIOTransport, 2> =
@@ -144,7 +150,7 @@ impl VirtIONetDevice {
144150

145151
// dev.set_driver(Some(Arc::downgrade(&virtio_net_driver()) as Weak<dyn Driver>));
146152

147-
return Some(dev);
153+
return Some((dev, irq_setup.into_deferred()));
148154
}
149155

150156
fn inner(&self) -> SpinLockGuard<'_, InnerVirtIONetDevice> {
@@ -767,11 +773,21 @@ pub fn virtio_net(
767773
dev_parent: Option<Arc<dyn Device>>,
768774
) {
769775
let virtio_net_deivce = VirtIONetDevice::new(transport, dev_id);
770-
if let Some(virtio_net_deivce) = virtio_net_deivce {
776+
if let Some((virtio_net_deivce, deferred_irq)) = virtio_net_deivce {
771777
debug!("VirtIONetDevice '{:?}' created", virtio_net_deivce.dev_id);
772778
if let Some(dev_parent) = dev_parent {
773779
virtio_net_deivce.set_dev_parent(Some(Arc::downgrade(&dev_parent)));
774780
}
781+
if let Some(deferred_irq) = deferred_irq {
782+
if let Err(err) = deferred_irq.install(virtio_net_deivce.dev_id().clone()) {
783+
error!(
784+
"VirtIONetDevice '{:?}' setup_irq failed: {:?}",
785+
virtio_net_deivce.dev_id(),
786+
err
787+
);
788+
return;
789+
}
790+
}
775791
virtio_device_manager()
776792
.device_add(virtio_net_deivce.clone() as Arc<dyn VirtIODevice>)
777793
.expect("Add virtio net failed");

kernel/src/driver/virtio/sysfs.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ use crate::{
2020
kobject::KObject,
2121
subsys::SubSysPrivate,
2222
},
23-
virtio::irq::{virtio_irq_manager, DefaultVirtioIrqHandler},
23+
virtio::irq::virtio_irq_manager,
2424
},
25-
exception::{irqdesc::IrqHandleFlags, manage::irq_manager},
2625
filesystem::{
2726
sysfs::{
2827
file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport, SYSFS_ATTR_MODE_RO,
@@ -265,22 +264,6 @@ impl VirtIODeviceManager {
265264
return Ok(());
266265
}
267266
let irq = irq.unwrap();
268-
if let Err(e) = irq_manager().request_irq(
269-
irq,
270-
dev.device_name(),
271-
&DefaultVirtioIrqHandler,
272-
IrqHandleFlags::IRQF_SHARED,
273-
Some(dev.dev_id().clone()),
274-
) {
275-
error!(
276-
"Failed to request irq for virtio device '{}': irq: {:?}, error {:?}",
277-
dev.device_name(),
278-
irq,
279-
e
280-
);
281-
return Err(e);
282-
}
283-
284267
virtio_irq_manager()
285268
.register_device(dev.clone())
286269
.map_err(|e| {

0 commit comments

Comments
 (0)