virtio-devices: mem: Add snapshot/restore support

Adding the snapshot/restore support along with migration as well,
allowing a VM with virtio-mem devices attached to be properly
migrated.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2021-09-22 17:26:30 +02:00 committed by Bo Chen
parent 7bbcc0f849
commit d7115ec656
3 changed files with 73 additions and 34 deletions

View File

@ -33,12 +33,16 @@ use std::result;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::{Arc, Barrier, Mutex}; use std::sync::{Arc, Barrier, Mutex};
use versionize::{VersionMap, Versionize, VersionizeResult};
use versionize_derive::Versionize;
use vm_device::dma_mapping::ExternalDmaMapping; use vm_device::dma_mapping::ExternalDmaMapping;
use vm_memory::{ use vm_memory::{
Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic,
GuestMemoryError, GuestMemoryRegion, GuestMemoryError, GuestMemoryRegion,
}; };
use vm_migration::{Migratable, MigratableError, Pausable, Snapshottable, Transportable}; use vm_migration::{
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, VersionMapped,
};
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
const QUEUE_SIZE: u16 = 128; const QUEUE_SIZE: u16 = 128;
@ -159,8 +163,8 @@ struct VirtioMemResp {
unsafe impl ByteValued for VirtioMemResp {} unsafe impl ByteValued for VirtioMemResp {}
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, Default)] #[derive(Copy, Clone, Debug, Default, Versionize)]
struct VirtioMemConfig { pub struct VirtioMemConfig {
// Block size and alignment. Cannot change. // Block size and alignment. Cannot change.
block_size: u64, block_size: u64,
// Valid with VIRTIO_MEM_F_ACPI_PXM. Cannot change. // Valid with VIRTIO_MEM_F_ACPI_PXM. Cannot change.
@ -320,14 +324,14 @@ impl Request {
} }
pub struct ResizeSender { pub struct ResizeSender {
size: Arc<AtomicU64>, hotplugged_size: Arc<AtomicU64>,
tx: mpsc::Sender<Result<(), Error>>, tx: mpsc::Sender<Result<(), Error>>,
evt: EventFd, evt: EventFd,
} }
impl ResizeSender { impl ResizeSender {
fn size(&self) -> u64 { fn size(&self) -> u64 {
self.size.load(Ordering::Acquire) self.hotplugged_size.load(Ordering::Acquire)
} }
fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> { fn send(&self, r: Result<(), Error>) -> Result<(), mpsc::SendError<Result<(), Error>>> {
@ -338,7 +342,7 @@ impl ResizeSender {
impl Clone for ResizeSender { impl Clone for ResizeSender {
fn clone(&self) -> Self { fn clone(&self) -> Self {
ResizeSender { ResizeSender {
size: self.size.clone(), hotplugged_size: self.hotplugged_size.clone(),
tx: self.tx.clone(), tx: self.tx.clone(),
evt: self evt: self
.evt .evt
@ -349,18 +353,18 @@ impl Clone for ResizeSender {
} }
pub struct Resize { pub struct Resize {
size: Arc<AtomicU64>, hotplugged_size: Arc<AtomicU64>,
tx: mpsc::Sender<Result<(), Error>>, tx: mpsc::Sender<Result<(), Error>>,
rx: mpsc::Receiver<Result<(), Error>>, rx: mpsc::Receiver<Result<(), Error>>,
evt: EventFd, evt: EventFd,
} }
impl Resize { impl Resize {
pub fn new() -> io::Result<Self> { pub fn new(hotplugged_size: u64) -> io::Result<Self> {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
Ok(Resize { Ok(Resize {
size: Arc::new(AtomicU64::new(0)), hotplugged_size: Arc::new(AtomicU64::new(hotplugged_size)),
tx, tx,
rx, rx,
evt: EventFd::new(EFD_NONBLOCK)?, evt: EventFd::new(EFD_NONBLOCK)?,
@ -369,25 +373,28 @@ impl Resize {
pub fn new_resize_sender(&self) -> Result<ResizeSender, Error> { pub fn new_resize_sender(&self) -> Result<ResizeSender, Error> {
Ok(ResizeSender { Ok(ResizeSender {
size: self.size.clone(), hotplugged_size: self.hotplugged_size.clone(),
tx: self.tx.clone(), tx: self.tx.clone(),
evt: self.evt.try_clone().map_err(Error::EventFdTryCloneFail)?, evt: self.evt.try_clone().map_err(Error::EventFdTryCloneFail)?,
}) })
} }
pub fn work(&self, size: u64) -> Result<(), Error> { pub fn work(&self, desired_size: u64) -> Result<(), Error> {
self.size.store(size, Ordering::Release); self.hotplugged_size.store(desired_size, Ordering::Release);
self.evt.write(1).map_err(Error::EventFdWriteFail)?; self.evt.write(1).map_err(Error::EventFdWriteFail)?;
self.rx.recv().map_err(Error::MpscRecvFail)? self.rx.recv().map_err(Error::MpscRecvFail)?
} }
} }
struct BlocksState(Vec<bool>); #[derive(Clone, Versionize)]
pub struct BlocksState {
bitmap: Vec<bool>,
}
impl BlocksState { impl BlocksState {
fn is_range_state(&self, first_block_index: usize, nb_blocks: u16, plug: bool) -> bool { fn is_range_state(&self, first_block_index: usize, nb_blocks: u16, plug: bool) -> bool {
for state in self for state in self
.0 .bitmap
.iter() .iter()
.skip(first_block_index) .skip(first_block_index)
.take(nb_blocks as usize) .take(nb_blocks as usize)
@ -401,7 +408,7 @@ impl BlocksState {
fn set_range(&mut self, first_block_index: usize, nb_blocks: u16, plug: bool) { fn set_range(&mut self, first_block_index: usize, nb_blocks: u16, plug: bool) {
for state in self for state in self
.0 .bitmap
.iter_mut() .iter_mut()
.skip(first_block_index) .skip(first_block_index)
.take(nb_blocks as usize) .take(nb_blocks as usize)
@ -411,7 +418,7 @@ impl BlocksState {
} }
fn inner(&self) -> &Vec<bool> { fn inner(&self) -> &Vec<bool> {
&self.0 &self.bitmap
} }
} }
@ -740,6 +747,16 @@ pub enum VirtioMemMappingSource {
Device(u32), Device(u32),
} }
#[derive(Versionize)]
pub struct MemState {
pub avail_features: u64,
pub acked_features: u64,
pub config: VirtioMemConfig,
pub blocks_state: BlocksState,
}
impl VersionMapped for MemState {}
pub struct Mem { pub struct Mem {
common: VirtioCommon, common: VirtioCommon,
id: String, id: String,
@ -838,11 +855,9 @@ impl Mem {
seccomp_action, seccomp_action,
hugepages, hugepages,
dma_mapping_handlers: Arc::new(Mutex::new(BTreeMap::new())), dma_mapping_handlers: Arc::new(Mutex::new(BTreeMap::new())),
blocks_state: Arc::new(Mutex::new(BlocksState(vec![ blocks_state: Arc::new(Mutex::new(BlocksState {
false; bitmap: vec![false; (config.region_size / config.block_size) as usize],
(config.region_size / config.block_size) })),
as usize
]))),
exit_evt, exit_evt,
}) })
} }
@ -899,6 +914,22 @@ impl Mem {
Ok(()) Ok(())
} }
fn state(&self) -> MemState {
MemState {
avail_features: self.common.avail_features,
acked_features: self.common.acked_features,
config: *(self.config.lock().unwrap()),
blocks_state: self.blocks_state.lock().unwrap().clone(),
}
}
fn set_state(&mut self, state: &MemState) {
self.common.avail_features = state.avail_features;
self.common.acked_features = state.acked_features;
*(self.config.lock().unwrap()) = state.config;
*(self.blocks_state.lock().unwrap()) = state.blocks_state.clone();
}
} }
impl Drop for Mem { impl Drop for Mem {
@ -1007,6 +1038,15 @@ impl Snapshottable for Mem {
fn id(&self) -> String { fn id(&self) -> String {
self.id.clone() self.id.clone()
} }
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
Snapshot::new_from_versioned_state(&self.id(), &self.state())
}
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
self.set_state(&snapshot.to_versioned_state(&self.id)?);
Ok(())
}
} }
impl Transportable for Mem {} impl Transportable for Mem {}
impl Migratable for Mem {} impl Migratable for Mem {}

View File

@ -128,7 +128,6 @@ const GPIO_DEVICE_NAME_PREFIX: &str = "_gpio";
const CONSOLE_DEVICE_NAME: &str = "_console"; const CONSOLE_DEVICE_NAME: &str = "_console";
const DISK_DEVICE_NAME_PREFIX: &str = "_disk"; const DISK_DEVICE_NAME_PREFIX: &str = "_disk";
const FS_DEVICE_NAME_PREFIX: &str = "_fs"; const FS_DEVICE_NAME_PREFIX: &str = "_fs";
const MEM_DEVICE_NAME_PREFIX: &str = "_mem";
const BALLOON_DEVICE_NAME: &str = "_balloon"; const BALLOON_DEVICE_NAME: &str = "_balloon";
const NET_DEVICE_NAME_PREFIX: &str = "_net"; const NET_DEVICE_NAME_PREFIX: &str = "_net";
const PMEM_DEVICE_NAME_PREFIX: &str = "_pmem"; const PMEM_DEVICE_NAME_PREFIX: &str = "_pmem";
@ -2670,20 +2669,19 @@ impl DeviceManager {
let mm = self.memory_manager.clone(); let mm = self.memory_manager.clone();
let mm = mm.lock().unwrap(); let mm = mm.lock().unwrap();
for (_memory_zone_id, memory_zone) in mm.memory_zones().iter() { for (memory_zone_id, memory_zone) in mm.memory_zones().iter() {
if let Some(virtio_mem_zone) = memory_zone.virtio_mem_zone() { if let Some(virtio_mem_zone) = memory_zone.virtio_mem_zone() {
let id = self.next_device_name(MEM_DEVICE_NAME_PREFIX)?; info!("Creating virtio-mem device: id = {}", memory_zone_id);
info!("Creating virtio-mem device: id = {}", id);
#[cfg(all(target_arch = "x86_64", not(feature = "acpi")))] #[cfg(all(target_arch = "x86_64", not(feature = "acpi")))]
let node_id: Option<u16> = None; let node_id: Option<u16> = None;
#[cfg(any(target_arch = "aarch64", feature = "acpi"))] #[cfg(any(target_arch = "aarch64", feature = "acpi"))]
let node_id = numa_node_id_from_memory_zone_id(&self.numa_nodes, _memory_zone_id) let node_id = numa_node_id_from_memory_zone_id(&self.numa_nodes, memory_zone_id)
.map(|i| i as u16); .map(|i| i as u16);
let virtio_mem_device = Arc::new(Mutex::new( let virtio_mem_device = Arc::new(Mutex::new(
virtio_devices::Mem::new( virtio_devices::Mem::new(
id.clone(), memory_zone_id.clone(),
virtio_mem_zone.region(), virtio_mem_zone.region(),
virtio_mem_zone virtio_mem_zone
.resize_handler() .resize_handler()
@ -2705,16 +2703,16 @@ impl DeviceManager {
devices.push(( devices.push((
Arc::clone(&virtio_mem_device) as VirtioDeviceArc, Arc::clone(&virtio_mem_device) as VirtioDeviceArc,
false, false,
id.clone(), memory_zone_id.clone(),
)); ));
// Fill the device tree with a new node. In case of restore, we // Fill the device tree with a new node. In case of restore, we
// know there is nothing to do, so we can simply override the // know there is nothing to do, so we can simply override the
// existing entry. // existing entry.
self.device_tree self.device_tree.lock().unwrap().insert(
.lock() memory_zone_id.clone(),
.unwrap() device_node!(memory_zone_id, virtio_mem_device),
.insert(id.clone(), device_node!(id, virtio_mem_device)); );
} }
} }

View File

@ -698,11 +698,12 @@ impl MemoryManager {
virtio_mem_regions.push(region.clone()); virtio_mem_regions.push(region.clone());
let hotplugged_size = zone.hotplugged_size.unwrap_or(0);
memory_zone.virtio_mem_zone = Some(VirtioMemZone { memory_zone.virtio_mem_zone = Some(VirtioMemZone {
region, region,
resize_handler: virtio_devices::Resize::new() resize_handler: virtio_devices::Resize::new(hotplugged_size)
.map_err(Error::EventFdFail)?, .map_err(Error::EventFdFail)?,
hotplugged_size: zone.hotplugged_size.unwrap_or(0), hotplugged_size,
hugepages: zone.hugepages, hugepages: zone.hugepages,
}); });