Skip to content

Commit 72b8622

Browse files
committed
fix: snapshot: disallow snapshots with vhost-user devices
Previously we were incorrectly allow snapshots to be created even with vhost-user devices by simply ignoring them. Now we correctly return an error if we detect vhost-user devices. Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent 53b382a commit 72b8622

3 files changed

Lines changed: 56 additions & 20 deletions

File tree

src/vmm/src/device_manager/persist.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ use crate::devices::virtio::vsock::persist::{
3737
VsockConstructorArgs, VsockState, VsockUdsConstructorArgs,
3838
};
3939
use crate::devices::virtio::vsock::{Vsock, VsockUnixBackend};
40-
use crate::logger::warn;
4140
use crate::mmds::data_store::MmdsVersion;
4241
use crate::resources::VmResources;
4342
use crate::snapshot::Persist;
@@ -256,20 +255,17 @@ impl<'a> Persist<'a> for MMIODeviceManager {
256255
// Both virtio-block and vhost-user-block share same device type.
257256
VirtioDeviceType::Block => {
258257
let block = locked_device.as_mut_any().downcast_mut::<Block>().unwrap();
259-
if block.is_vhost_user() {
260-
warn!(
261-
"Skipping vhost-user-block device. VhostUserBlock does not support \
262-
snapshotting yet"
263-
);
264-
} else {
265-
let device_state = block.save();
266-
states.block_devices.push(VirtioDeviceState {
267-
device_id,
268-
device_state,
269-
transport_state,
270-
device_info,
271-
});
272-
}
258+
assert!(
259+
!block.is_vhost_user(),
260+
"vhost-user-block does not support snapshotting yet"
261+
);
262+
let device_state = block.save();
263+
states.block_devices.push(VirtioDeviceState {
264+
device_id,
265+
device_state,
266+
transport_state,
267+
device_info,
268+
});
273269
}
274270
VirtioDeviceType::Net => {
275271
let net = locked_device.as_mut_any().downcast_mut::<Net>().unwrap();

src/vmm/src/lib.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ use crate::devices::virtio::balloon::{
141141
};
142142
use crate::devices::virtio::block::BlockError;
143143
use crate::devices::virtio::block::device::Block;
144-
use crate::devices::virtio::device::VirtioDeviceType;
144+
use crate::devices::virtio::device::{VirtioDevice, VirtioDeviceType};
145145
use crate::devices::virtio::mem::device::VirtioMem;
146146
use crate::devices::virtio::mem::{VIRTIO_MEM_DEV_ID, VirtioMemError, VirtioMemStatus};
147147
use crate::devices::virtio::net::Net;
@@ -449,6 +449,33 @@ impl Vmm {
449449
}
450450
}
451451

452+
/// Check if the VM has any devices without snapshot support
453+
pub fn check_unsnapshottable_devices(&self) -> Result<(), MicrovmStateError> {
454+
let mut tuples = Vec::new();
455+
self.device_manager
456+
.for_each_virtio_device(|device_type, device| {
457+
if let VirtioDeviceType::Block = device_type
458+
&& let Some(b) = device.as_any().downcast_ref::<Block>()
459+
{
460+
if b.is_vhost_user() {
461+
tuples.push(("vhost-user-block", b.id().to_owned()));
462+
}
463+
}
464+
});
465+
if tuples.is_empty() {
466+
return Ok(());
467+
} else {
468+
let msg = tuples
469+
.iter()
470+
.map(|(t, id)| format!("{t}(id: {id})"))
471+
.collect::<Vec<_>>()
472+
.join(",");
473+
return Err(MicrovmStateError::NotAllowed(format!(
474+
"Devices without snapshot support are present: {msg}"
475+
)));
476+
}
477+
}
478+
452479
/// Starts the microVM vcpus.
453480
///
454481
/// # Errors
@@ -555,7 +582,8 @@ impl Vmm {
555582

556583
/// Saves the state of a paused Microvm.
557584
pub fn save_state(&mut self, vm_info: &VmInfo) -> Result<MicrovmState, MicrovmStateError> {
558-
use self::MicrovmStateError::SaveVmState;
585+
self.check_unsnapshottable_devices()?;
586+
559587
// We need to save device state before saving KVM state.
560588
// Some devices, (at the time of writing this comment block device with async engine)
561589
// might modify the VirtIO transport and send an interrupt to the guest. If we save KVM
@@ -567,13 +595,17 @@ impl Vmm {
567595
let vm_state = {
568596
#[cfg(target_arch = "x86_64")]
569597
{
570-
self.vm.save_state().map_err(SaveVmState)?
598+
self.vm
599+
.save_state()
600+
.map_err(MicrovmStateError::SaveVmState)?
571601
}
572602
#[cfg(target_arch = "aarch64")]
573603
{
574604
let mpidrs = construct_kvm_mpidrs(&vcpu_states);
575605

576-
self.vm.save_state(&mpidrs).map_err(SaveVmState)?
606+
self.vm
607+
.save_state(&mpidrs)
608+
.map_err(MicrovmStateError::SaveVmState)?
577609
}
578610
};
579611

tests/integration_tests/functional/test_drive_vhost_user.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ def _check_drives(test_microvm, assert_dict, keys_array):
8888
def test_vhost_user_block(uvm_vhost_user_booted_ro):
8989
"""
9090
This test simply tries to boot a VM with
91-
vhost-user-block as a root device.
91+
vhost-user-block as a root device and then
92+
tries to snapshot it.
9293
"""
9394

9495
vm = uvm_vhost_user_booted_ro
@@ -105,6 +106,13 @@ def test_vhost_user_block(uvm_vhost_user_booted_ro):
105106
_check_drives(vm, assert_dict, assert_dict.keys())
106107
vhost_user_block_metrics.validate(vm)
107108

109+
with pytest.raises(
110+
RuntimeError, match="Devices without snapshot support are present: vhost-user-block\(id: rootfs\)"
111+
):
112+
vm.api.snapshot_create.put(
113+
mem_file_path="memfile", snapshot_path="statefile", snapshot_type="Full"
114+
)
115+
108116

109117
def test_vhost_user_block_read_write(uvm_vhost_user_booted_rw):
110118
"""

0 commit comments

Comments
 (0)