From ab1dddb62a69a6e948b5ea2c808a89155c85f9bd Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Thu, 26 Mar 2026 20:09:01 +0100 Subject: [PATCH] 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 --- performance-metrics/src/util.rs | 45 ++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/performance-metrics/src/util.rs b/performance-metrics/src/util.rs index dcc225750..98fd56828 100644 --- a/performance-metrics/src/util.rs +++ b/performance-metrics/src/util.rs @@ -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 {