Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
760c18b
virtio: Change VirtioDevice::reset() return type to bool
ilstam May 12, 2026
2e2eb2d
virtio: mmio: Simplify MMIO transport reset logic
ilstam May 13, 2026
f417588
virtio: pci: Fix deadlock in device reset path
ilstam May 14, 2026
f524b0e
virtio: pci: Reset feature select registers on device reset
ilstam May 14, 2026
0c4f633
virtio: pci: Drop MSI-X configuration on device reset
ilstam May 14, 2026
9248ad0
virtio: mmio: Add unit test for device reset failure
ilstam May 13, 2026
ef74639
virtio: Add deactivate() method to VirtioDevice trait
ilstam May 15, 2026
391f1ee
virtio: Notify queues after device activation
ilstam Jun 25, 2026
6312d0c
virtio: Consume eventfds while the device is deactivated
ilstam Jun 23, 2026
7cfaf2f
virtio: Move queue reset from transport to VirtioDevice::reset()
ilstam May 15, 2026
d5d2cf4
virtio: Complete reset() and introduce _reset()
ilstam May 15, 2026
48fc440
virtio: net: Add a RxBuffers::clear() method
ilstam May 13, 2026
96313eb
virtio: net: Implement device reset
ilstam May 13, 2026
3666803
tests: Add integration test for virtio-net device reset
ilstam May 13, 2026
d57cc1f
virtio: block: Implement device reset
ilstam May 14, 2026
eee0b22
tests: Add integration test for virtio-block device reset
ilstam May 14, 2026
5317f59
virtio: pmem: Implement device reset
ilstam May 14, 2026
0113a09
tests: Add integration test for virtio-pmem device reset
ilstam May 14, 2026
290a5dc
virtio: balloon: Implement device reset
ilstam May 14, 2026
f9ea2f4
tests: Add integration test for virtio-balloon device reset
ilstam May 14, 2026
0618104
virtio: entropy: Implement device reset
ilstam May 14, 2026
be64d19
tests: Add integration test for virtio-rng device reset
ilstam May 14, 2026
b856877
virtio: vsock: Register the host socket listener on activation
ilstam Jul 2, 2026
a20e512
virtio: vsock: Implement device reset
ilstam May 14, 2026
88470bc
tests: Add integration test for virtio-vsock device reset
ilstam May 14, 2026
1baf3f5
virtio: mem: Implement device reset
ilstam May 14, 2026
eecc924
tests: Add integration test for virtio-mem device reset
ilstam May 15, 2026
fa12003
CHANGELOG: Mention virtio device reset support
ilstam May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to

### Added

- [#5891](https://github.com/firecracker-microvm/firecracker/pull/5891): Added
support for virtio device reset.
- [#5983](https://github.com/firecracker-microvm/firecracker/pull/5983): Add two
optional metrics fields, set via `PUT /metrics` or a config file: `emit_id`
emits the microVM instance id under a top-level `id` field, and `properties`
Expand Down
12 changes: 12 additions & 0 deletions resources/seccomp/aarch64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,18 @@
{
"syscall": "close"
},
{
"syscall": "eventfd2",
"comment": "Needed for recreating the MSI-X vector eventfds when a PCI device is reset by the guest (MsixVectorGroup recreated from the vCPU thread)"
},
{
"syscall": "io_uring_enter",
"comment": "Needed to drain the async block IO engine's io_uring when the device is reset by the guest (from the vCPU thread)"
},
{
"syscall": "epoll_ctl",
"comment": "Needed to remove the vsock muxer's connection and listener epoll listeners when the device is reset by the guest (from the vCPU thread)"
},
{
"syscall": "fstat",
"comment": "Used for reading the local timezone from /etc/localtime"
Expand Down
12 changes: 12 additions & 0 deletions resources/seccomp/x86_64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,18 @@
{
"syscall": "close"
},
{
"syscall": "eventfd2",
"comment": "Needed for recreating the MSI-X vector eventfds when a PCI device is reset by the guest (MsixVectorGroup recreated from the vCPU thread)"
},
{
"syscall": "io_uring_enter",
"comment": "Needed to drain the async block IO engine's io_uring when the device is reset by the guest (from the vCPU thread)"
},
{
"syscall": "epoll_ctl",
"comment": "Needed to remove the vsock muxer's connection and listener epoll listeners when the device is reset by the guest (from the vCPU thread)"
},
{
"syscall": "fstat",
"comment": "Used for reading the local timezone from /etc/localtime"
Expand Down
6 changes: 6 additions & 0 deletions src/vmm/src/device_manager/mmio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,12 @@ pub(crate) mod tests {
fn is_activated(&self) -> bool {
false
}

fn deactivate(&mut self) {}

fn _reset(&mut self) -> bool {
false
}
}

#[test]
Expand Down
14 changes: 14 additions & 0 deletions src/vmm/src/devices/virtio/balloon/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,20 @@ impl VirtioDevice for Balloon {
self.device_state.is_activated()
}

fn deactivate(&mut self) {
self.device_state = DeviceState::Inactive;
}

fn _reset(&mut self) -> bool {
Comment thread
ilstam marked this conversation as resolved.
Comment thread
ilstam marked this conversation as resolved.
self.config_space.actual_pages = 0;
self.config_space.free_page_hint_cmd_id = FREE_PAGE_HINT_STOP;
self.stats_timer.arm(Duration::ZERO, None);
Comment thread
ilstam marked this conversation as resolved.
self.stats_desc_index = None;
Comment thread
ilstam marked this conversation as resolved.
self.latest_stats = BalloonStats::default();
self.hinting_state = Default::default();
true
}

fn kick(&mut self) {
if self.is_activated() {
if self.free_page_hinting() {
Expand Down
1 change: 1 addition & 0 deletions src/vmm/src/devices/virtio/balloon/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ impl MutEventSubscriber for Balloon {
"Balloon: The device is not yet activated. Spurious event received: {:?}",
source
);
self.drain_queue_events();
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/vmm/src/devices/virtio/block/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,20 @@ impl VirtioDevice for Block {
}
}

fn deactivate(&mut self) {
match self {
Self::Virtio(b) => b.deactivate(),
Self::VhostUser(b) => b.deactivate(),
}
}

fn _reset(&mut self) -> bool {
match self {
Self::Virtio(b) => b._reset(),
Self::VhostUser(b) => b._reset(),
}
}

fn prepare_save(&mut self) {
match self {
Self::Virtio(b) => b.prepare_save(),
Expand Down
8 changes: 8 additions & 0 deletions src/vmm/src/devices/virtio/block/vhost_user/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,14 @@ where
fn is_activated(&self) -> bool {
self.device_state.is_activated()
}

fn deactivate(&mut self) {
self.device_state = DeviceState::Inactive;
}

fn _reset(&mut self) -> bool {
false
Comment thread
ilstam marked this conversation as resolved.
}
}

#[cfg(test)]
Expand Down
13 changes: 13 additions & 0 deletions src/vmm/src/devices/virtio/block/virtio/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,19 @@ impl VirtioDevice for VirtioBlock {
fn is_activated(&self) -> bool {
self.device_state.is_activated()
}

fn deactivate(&mut self) {
Comment thread
ilstam marked this conversation as resolved.
self.device_state = DeviceState::Inactive;
}

fn _reset(&mut self) -> bool {
if let Err(err) = self.disk.file_engine.drain(true) {
error!("Failed to reset block IO engine: {:?}", err);
return false;
}
self.is_io_engine_throttled = false;
Comment thread
ilstam marked this conversation as resolved.
true
}
}

impl Drop for VirtioBlock {
Expand Down
12 changes: 12 additions & 0 deletions src/vmm/src/devices/virtio/block/virtio/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ impl MutEventSubscriber for VirtioBlock {
"Block: The device is not yet activated. Spurious event received: {:?}",
source
);
match source {
Self::PROCESS_QUEUE => self.drain_queue_events(),
Self::PROCESS_RATE_LIMITER => {
self.rate_limiter.event_handler();
}
Self::PROCESS_ASYNC_COMPLETION => {
if let FileEngine::Async(ref engine) = self.disk.file_engine {
engine.completion_evt().read();
}
}
_ => (),
}
}
}

Expand Down
40 changes: 36 additions & 4 deletions src/vmm/src/devices/virtio/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,27 @@ pub trait VirtioDevice: AsAny + MutEventSubscriber + Send {
/// Checks if the resources of this device are activated.
fn is_activated(&self) -> bool;

/// Optionally deactivates this device and returns ownership of the guest memory map, interrupt
/// event, and queue events.
fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
None
/// Set the device state to Inactive
fn deactivate(&mut self);

/// Reset the device. Returns true on success, false otherwise.
/// It must not be overridden.
Comment thread
ilstam marked this conversation as resolved.
fn reset(&mut self) -> bool {
if !self._reset() {
return false;
}
self.deactivate();
self.set_acked_features(0);
for queue in self.queues_mut() {
*queue = Queue::new(queue.max_size);
}
true
}

/// Backend-specific reset logic. Returns true on success, false if the
/// backend does not support reset.
fn _reset(&mut self) -> bool;

/// Mark pages used by queues as dirty.
fn mark_queue_memory_dirty(&mut self, mem: &GuestMemoryMmap) -> Result<(), QueueError> {
for queue in self.queues_mut() {
Expand All @@ -224,6 +239,15 @@ pub trait VirtioDevice: AsAny + MutEventSubscriber + Send {
}
}

/// Drain all queue notification eventfds, discarding any pending
/// notifications. This is used if a notification arrives while a device
/// is being reset and before it's activated again.
fn drain_queue_events(&self) {
for event in self.queue_events() {
event.read();
}
}

/// Kick the device, as if it had received external events.
fn kick(&mut self) {
if self.is_activated() {
Expand Down Expand Up @@ -327,6 +351,14 @@ pub(crate) mod tests {
fn is_activated(&self) -> bool {
todo!()
}

fn deactivate(&mut self) {
todo!()
}

fn _reset(&mut self) -> bool {
todo!()
}
}

#[test]
Expand Down
15 changes: 15 additions & 0 deletions src/vmm/src/devices/virtio/mem/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,21 @@ impl VirtioDevice for VirtioMem {
self.device_state.is_activated()
}

fn deactivate(&mut self) {
self.device_state = DeviceState::Inactive;
}

fn _reset(&mut self) -> bool {
// Virtio spec, section 5.15.5.2:
// The device MUST NOT change the state of memory blocks during device
// reset. The device MUST NOT modify memory or memory properties of
// plugged memory blocks during device reset.
//
// Note: the Linux virtio-mem driver does not support rebinding when
// memory is plugged
true
}

Comment thread
ilstam marked this conversation as resolved.
fn activate(
&mut self,
mem: GuestMemoryMmap,
Expand Down
1 change: 1 addition & 0 deletions src/vmm/src/devices/virtio/mem/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl MutEventSubscriber for VirtioMem {

if !self.is_activated() {
warn!("virtio-mem: The device is not activated yet. Spurious event received: {source}");
self.drain_queue_events();
return;
}

Expand Down
2 changes: 2 additions & 0 deletions src/vmm/src/devices/virtio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub enum ActivateError {
QueueMemoryError(QueueError),
/// The driver didn't acknowledge a required feature: {0}
RequiredFeatureNotAcked(&'static str),
/// Vsock backend: {0}
VsockBackend(vsock::VsockError),
}

/// Trait that helps in upcasting an object to Any
Expand Down
31 changes: 31 additions & 0 deletions src/vmm/src/devices/virtio/net/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ impl RxBuffers {
})
}

/// Reset the RX buffers to their initial state.
fn clear(&mut self) {
self.iovec.clear();
self.parsed_descriptors.clear();
self.used_descriptors = 0;
self.used_bytes = 0;
self.min_buffer_size = 0;
}

/// Add a new `DescriptorChain` that we received from the RX queue in the buffer.
///
/// SAFETY: The `DescriptorChain` cannot be referencing the same memory location as any other
Expand Down Expand Up @@ -1072,6 +1081,16 @@ impl VirtioDevice for Net {
self.device_state.is_activated()
}

fn deactivate(&mut self) {
self.device_state = DeviceState::Inactive;
}

fn _reset(&mut self) -> bool {
self.rx_buffer.clear();
Comment thread
Manciukic marked this conversation as resolved.
self.tx_buffer.clear();
true
}

/// Prepare saving state
fn prepare_save(&mut self) {
// We shouldn't be messing with the queue if the device is not activated.
Expand Down Expand Up @@ -2589,4 +2608,16 @@ pub mod tests {
assert!(queues[RX_INDEX].uses_notif_suppression);
assert!(queues[TX_INDEX].uses_notif_suppression);
}

#[test]
fn test_reset() {
let mem = single_region_mem(2 * MAX_BUFFER_SIZE);
let mut th = TestHelper::get_default(&mem);
th.activate_net();

assert!(th.net().is_activated());
assert!(th.net().reset());
assert!(!th.net().is_activated());
assert_eq!(th.net().acked_features(), 0);
}
}
10 changes: 10 additions & 0 deletions src/vmm/src/devices/virtio/net/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ impl MutEventSubscriber for Net {
"Net: The device is not yet activated. Spurious event received: {:?}",
source
);
match source {
Self::PROCESS_VIRTQ_RX | Self::PROCESS_VIRTQ_TX => self.drain_queue_events(),
Self::PROCESS_RX_RATE_LIMITER => {
self.rx_rate_limiter.event_handler();
}
Self::PROCESS_TX_RATE_LIMITER => {
self.tx_rate_limiter.event_handler();
}
_ => (),
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/vmm/src/devices/virtio/pmem/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,14 @@ impl VirtioDevice for Pmem {
self.device_state.is_activated()
}

fn deactivate(&mut self) {
self.device_state = DeviceState::Inactive;
}

fn _reset(&mut self) -> bool {
true
}

fn kick(&mut self) {
if self.is_activated() {
info!("kick pmem {}.", self.config.id);
Expand Down
7 changes: 7 additions & 0 deletions src/vmm/src/devices/virtio/pmem/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ impl MutEventSubscriber for Pmem {

if !self.is_activated() {
warn!("pmem: The device is not activated yet. Spurious event received from {source}");
match source {
Self::PROCESS_PMEM_QUEUE => self.drain_queue_events(),
Self::PROCESS_RATE_LIMITER => {
self.rate_limiter.event_handler();
}
_ => (),
}
return;
}

Expand Down
13 changes: 0 additions & 13 deletions src/vmm/src/devices/virtio/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,19 +683,6 @@ impl Queue {

new - used_event - Wrapping(1) < new - old
}

/// Resets the Virtio Queue
pub(crate) fn reset(&mut self) {
self.ready = false;
self.size = self.max_size;
self.desc_table_address = GuestAddress(0);
self.avail_ring_address = GuestAddress(0);
self.used_ring_address = GuestAddress(0);
self.next_avail = Wrapping(0);
self.next_used = Wrapping(0);
self.num_added = Wrapping(0);
self.uses_notif_suppression = false;
}
}

#[cfg(kani)]
Expand Down
Loading
Loading