Skip to content

Commit 656f870

Browse files
bchaliosManciukic
authored andcommitted
refactor: VirtIO MMIO persistence logic
VirtIO MMIO restore logic activates the device the moment we restore the device state, if the device was activated when snapshotted. Move the activation responsibility to the logic the restores the MMIO transport. The reason for this change is that that's how it will be done for the PCI transport. Unifying this will allow us reusing the same types for restoring the non-transport state of devices. Note that we needed to change the way Net devices are saved/restored. RxBuffer type of Net devices holds RX descriptors that we have parsed from the Queue ahead of time. The way we restored this info was manipulating the queue to re-parse the RX descriptors during the restore phase. However, we need the device to be activated to do so, which now isn't. So, instead of storing this info inside the snapshot make sure we have flushed everything before taking the snapshot. Also, simplify a bit the types that we use for serializing/deserializing the state of a device. Signed-off-by: Babis Chalios <[email protected]>
1 parent 3ddb116 commit 656f870

File tree

12 files changed

+224
-336
lines changed

12 files changed

+224
-336
lines changed

src/vmm/src/device_manager/mmio.rs

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use kvm_ioctls::IoEventAddress;
1515
use linux_loader::cmdline as kernel_cmdline;
1616
#[cfg(target_arch = "x86_64")]
1717
use log::debug;
18-
use log::info;
1918
use serde::{Deserialize, Serialize};
2019
use vm_allocator::AllocPolicy;
2120

@@ -27,14 +26,8 @@ use crate::arch::{RTC_MEM_START, SERIAL_MEM_START};
2726
#[cfg(target_arch = "aarch64")]
2827
use crate::devices::legacy::{RTCDevice, SerialDevice};
2928
use crate::devices::pseudo::BootTimer;
30-
use crate::devices::virtio::balloon::Balloon;
31-
use crate::devices::virtio::block::device::Block;
3229
use crate::devices::virtio::device::VirtioDevice;
33-
use crate::devices::virtio::net::Net;
34-
use crate::devices::virtio::rng::Entropy;
3530
use crate::devices::virtio::transport::mmio::MmioTransport;
36-
use crate::devices::virtio::vsock::{TYPE_VSOCK, Vsock, VsockUnixBackend};
37-
use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_RNG};
3831
#[cfg(target_arch = "x86_64")]
3932
use crate::vstate::memory::GuestAddress;
4033

@@ -441,79 +434,6 @@ impl MMIODeviceManager {
441434
Ok(())
442435
}
443436

444-
/// Artificially kick devices as if they had external events.
445-
pub fn kick_devices(&self) {
446-
info!("Artificially kick devices.");
447-
// We only kick virtio devices for now.
448-
let _: Result<(), MmioError> = self.for_each_virtio_device(|virtio_type, id, device| {
449-
let mmio_transport_locked = device.inner.lock().expect("Poisoned locked");
450-
let mut virtio = mmio_transport_locked.locked_device();
451-
match *virtio_type {
452-
TYPE_BALLOON => {
453-
let balloon = virtio.as_mut_any().downcast_mut::<Balloon>().unwrap();
454-
// If device is activated, kick the balloon queue(s) to make up for any
455-
// pending or in-flight epoll events we may have not captured in snapshot.
456-
// Stats queue doesn't need kicking as it is notified via a `timer_fd`.
457-
if balloon.is_activated() {
458-
info!("kick balloon {}.", id);
459-
balloon.process_virtio_queues().unwrap();
460-
}
461-
}
462-
TYPE_BLOCK => {
463-
// We only care about kicking virtio block.
464-
// If we need to kick vhost-user-block we can do nothing.
465-
if let Some(block) = virtio.as_mut_any().downcast_mut::<Block>() {
466-
// If device is activated, kick the block queue(s) to make up for any
467-
// pending or in-flight epoll events we may have not captured in
468-
// snapshot. No need to kick Ratelimiters
469-
// because they are restored 'unblocked' so
470-
// any inflight `timer_fd` events can be safely discarded.
471-
if block.is_activated() {
472-
info!("kick block {}.", id);
473-
block.process_virtio_queues().unwrap();
474-
}
475-
}
476-
}
477-
TYPE_NET => {
478-
let net = virtio.as_mut_any().downcast_mut::<Net>().unwrap();
479-
// If device is activated, kick the net queue(s) to make up for any
480-
// pending or in-flight epoll events we may have not captured in snapshot.
481-
// No need to kick Ratelimiters because they are restored 'unblocked' so
482-
// any inflight `timer_fd` events can be safely discarded.
483-
if net.is_activated() {
484-
info!("kick net {}.", id);
485-
net.process_virtio_queues().unwrap();
486-
}
487-
}
488-
TYPE_VSOCK => {
489-
// Vsock has complicated protocol that isn't resilient to any packet loss,
490-
// so for Vsock we don't support connection persistence through snapshot.
491-
// Any in-flight packets or events are simply lost.
492-
// Vsock is restored 'empty'.
493-
// The only reason we still `kick` it is to make guest process
494-
// `TRANSPORT_RESET_EVENT` event we sent during snapshot creation.
495-
let vsock = virtio
496-
.as_mut_any()
497-
.downcast_mut::<Vsock<VsockUnixBackend>>()
498-
.unwrap();
499-
if vsock.is_activated() {
500-
info!("kick vsock {id}.");
501-
vsock.signal_used_queue(0).unwrap();
502-
}
503-
}
504-
TYPE_RNG => {
505-
let entropy = virtio.as_mut_any().downcast_mut::<Entropy>().unwrap();
506-
if entropy.is_activated() {
507-
info!("kick entropy {id}.");
508-
entropy.process_virtio_queues().unwrap();
509-
}
510-
}
511-
_ => (),
512-
}
513-
Ok(())
514-
});
515-
}
516-
517437
#[cfg(target_arch = "aarch64")]
518438
pub fn virtio_device_info(&self) -> Vec<&MMIODeviceInfo> {
519439
let mut device_info = Vec::new();

src/vmm/src/device_manager/mod.rs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Use of this source code is governed by a BSD-style license that can be
66
// found in the THIRD-PARTY file.
77

8+
use std::convert::Infallible;
89
use std::fmt::Debug;
910
use std::sync::{Arc, Mutex};
1011

@@ -13,7 +14,7 @@ use event_manager::{MutEventSubscriber, SubscriberOps};
1314
#[cfg(target_arch = "x86_64")]
1415
use legacy::{LegacyDeviceError, PortIODeviceManager};
1516
use linux_loader::loader::Cmdline;
16-
use log::error;
17+
use log::{error, info};
1718
use mmio::{MMIODeviceManager, MmioError};
1819
use pci_mngr::{PciDevices, PciDevicesConstructorArgs, PciManagerError};
1920
use persist::{ACPIDeviceManagerConstructorArgs, MMIODevManagerConstructorArgs};
@@ -30,8 +31,14 @@ use crate::devices::legacy::RTCDevice;
3031
use crate::devices::legacy::serial::SerialOut;
3132
use crate::devices::legacy::{IER_RDA_BIT, IER_RDA_OFFSET, SerialDevice};
3233
use crate::devices::pseudo::BootTimer;
34+
use crate::devices::virtio::balloon::Balloon;
35+
use crate::devices::virtio::block::device::Block;
3336
use crate::devices::virtio::device::VirtioDevice;
37+
use crate::devices::virtio::net::Net;
38+
use crate::devices::virtio::rng::Entropy;
3439
use crate::devices::virtio::transport::mmio::{IrqTrigger, MmioTransport};
40+
use crate::devices::virtio::vsock::{TYPE_VSOCK, Vsock, VsockUnixBackend};
41+
use crate::devices::virtio::{TYPE_BALLOON, TYPE_BLOCK, TYPE_NET, TYPE_RNG};
3542
use crate::resources::VmResources;
3643
use crate::snapshot::Persist;
3744
use crate::vstate::memory::GuestMemoryMmap;
@@ -274,6 +281,106 @@ impl DeviceManager {
274281
self.pci_devices
275282
.attach_pci_segment(&self.resource_allocator)
276283
}
284+
285+
fn do_kick_device(virtio_device: Arc<Mutex<dyn VirtioDevice>>) {
286+
let mut device = virtio_device.lock().expect("Poisoned lock");
287+
match device.device_type() {
288+
TYPE_BALLOON => {
289+
let balloon = device.as_mut_any().downcast_mut::<Balloon>().unwrap();
290+
// If device is activated, kick the balloon queue(s) to make up for any
291+
// pending or in-flight epoll events we may have not captured in snapshot.
292+
// Stats queue doesn't need kicking as it is notified via a `timer_fd`.
293+
if balloon.is_activated() {
294+
info!("kick balloon {}.", balloon.id());
295+
balloon.process_virtio_queues().unwrap();
296+
}
297+
}
298+
TYPE_BLOCK => {
299+
// We only care about kicking virtio block.
300+
// If we need to kick vhost-user-block we can do nothing.
301+
if let Some(block) = device.as_mut_any().downcast_mut::<Block>() {
302+
// If device is activated, kick the block queue(s) to make up for any
303+
// pending or in-flight epoll events we may have not captured in
304+
// snapshot. No need to kick Ratelimiters
305+
// because they are restored 'unblocked' so
306+
// any inflight `timer_fd` events can be safely discarded.
307+
if block.is_activated() {
308+
info!("kick block {}.", block.id());
309+
block.process_virtio_queues().unwrap();
310+
}
311+
}
312+
}
313+
TYPE_NET => {
314+
let net = device.as_mut_any().downcast_mut::<Net>().unwrap();
315+
// If device is activated, kick the net queue(s) to make up for any
316+
// pending or in-flight epoll events we may have not captured in snapshot.
317+
// No need to kick Ratelimiters because they are restored 'unblocked' so
318+
// any inflight `timer_fd` events can be safely discarded.
319+
if net.is_activated() {
320+
info!("kick net {}.", net.id());
321+
net.process_virtio_queues().unwrap();
322+
}
323+
}
324+
TYPE_VSOCK => {
325+
// Vsock has complicated protocol that isn't resilient to any packet loss,
326+
// so for Vsock we don't support connection persistence through snapshot.
327+
// Any in-flight packets or events are simply lost.
328+
// Vsock is restored 'empty'.
329+
// The only reason we still `kick` it is to make guest process
330+
// `TRANSPORT_RESET_EVENT` event we sent during snapshot creation.
331+
let vsock = device
332+
.as_mut_any()
333+
.downcast_mut::<Vsock<VsockUnixBackend>>()
334+
.unwrap();
335+
if vsock.is_activated() {
336+
info!("kick vsock {}.", vsock.id());
337+
vsock.signal_used_queue(0).unwrap();
338+
}
339+
}
340+
TYPE_RNG => {
341+
let entropy = device.as_mut_any().downcast_mut::<Entropy>().unwrap();
342+
if entropy.is_activated() {
343+
info!("kick entropy {}.", entropy.id());
344+
entropy.process_virtio_queues().unwrap();
345+
}
346+
}
347+
_ => (),
348+
}
349+
}
350+
351+
/// Artificially kick VirtIO devices as if they had external events.
352+
pub fn kick_virtio_devices(&self) {
353+
info!("Artificially kick devices");
354+
// Go through MMIO VirtIO devices
355+
let _: Result<(), MmioError> = self.mmio_devices.for_each_virtio_device(|_, _, device| {
356+
let mmio_transport_locked = device.inner.lock().expect("Poisoned lock");
357+
Self::do_kick_device(mmio_transport_locked.device());
358+
Ok(())
359+
});
360+
}
361+
362+
fn do_mark_virtio_queue_memory_dirty(
363+
device: Arc<Mutex<dyn VirtioDevice>>,
364+
mem: &GuestMemoryMmap,
365+
) {
366+
// SAFETY:
367+
// This should never fail as we mark pages only if device has already been activated,
368+
// and the address validation was already performed on device activation.
369+
let mut locked_device = device.lock().expect("Poisoned lock");
370+
if locked_device.is_activated() {
371+
locked_device.mark_queue_memory_dirty(mem).unwrap()
372+
}
373+
}
374+
375+
/// Mark queue memory dirty for activated VirtIO devices
376+
pub fn mark_virtio_queue_memory_dirty(&self, mem: &GuestMemoryMmap) {
377+
// Go through MMIO VirtIO devices
378+
let _: Result<(), Infallible> = self.mmio_devices.for_each_virtio_device(|_, _, device| {
379+
let mmio_transport_locked = device.inner.lock().expect("Poisoned locked");
380+
Self::do_mark_virtio_queue_memory_dirty(mmio_transport_locked.device(), mem);
381+
Ok(())
382+
});
383+
}
277384
}
278385

279386
#[derive(Debug, Default, Clone, Serialize, Deserialize)]

0 commit comments

Comments
 (0)