performance-metrics: Add QCOW2 tempfile helpers

Add qcow_tempfile() which creates a QCOW2 v3 image with all clusters
allocated via QcowFile::new plus sequential writes, then reopens it
as QcowDiskSync. Add QCOW_CLUSTER_SIZE constant for the default
64 KiB cluster size.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-03-26 20:09:01 +01:00
committed by Rob Bradford
parent 4a607ed82d
commit ab1dddb62a

View File

@@ -4,14 +4,18 @@
//! Shared benchmark helpers.
use std::io::ErrorKind;
use std::io::{ErrorKind, Seek, SeekFrom, Write};
use std::thread;
use std::time::Duration;
use block::qcow::{QcowFile, RawFile};
use block::qcow_async::QcowDiskAsync;
use block::qcow_sync::QcowDiskSync;
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::tempfile::TempFile;
pub const BLOCK_SIZE: u64 = 4096;
pub const QCOW_CLUSTER_SIZE: u64 = 65536;
/// Create a temporary file pre sized to hold `num_blocks` blocks.
pub fn sized_tempfile(num_blocks: usize) -> TempFile {
@@ -22,6 +26,45 @@ pub fn sized_tempfile(num_blocks: usize) -> TempFile {
tmp
}
/// Create a QCOW2 image with `num_clusters` allocated clusters and return
/// 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.
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 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");
}
qcow.flush().expect("flush failed");
tmp
}
/// Create a QCOW2 image with `num_clusters` allocated clusters opened
/// via `QcowDiskSync` (blocking I/O backend).
pub fn qcow_tempfile(num_clusters: usize) -> (TempFile, QcowDiskSync) {
let tmp = create_qcow_tempfile(num_clusters);
let disk = QcowDiskSync::new(tmp.as_file().try_clone().unwrap(), false, false, true)
.expect("failed to open QCOW2 via QcowDiskSync");
(tmp, disk)
}
/// Create a QCOW2 image with `num_clusters` allocated clusters opened
/// via `QcowDiskAsync` (io_uring backend).
pub fn qcow_async_tempfile(num_clusters: usize) -> (TempFile, QcowDiskAsync) {
let tmp = create_qcow_tempfile(num_clusters);
let disk = QcowDiskAsync::new(tmp.as_file().try_clone().unwrap(), false, false, true)
.expect("failed to open QCOW2 via QcowDiskAsync");
(tmp, disk)
}
/// Spin and wait until the given eventfd becomes readable.
pub fn wait_for_eventfd(notifier: &EventFd) {
loop {