devices: Added suspendable.rs with the Suspendable trait.

Suspendable trait will be implemented for structs that will
snapshot/restore/sleep/wake, related to suspend/resume with snapshotting.
Added test generation for suspendable trait.

Bug=b:232437513
Test=cargo test

Change-Id: I071bad026c15ce346b6657871e7578528768bfc2
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3842812
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Frederick Mayle <fmayle@google.com>
Reviewed-by: Noah Gold <nkgold@google.com>
Commit-Queue: Elie Kheirallah <khei@google.com>
Reviewed-by: Steven Moreland <smoreland@google.com>
This commit is contained in:
Elie Kheirallah 2022-08-19 22:54:38 +00:00 committed by crosvm LUCI
parent 21c739216e
commit 7a11fa44d1
2 changed files with 171 additions and 0 deletions

View file

@ -30,6 +30,7 @@ mod serial;
pub mod serial_device;
#[cfg(feature = "tpm")]
mod software_tpm;
mod suspendable;
mod sys;
pub mod virtio;
#[cfg(all(feature = "vtpm", target_arch = "x86_64"))]
@ -101,6 +102,8 @@ pub use self::serial_device::SerialParameters;
pub use self::serial_device::SerialType;
#[cfg(feature = "tpm")]
pub use self::software_tpm::SoftwareTpm;
pub use self::suspendable::DeviceState;
pub use self::suspendable::Suspendable;
pub use self::virtio::VirtioMmioDevice;
pub use self::virtio::VirtioPciDevice;
#[cfg(all(feature = "vtpm", target_arch = "x86_64"))]

168
devices/src/suspendable.rs Normal file
View file

@ -0,0 +1,168 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Trait to suspend virtual hardware.
use anyhow::anyhow;
use anyhow::Result as AnyhowResult;
use serde::Deserialize;
use serde::Serialize;
#[derive(Copy, Clone, Serialize, Deserialize)]
pub enum DeviceState {
Awake,
Sleep,
}
/// This trait provides the functions required for a device to implement to successfully
/// suspend/resume in crosvm.
pub trait Suspendable {
/// Save the device state in an image that can be restored.
fn snapshot(&self) -> AnyhowResult<String> {
Ok(format!(
"Suspendable::snapshot not implemented for {}",
std::any::type_name::<Self>()
))
}
/// Load a saved snapshot of an image.
fn restore(&mut self, _data: &str) -> AnyhowResult<()> {
Err(anyhow!(
"Suspendable::restore not implemented for {}",
std::any::type_name::<Self>()
))
}
/// Stop all threads related to the device.
/// Sleep should be idempotent.
fn sleep(&mut self) -> AnyhowResult<()> {
Err(anyhow!(
"Suspendable::sleep not implemented for {}",
std::any::type_name::<Self>()
))
}
/// Create/Resume all threads related to the device.
/// Wake should be idempotent.
fn wake(&mut self) -> AnyhowResult<()> {
Err(anyhow!(
"Suspendable::wake not implemented for {}",
std::any::type_name::<Self>()
))
}
}
// General tests that should pass on all suspendables.
// Do implement device-specific tests to validate the functionality of the device.
// Those tests are not a replacement for regular tests. Only an extension specific to the trait's
// basic functionality.
#[macro_export]
macro_rules! suspendable_tests {
($($name:ident: $expr:expr,)*) => {
$(
mod $name {
use super::*;
#[test]
fn test_sleep_idempotent() {
let unit = &mut $expr;
let res = unit.sleep();
let res2 = unit.sleep();
match res {
Ok(()) => (),
Err(e) => println!("{}", e),
}
match res2 {
Ok(()) => (),
Err(e) => println!("idempotent: {}", e),
}
}
#[test]
fn test_snapshot_restore() {
let unit = &mut $expr;
let snap = unit.snapshot();
match snap {
Ok(snap_res) => {
let res = unit.restore(&snap_res);
match res {
Ok(()) => (),
Err(e) => println!("{}", e),
}
},
Err(e) => println!("{}", e),
}
}
#[test]
fn test_sleep_snapshot() {
let unit = &mut $expr;
let sleep_result = unit.sleep();
let snap_result = unit.snapshot();
match sleep_result {
Ok(()) => (),
Err(e) => println!("{}", e),
}
match snap_result {
Ok(_res) => (),
Err(e) => println!("{}", e),
}
}
#[test]
fn test_sleep_snapshot_restore_wake() {
let unit = &mut $expr;
let sleep_result = unit.sleep();
let snap_result = unit.snapshot();
match sleep_result {
Ok(()) => (),
Err(e) => println!("{}", e),
}
match snap_result {
Ok(snap_res) => {
let res = unit.restore(&snap_res);
match res {
Ok(()) => (),
Err(e) => println!("{}", e),
}
},
Err(e) => println!("{}", e),
}
let wake_res = unit.wake();
match wake_res {
Ok(()) => (),
Err(e) => println!("{}", e),
}
}
#[test]
fn test_sleep_snapshot_wake() {
let unit = &mut $expr;
let sleep_result = unit.sleep();
let snap_result = unit.snapshot();
match sleep_result {
Ok(()) => (),
Err(e) => println!("{}", e),
}
match snap_result {
Ok(_snap_res) => (),
Err(e) => println!("{}", e),
}
let wake_res = unit.wake();
match wake_res {
Ok(()) => (),
Err(e) => println!("{}", e),
}
}
#[test]
fn test_snapshot() {
let unit = &mut $expr;
let snap_result = unit.snapshot();
match snap_result {
Ok(_snap_res) => (),
Err(e) => println!("{}", e),
}
}
}
)*
}
}