Skip to content

Commit 7f6dbad

Browse files
committed
refactor: set VirtIO interrupt during activation
The MMIO transport for VirtIO devices uses an `IrqTrigger` object as the object that models the logic for sending interrupts from the device to the guest. We create one such object for every VirtIO device when creating it. The MMIO device manager associates this object with an IRQ number and registers it with KVM. This commit changes the timing of association of an `IrqTrigger` with a VirtIO-mmio device. It only assigns such an object to the device during its activation. We do this to prepare for supporting a PCI transport for VirtIO devices. The cloud hypervisor implementation for these passes the interrupt objects used by the device during activation, so we make this change to have a uniform way to handle interrupts for both transports. Functionally, nothing changes for MMIO devices, as before activation we don't trigger any interrupts. Signed-off-by: Babis Chalios <[email protected]>
1 parent 74e3113 commit 7f6dbad

File tree

28 files changed

+487
-277
lines changed

28 files changed

+487
-277
lines changed

src/vmm/src/builder.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use crate::devices::virtio::block::device::Block;
4444
use crate::devices::virtio::device::VirtioDevice;
4545
use crate::devices::virtio::net::Net;
4646
use crate::devices::virtio::rng::Entropy;
47-
use crate::devices::virtio::transport::mmio::MmioTransport;
47+
use crate::devices::virtio::transport::mmio::{IrqTrigger, MmioTransport};
4848
use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
4949
#[cfg(feature = "gdb")]
5050
use crate::gdb;
@@ -597,8 +597,14 @@ fn attach_virtio_device<T: 'static + VirtioDevice + MutEventSubscriber + Debug>(
597597
) -> Result<(), MmioError> {
598598
event_manager.add_subscriber(device.clone());
599599

600+
let interrupt = Arc::new(IrqTrigger::new()?);
600601
// The device mutex mustn't be locked here otherwise it will deadlock.
601-
let device = MmioTransport::new(vmm.vm.guest_memory().clone(), device, is_vhost_user);
602+
let device = MmioTransport::new(
603+
vmm.vm.guest_memory().clone(),
604+
interrupt,
605+
device,
606+
is_vhost_user,
607+
);
602608
vmm.mmio_device_manager
603609
.register_mmio_virtio_for_boot(
604610
vmm.vm.fd(),

src/vmm/src/device_manager/mmio.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pub enum MmioError {
5353
InvalidDeviceType,
5454
/// {0}
5555
InternalDeviceError(String),
56+
/// Could not create IRQ for MMIO device: {0}
57+
CreateIrq(#[from] std::io::Error),
5658
/// Invalid MMIO IRQ configuration.
5759
InvalidIrqConfig,
5860
/// Failed to register IO event: {0}
@@ -205,7 +207,7 @@ impl MMIODeviceManager {
205207
vm.register_ioevent(queue_evt, &io_addr, u32::try_from(i).unwrap())
206208
.map_err(MmioError::RegisterIoEvent)?;
207209
}
208-
vm.register_irqfd(&locked_device.interrupt_trigger().irq_evt, irq.get())
210+
vm.register_irqfd(&mmio_device.interrupt.irq_evt, irq.get())
209211
.map_err(MmioError::RegisterIrqFd)?;
210212
}
211213

@@ -549,7 +551,8 @@ mod tests {
549551
cmdline: &mut kernel_cmdline::Cmdline,
550552
dev_id: &str,
551553
) -> Result<u64, MmioError> {
552-
let mmio_device = MmioTransport::new(guest_mem, device, false);
554+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
555+
let mmio_device = MmioTransport::new(guest_mem, interrupt, device, false);
553556
let device_info = self.register_mmio_virtio_for_boot(
554557
vm,
555558
resource_allocator,
@@ -576,7 +579,7 @@ mod tests {
576579
dummy: u32,
577580
queues: Vec<Queue>,
578581
queue_evts: [EventFd; 1],
579-
interrupt_trigger: IrqTrigger,
582+
interrupt_trigger: Option<Arc<IrqTrigger>>,
580583
}
581584

582585
impl DummyDevice {
@@ -585,7 +588,7 @@ mod tests {
585588
dummy: 0,
586589
queues: QUEUE_SIZES.iter().map(|&s| Queue::new(s)).collect(),
587590
queue_evts: [EventFd::new(libc::EFD_NONBLOCK).expect("cannot create eventFD")],
588-
interrupt_trigger: IrqTrigger::new().expect("cannot create eventFD"),
591+
interrupt_trigger: None,
589592
}
590593
}
591594
}
@@ -617,8 +620,11 @@ mod tests {
617620
&self.queue_evts
618621
}
619622

620-
fn interrupt_trigger(&self) -> &IrqTrigger {
621-
&self.interrupt_trigger
623+
fn interrupt_trigger(&self) -> Arc<IrqTrigger> {
624+
self.interrupt_trigger
625+
.as_ref()
626+
.expect("Device is not activated")
627+
.clone()
622628
}
623629

624630
fn ack_features_by_page(&mut self, page: u32, value: u32) {
@@ -636,7 +642,11 @@ mod tests {
636642
let _ = data;
637643
}
638644

639-
fn activate(&mut self, _: GuestMemoryMmap) -> Result<(), ActivateError> {
645+
fn activate(
646+
&mut self,
647+
_: GuestMemoryMmap,
648+
_: Arc<IrqTrigger>,
649+
) -> Result<(), ActivateError> {
640650
Ok(())
641651
}
642652

src/vmm/src/device_manager/persist.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use crate::devices::virtio::rng::Entropy;
3434
use crate::devices::virtio::rng::persist::{
3535
EntropyConstructorArgs, EntropyPersistError as EntropyError, EntropyState,
3636
};
37-
use crate::devices::virtio::transport::mmio::MmioTransport;
37+
use crate::devices::virtio::transport::mmio::{IrqTrigger, MmioTransport};
3838
use crate::devices::virtio::vsock::persist::{
3939
VsockConstructorArgs, VsockState, VsockUdsConstructorArgs,
4040
};
@@ -476,8 +476,22 @@ impl<'a> Persist<'a> for MMIODeviceManager {
476476
device_info: &MMIODeviceInfo,
477477
event_manager: &mut EventManager|
478478
-> Result<(), Self::Error> {
479+
// If the device was already activated, an interrupt was created and assigned to it. In
480+
// that case grab a reference to it and pass it to the transport state. Otherwise,
481+
// create a fresh one. Like this both the transport and the device will hold a
482+
// reference to the same interrupt object, at all times.
483+
let interrupt = {
484+
let locked_dev = device.lock().expect("Poisoned lock");
485+
if locked_dev.is_activated() {
486+
locked_dev.interrupt_trigger()
487+
} else {
488+
Arc::new(IrqTrigger::new().expect("Could not create IRQ for MMIO device"))
489+
}
490+
};
491+
479492
let restore_args = MmioTransportConstructorArgs {
480493
mem: mem.clone(),
494+
interrupt,
481495
device,
482496
is_vhost_user,
483497
};

src/vmm/src/devices/virtio/balloon/device.rs

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use std::fmt;
4+
use std::sync::Arc;
55
use std::time::Duration;
66

77
use log::error;
@@ -149,6 +149,7 @@ impl BalloonStats {
149149
}
150150
}
151151

152+
#[derive(Debug)]
152153
/// Virtio balloon device.
153154
pub struct Balloon {
154155
// Virtio fields.
@@ -161,7 +162,6 @@ pub struct Balloon {
161162
pub(crate) queues: Vec<Queue>,
162163
pub(crate) queue_evts: [EventFd; BALLOON_NUM_QUEUES],
163164
pub(crate) device_state: DeviceState,
164-
pub(crate) irq_trigger: IrqTrigger,
165165

166166
// Implementation specific fields.
167167
pub(crate) restored_from_file: bool,
@@ -175,29 +175,6 @@ pub struct Balloon {
175175
pub(crate) pfn_buffer: [u32; MAX_PAGE_COMPACT_BUFFER],
176176
}
177177

178-
// TODO Use `#[derive(Debug)]` when a new release of
179-
// [rust-timerfd](https://github.com/main--/rust-timerfd) is published that includes
180-
// https://github.com/main--/rust-timerfd/pull/12.
181-
impl fmt::Debug for Balloon {
182-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183-
f.debug_struct("Balloon")
184-
.field("avail_features", &self.avail_features)
185-
.field("acked_features", &self.acked_features)
186-
.field("config_space", &self.config_space)
187-
.field("activate_evt", &self.activate_evt)
188-
.field("queues", &self.queues)
189-
.field("queue_evts", &self.queue_evts)
190-
.field("device_state", &self.device_state)
191-
.field("irq_trigger", &self.irq_trigger)
192-
.field("restored_from_file", &self.restored_from_file)
193-
.field("stats_polling_interval_s", &self.stats_polling_interval_s)
194-
.field("stats_desc_index", &self.stats_desc_index)
195-
.field("latest_stats", &self.latest_stats)
196-
.field("pfn_buffer", &self.pfn_buffer)
197-
.finish()
198-
}
199-
}
200-
201178
impl Balloon {
202179
/// Instantiate a new balloon device.
203180
pub fn new(
@@ -242,7 +219,6 @@ impl Balloon {
242219
},
243220
queue_evts,
244221
queues,
245-
irq_trigger: IrqTrigger::new().map_err(BalloonError::EventFd)?,
246222
device_state: DeviceState::Inactive,
247223
activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(BalloonError::EventFd)?,
248224
restored_from_file,
@@ -282,7 +258,7 @@ impl Balloon {
282258

283259
pub(crate) fn process_inflate(&mut self) -> Result<(), BalloonError> {
284260
// This is safe since we checked in the event handler that the device is activated.
285-
let mem = self.device_state.mem().unwrap();
261+
let (mem, _) = self.device_state.active_state().unwrap();
286262
METRICS.inflate_count.inc();
287263

288264
let queue = &mut self.queues[INFLATE_INDEX];
@@ -389,7 +365,7 @@ impl Balloon {
389365

390366
pub(crate) fn process_stats_queue(&mut self) -> Result<(), BalloonError> {
391367
// This is safe since we checked in the event handler that the device is activated.
392-
let mem = self.device_state.mem().unwrap();
368+
let (mem, _) = self.device_state.active_state().unwrap();
393369
METRICS.stats_updates_count.inc();
394370

395371
while let Some(head) = self.queues[STATS_INDEX].pop() {
@@ -426,10 +402,12 @@ impl Balloon {
426402
}
427403

428404
pub(crate) fn signal_used_queue(&self) -> Result<(), BalloonError> {
429-
self.irq_trigger.trigger_irq(IrqType::Vring).map_err(|err| {
430-
METRICS.event_fails.inc();
431-
BalloonError::InterruptError(err)
432-
})
405+
self.interrupt_trigger()
406+
.trigger_irq(IrqType::Vring)
407+
.map_err(|err| {
408+
METRICS.event_fails.inc();
409+
BalloonError::InterruptError(err)
410+
})
433411
}
434412

435413
/// Process device virtio queue(s).
@@ -461,7 +439,7 @@ impl Balloon {
461439
pub fn update_size(&mut self, amount_mib: u32) -> Result<(), BalloonError> {
462440
if self.is_activated() {
463441
self.config_space.num_pages = mib_to_pages(amount_mib)?;
464-
self.irq_trigger
442+
self.interrupt_trigger()
465443
.trigger_irq(IrqType::Config)
466444
.map_err(BalloonError::InterruptError)
467445
} else {
@@ -573,8 +551,11 @@ impl VirtioDevice for Balloon {
573551
&self.queue_evts
574552
}
575553

576-
fn interrupt_trigger(&self) -> &IrqTrigger {
577-
&self.irq_trigger
554+
fn interrupt_trigger(&self) -> Arc<IrqTrigger> {
555+
self.device_state
556+
.active_state()
557+
.expect("Device is not activated")
558+
.1
578559
}
579560

580561
fn read_config(&self, offset: u64, data: &mut [u8]) {
@@ -601,13 +582,17 @@ impl VirtioDevice for Balloon {
601582
dst.copy_from_slice(data);
602583
}
603584

604-
fn activate(&mut self, mem: GuestMemoryMmap) -> Result<(), ActivateError> {
585+
fn activate(
586+
&mut self,
587+
mem: GuestMemoryMmap,
588+
interrupt: Arc<IrqTrigger>,
589+
) -> Result<(), ActivateError> {
605590
for q in self.queues.iter_mut() {
606591
q.initialize(&mem)
607592
.map_err(ActivateError::QueueMemoryError)?;
608593
}
609594

610-
self.device_state = DeviceState::Activated(mem);
595+
self.device_state = DeviceState::Activated((mem, interrupt));
611596
if self.activate_evt.write(1).is_err() {
612597
METRICS.activate_fails.inc();
613598
self.device_state = DeviceState::Inactive;
@@ -813,10 +798,11 @@ pub(crate) mod tests {
813798
fn test_invalid_request() {
814799
let mut balloon = Balloon::new(0, true, 0, false).unwrap();
815800
let mem = default_mem();
801+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
816802
// Only initialize the inflate queue to demonstrate invalid request handling.
817803
let infq = VirtQueue::new(GuestAddress(0), &mem, 16);
818804
balloon.set_queue(INFLATE_INDEX, infq.create_queue());
819-
balloon.activate(mem.clone()).unwrap();
805+
balloon.activate(mem.clone(), interrupt).unwrap();
820806

821807
// Fill the second page with non-zero bytes.
822808
for i in 0..0x1000 {
@@ -872,9 +858,10 @@ pub(crate) mod tests {
872858
fn test_inflate() {
873859
let mut balloon = Balloon::new(0, true, 0, false).unwrap();
874860
let mem = default_mem();
861+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
875862
let infq = VirtQueue::new(GuestAddress(0), &mem, 16);
876863
balloon.set_queue(INFLATE_INDEX, infq.create_queue());
877-
balloon.activate(mem.clone()).unwrap();
864+
balloon.activate(mem.clone(), interrupt).unwrap();
878865

879866
// Fill the third page with non-zero bytes.
880867
for i in 0..0x1000 {
@@ -942,9 +929,10 @@ pub(crate) mod tests {
942929
fn test_deflate() {
943930
let mut balloon = Balloon::new(0, true, 0, false).unwrap();
944931
let mem = default_mem();
932+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
945933
let defq = VirtQueue::new(GuestAddress(0), &mem, 16);
946934
balloon.set_queue(DEFLATE_INDEX, defq.create_queue());
947-
balloon.activate(mem.clone()).unwrap();
935+
balloon.activate(mem.clone(), interrupt).unwrap();
948936

949937
let page_addr = 0x10;
950938

@@ -990,9 +978,10 @@ pub(crate) mod tests {
990978
fn test_stats() {
991979
let mut balloon = Balloon::new(0, true, 1, false).unwrap();
992980
let mem = default_mem();
981+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
993982
let statsq = VirtQueue::new(GuestAddress(0), &mem, 16);
994983
balloon.set_queue(STATS_INDEX, statsq.create_queue());
995-
balloon.activate(mem.clone()).unwrap();
984+
balloon.activate(mem.clone(), interrupt).unwrap();
996985

997986
let page_addr = 0x100;
998987

@@ -1068,7 +1057,7 @@ pub(crate) mod tests {
10681057
assert!(balloon.stats_desc_index.is_some());
10691058
balloon.process_stats_timer_event().unwrap();
10701059
assert!(balloon.stats_desc_index.is_none());
1071-
assert!(balloon.irq_trigger.has_pending_irq(IrqType::Vring));
1060+
assert!(balloon.interrupt_trigger().has_pending_irq(IrqType::Vring));
10721061
});
10731062
}
10741063
}
@@ -1077,21 +1066,23 @@ pub(crate) mod tests {
10771066
fn test_process_balloon_queues() {
10781067
let mut balloon = Balloon::new(0x10, true, 0, false).unwrap();
10791068
let mem = default_mem();
1069+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
10801070
let infq = VirtQueue::new(GuestAddress(0), &mem, 16);
10811071
let defq = VirtQueue::new(GuestAddress(0), &mem, 16);
10821072

10831073
balloon.set_queue(INFLATE_INDEX, infq.create_queue());
10841074
balloon.set_queue(DEFLATE_INDEX, defq.create_queue());
10851075

1086-
balloon.activate(mem).unwrap();
1076+
balloon.activate(mem, interrupt).unwrap();
10871077
balloon.process_virtio_queues()
10881078
}
10891079

10901080
#[test]
10911081
fn test_update_stats_interval() {
10921082
let mut balloon = Balloon::new(0, true, 0, false).unwrap();
10931083
let mem = default_mem();
1094-
balloon.activate(mem).unwrap();
1084+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
1085+
balloon.activate(mem, interrupt).unwrap();
10951086
assert_eq!(
10961087
format!("{:?}", balloon.update_stats_polling_interval(1)),
10971088
"Err(StatisticsStateChange)"
@@ -1100,7 +1091,8 @@ pub(crate) mod tests {
11001091

11011092
let mut balloon = Balloon::new(0, true, 1, false).unwrap();
11021093
let mem = default_mem();
1103-
balloon.activate(mem).unwrap();
1094+
let interrupt = Arc::new(IrqTrigger::new().unwrap());
1095+
balloon.activate(mem, interrupt).unwrap();
11041096
assert_eq!(
11051097
format!("{:?}", balloon.update_stats_polling_interval(0)),
11061098
"Err(StatisticsStateChange)"
@@ -1120,7 +1112,8 @@ pub(crate) mod tests {
11201112
fn test_num_pages() {
11211113
let mut balloon = Balloon::new(0, true, 0, false).unwrap();
11221114
// Switch the state to active.
1123-
balloon.device_state = DeviceState::Activated(single_region_mem(0x1));
1115+
balloon.device_state =
1116+
DeviceState::Activated((single_region_mem(0x1), Arc::new(IrqTrigger::new().unwrap())));
11241117

11251118
assert_eq!(balloon.num_pages(), 0);
11261119
assert_eq!(balloon.actual_pages(), 0);

0 commit comments

Comments
 (0)