mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
performance-metrics: Switch to QcowDisk
Switch from QcowFile to QcowDisk taking advantage of QcowTempDisk where appropriate. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
@@ -8,6 +8,7 @@ version = "0.1.0"
|
||||
[features]
|
||||
default = []
|
||||
io_uring = ["dep:io-uring"]
|
||||
test-utils = []
|
||||
|
||||
[dependencies]
|
||||
bitflags = { workspace = true }
|
||||
|
||||
@@ -12,27 +12,31 @@ pub mod internal;
|
||||
pub mod worker;
|
||||
|
||||
use std::fs::File;
|
||||
#[cfg(test)]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use std::io::Seek;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io};
|
||||
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
||||
#[cfg(test)]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
use self::internal::backing::shared_backing_from;
|
||||
use self::internal::metadata::{BackingRead, QcowMetadata};
|
||||
use self::internal::qcow_raw_file::QcowRawFile;
|
||||
#[cfg(test)]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use self::internal::{BackingFileConfig, Error as QcowError, QcowHeader};
|
||||
use self::internal::{MAX_NESTING_DEPTH, RawFile, parse_qcow};
|
||||
#[cfg(feature = "io_uring")]
|
||||
use self::worker::async_uring::QcowAsync;
|
||||
use self::worker::sync::QcowSync;
|
||||
use crate::async_io::{AsyncIo, BorrowedDiskFd, DiskFileError, GuestMemoryTarget};
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use crate::async_io::GuestMemoryTarget;
|
||||
use crate::async_io::{AsyncIo, BorrowedDiskFd, DiskFileError};
|
||||
use crate::disk_file;
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use crate::disk_file::AsyncDiskFile;
|
||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||
|
||||
@@ -106,6 +110,7 @@ impl QcowDisk {
|
||||
}
|
||||
|
||||
/// Synchronous write convenience for tests and benchmarks.
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub fn write_all_at(&self, offset: u64, data: &[u8]) {
|
||||
let mut async_io = self.create_async_io(1).unwrap();
|
||||
let mem =
|
||||
@@ -121,7 +126,7 @@ impl QcowDisk {
|
||||
}
|
||||
|
||||
/// Writes a fresh qcow2 layout into `file`
|
||||
#[cfg(test)]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub(crate) fn create_image(
|
||||
file: &File,
|
||||
virtual_size: u64,
|
||||
@@ -152,13 +157,13 @@ pub(crate) fn create_image(
|
||||
}
|
||||
|
||||
/// Helper struct to create a new qcow2 image in a temporary file.
|
||||
#[cfg(test)]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub struct QcowTempDisk {
|
||||
tmp: TempFile,
|
||||
disk: QcowDisk,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
impl QcowTempDisk {
|
||||
/// Creates a new qcow2 image in a temporary file with optional
|
||||
/// backing file. Flags are passed to QcowDisk::new.
|
||||
|
||||
@@ -5,7 +5,7 @@ name = "performance-metrics"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
block = { path = "../block", features = ["io_uring"] }
|
||||
block = { path = "../block", features = ["io_uring", "test-utils"] }
|
||||
clap = { workspace = true, features = ["wrap_help"] }
|
||||
libc = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive", "rc"] }
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//! Shared benchmark helpers.
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{ErrorKind, Seek, SeekFrom, Write};
|
||||
use std::io::ErrorKind;
|
||||
use std::os::unix::fs::FileExt;
|
||||
use std::process::Command;
|
||||
use std::sync::Arc;
|
||||
@@ -15,8 +15,8 @@ use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use block::async_io::{AsyncIo, GuestMemoryTarget};
|
||||
use block::formats::qcow::QcowDisk;
|
||||
use block::formats::qcow::internal::{BackingFileConfig, ImageType, QcowFile, RawFile};
|
||||
use block::formats::qcow::internal::{BackingFileConfig, ImageType};
|
||||
use block::formats::qcow::{QcowDisk, QcowTempDisk};
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
@@ -37,21 +37,18 @@ pub fn sized_tempfile(num_blocks: usize) -> TempFile {
|
||||
/// the tempfile handle.
|
||||
///
|
||||
/// Each cluster is default QCOW2 cluster size of 64 KiB. The image is
|
||||
/// created via `QcowFile::new` then populated with writes so that the
|
||||
/// clusters are actually allocated in the L2 / refcount tables.
|
||||
/// created via `QcowTempDisk::new` then populated with writes via the
|
||||
/// synchronous AsyncIo backend so that the clusters are actually
|
||||
/// allocated in the L2 / refcount tables.
|
||||
fn create_qcow_tempfile(num_clusters: usize) -> TempFile {
|
||||
let tmp = TempFile::new().expect("failed to create tempfile");
|
||||
let virtual_size = QCOW_CLUSTER_SIZE * num_clusters as u64;
|
||||
let raw = RawFile::new(tmp.as_file().try_clone().unwrap(), false);
|
||||
let mut qcow = QcowFile::new(raw, 3, virtual_size, true).expect("failed to create QCOW2 file");
|
||||
let tmp_disk = QcowTempDisk::new(virtual_size, None, false, true, false)
|
||||
.expect("failed to create QCOW2 file");
|
||||
let buf = vec![0xA5u8; QCOW_CLUSTER_SIZE as usize];
|
||||
for i in 0..num_clusters {
|
||||
qcow.seek(SeekFrom::Start(i as u64 * QCOW_CLUSTER_SIZE))
|
||||
.expect("seek failed");
|
||||
qcow.write_all(&buf).expect("write failed");
|
||||
for i in 0..num_clusters as u64 {
|
||||
tmp_disk.disk().write_all_at(i * QCOW_CLUSTER_SIZE, &buf);
|
||||
}
|
||||
qcow.flush().expect("flush failed");
|
||||
tmp
|
||||
tmp_disk.into_tempfile()
|
||||
}
|
||||
|
||||
/// Create a QCOW2 image with `num_clusters` allocated clusters opened
|
||||
@@ -196,11 +193,10 @@ pub fn drain_async_completions(async_io: &mut dyn AsyncIo, count: usize) {
|
||||
/// Create an empty QCOW2 image sized for `num_clusters` clusters.
|
||||
/// No data clusters are allocated.
|
||||
fn create_empty_qcow_tempfile(num_clusters: usize) -> TempFile {
|
||||
let tmp = TempFile::new().expect("failed to create tempfile");
|
||||
let virtual_size = QCOW_CLUSTER_SIZE * num_clusters as u64;
|
||||
let raw = RawFile::new(tmp.as_file().try_clone().unwrap(), false);
|
||||
QcowFile::new(raw, 3, virtual_size, true).expect("failed to create qcow2 file");
|
||||
tmp
|
||||
QcowTempDisk::new(virtual_size, None, false, true, false)
|
||||
.expect("failed to create qcow2 file")
|
||||
.into_tempfile()
|
||||
}
|
||||
|
||||
/// Empty QCOW2 opened via QcowDisk with synchronous backend.
|
||||
@@ -241,16 +237,13 @@ fn create_overlay_tempfiles(num_clusters: usize) -> (TempFile, TempFile) {
|
||||
}
|
||||
}
|
||||
|
||||
let overlay = TempFile::new().expect("failed to create overlay tempfile");
|
||||
{
|
||||
let raw = RawFile::new(overlay.as_file().try_clone().unwrap(), false);
|
||||
let backing_config = BackingFileConfig {
|
||||
path: backing.as_path().to_str().unwrap().to_string(),
|
||||
format: Some(ImageType::Raw),
|
||||
};
|
||||
QcowFile::new_from_backing(raw, 3, virtual_size, &backing_config, true)
|
||||
.expect("failed to create overlay qcow2");
|
||||
}
|
||||
let backing_config = BackingFileConfig {
|
||||
path: backing.as_path().to_str().unwrap().to_string(),
|
||||
format: Some(ImageType::Raw),
|
||||
};
|
||||
let overlay = QcowTempDisk::new(virtual_size, Some(&backing_config), false, true, false)
|
||||
.expect("failed to create overlay qcow2")
|
||||
.into_tempfile();
|
||||
|
||||
(backing, overlay)
|
||||
}
|
||||
@@ -360,17 +353,14 @@ pub const L2_ENTRIES_PER_TABLE: usize = QCOW_CLUSTER_SIZE as usize / 8;
|
||||
/// spanning `num_l2_tables` L2 tables.
|
||||
fn create_sparse_qcow_tempfile(num_l2_tables: usize) -> TempFile {
|
||||
let virtual_size = QCOW_CLUSTER_SIZE * (num_l2_tables as u64 * L2_ENTRIES_PER_TABLE as u64);
|
||||
let tmp = TempFile::new().expect("failed to create tempfile");
|
||||
let raw = RawFile::new(tmp.as_file().try_clone().unwrap(), false);
|
||||
let mut qcow = QcowFile::new(raw, 3, virtual_size, true).expect("failed to create qcow2 file");
|
||||
let tmp_disk = QcowTempDisk::new(virtual_size, None, false, true, false)
|
||||
.expect("failed to create qcow2 file");
|
||||
let buf = vec![0xA5u8; QCOW_CLUSTER_SIZE as usize];
|
||||
for i in 0..num_l2_tables {
|
||||
let offset = i as u64 * L2_ENTRIES_PER_TABLE as u64 * QCOW_CLUSTER_SIZE;
|
||||
qcow.seek(SeekFrom::Start(offset)).expect("seek failed");
|
||||
qcow.write_all(&buf).expect("write failed");
|
||||
for i in 0..num_l2_tables as u64 {
|
||||
let offset = i * L2_ENTRIES_PER_TABLE as u64 * QCOW_CLUSTER_SIZE;
|
||||
tmp_disk.disk().write_all_at(offset, &buf);
|
||||
}
|
||||
qcow.flush().expect("flush failed");
|
||||
tmp
|
||||
tmp_disk.into_tempfile()
|
||||
}
|
||||
|
||||
/// Sparse QCOW2 opened via QcowDisk with synchronous backend.
|
||||
|
||||
Reference in New Issue
Block a user