diff --git a/block/src/lib.rs b/block/src/lib.rs index 34c96eea7..72210302a 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -788,6 +788,7 @@ pub trait AsyncAdaptor { } } +#[derive(PartialEq, Eq, Debug)] pub enum ImageType { FixedVhd, Qcow2, diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs index e76c07c15..d802291c1 100644 --- a/block/src/qcow_sync.rs +++ b/block/src/qcow_sync.rs @@ -29,10 +29,19 @@ pub struct QcowDiskSync { } impl QcowDiskSync { - pub fn new(file: File, direct_io: bool) -> QcowResult { - Ok(QcowDiskSync { - qcow_file: Arc::new(Mutex::new(QcowFile::from(RawFile::new(file, direct_io))?)), - }) + pub fn new(file: File, direct_io: bool, backing_files: bool) -> QcowResult { + if backing_files { + Ok(QcowDiskSync { + qcow_file: Arc::new(Mutex::new(QcowFile::from(RawFile::new(file, direct_io))?)), + }) + } else { + Ok(QcowDiskSync { + qcow_file: Arc::new(Mutex::new(QcowFile::from_with_nesting_depth( + RawFile::new(file, direct_io), + 0, + )?)), + }) + } } } diff --git a/cloud-hypervisor/tests/integration.rs b/cloud-hypervisor/tests/integration.rs index 594a1140b..5566caf98 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -3422,6 +3422,7 @@ mod common_parallel { disable_io_uring: bool, disable_aio: bool, verify_os_disk: bool, + backing_files: bool, ) { let disk_config = UbuntuDiskConfig::new(image_name.to_string()); let guest = Guest::new(Box::new(disk_config)); @@ -3448,8 +3449,9 @@ mod common_parallel { .args([ "--disk", format!( - "path={}", - guest.disk_config.disk(DiskType::OperatingSystem).unwrap() + "path={},backing_files={}", + guest.disk_config.disk(DiskType::OperatingSystem).unwrap(), + if backing_files { "on"} else {"off"} ) .as_str(), format!( @@ -3528,17 +3530,17 @@ mod common_parallel { #[test] fn test_virtio_block_io_uring() { - _test_virtio_block(FOCAL_IMAGE_NAME, false, true, false); + _test_virtio_block(FOCAL_IMAGE_NAME, false, true, false, false); } #[test] fn test_virtio_block_aio() { - _test_virtio_block(FOCAL_IMAGE_NAME, true, false, false); + _test_virtio_block(FOCAL_IMAGE_NAME, true, false, false, false); } #[test] fn test_virtio_block_sync() { - _test_virtio_block(FOCAL_IMAGE_NAME, true, true, false); + _test_virtio_block(FOCAL_IMAGE_NAME, true, true, false, false); } fn run_qemu_img(path: &std::path::Path, args: &[&str]) -> std::process::Output { @@ -3768,22 +3770,28 @@ mod common_parallel { #[test] fn test_virtio_block_qcow2() { - _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2, false, false, true); + _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2, false, false, true, false); } #[test] fn test_virtio_block_qcow2_zlib() { - _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZLIB, false, false, true); + _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZLIB, false, false, true, false); } #[test] fn test_virtio_block_qcow2_zstd() { - _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZSTD, false, false, true); + _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_ZSTD, false, false, true, false); } #[test] fn test_virtio_block_qcow2_backing_zstd_file() { - _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_BACKING_ZSTD_FILE, false, false, true); + _test_virtio_block( + JAMMY_IMAGE_NAME_QCOW2_BACKING_ZSTD_FILE, + false, + false, + true, + true, + ); } #[test] @@ -3793,12 +3801,19 @@ mod common_parallel { false, false, true, + true, ); } #[test] fn test_virtio_block_qcow2_backing_raw_file() { - _test_virtio_block(JAMMY_IMAGE_NAME_QCOW2_BACKING_RAW_FILE, false, false, true); + _test_virtio_block( + JAMMY_IMAGE_NAME_QCOW2_BACKING_RAW_FILE, + false, + false, + true, + true, + ); } /// Configuration for QCOW2 multiqueue test image setup @@ -3873,7 +3888,15 @@ mod common_parallel { "path={}", guest.disk_config.disk(DiskType::CloudInit).unwrap() ), - &format!("path={},num_queues=8", test_image_path.to_str().unwrap()), + &format!( + "path={},num_queues=8,backing_files={}", + test_image_path.to_str().unwrap(), + if initial_backing_checksum.is_some() { + "on" + } else { + "off" + }, + ), ]) .default_net() .capture_output() @@ -4491,7 +4514,7 @@ mod common_parallel { .output() .expect("Expect generating VHD image from RAW image"); - _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false, false, false); + _test_virtio_block(FOCAL_IMAGE_NAME_VHD, false, false, false, false); } #[test] @@ -4515,7 +4538,7 @@ mod common_parallel { .output() .expect("Expect generating dynamic VHDx image from RAW image"); - _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false, false, true); + _test_virtio_block(FOCAL_IMAGE_NAME_VHDX, false, false, true, false); } #[test] diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 78d6f9f1e..e1f9213d3 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -1093,7 +1093,7 @@ impl DiskConfig { ops_size=,ops_one_time_burst=,ops_refill_time=,\ id=,pci_segment=,rate_limit_group=,\ queue_affinity=,\ - serial="; + serial=,backing_files=on|off"; pub fn parse(disk: &str) -> Result { let mut parser = OptionParser::new(); @@ -1118,7 +1118,8 @@ impl DiskConfig { .add("pci_segment") .add("serial") .add("rate_limit_group") - .add("queue_affinity"); + .add("queue_affinity") + .add("backing_files"); parser.parse(disk).map_err(Error::ParseDisk)?; let path = parser.get("path").map(PathBuf::from); @@ -1203,6 +1204,12 @@ impl DiskConfig { }) .collect() }); + let backing_files = parser + .convert::("backing_files") + .map_err(Error::ParseDisk)? + .unwrap_or(Toggle(false)) + .0; + let bw_tb_config = if bw_size != 0 && bw_refill_time != 0 { Some(TokenBucketConfig { size: bw_size, @@ -1247,6 +1254,7 @@ impl DiskConfig { pci_segment, serial, queue_affinity, + backing_files, }) } @@ -3414,6 +3422,7 @@ mod unit_tests { pci_segment: 0, serial: None, queue_affinity: None, + backing_files: false, } } diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 6d465047b..9b2a32455 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -2657,6 +2657,10 @@ impl DeviceManager { let image_type = detect_image_type(&mut file).map_err(DeviceManagerError::DetectImageType)?; + if image_type != ImageType::Qcow2 && disk_cfg.backing_files { + warn!("Enabling backing_files option only applies for QCOW2 files"); + } + let image = match image_type { ImageType::FixedVhd => { // Use asynchronous backend relying on io_uring if the @@ -2710,7 +2714,7 @@ impl DeviceManager { ImageType::Qcow2 => { info!("Using synchronous QCOW2 disk file"); Box::new( - QcowDiskSync::new(file, disk_cfg.direct) + QcowDiskSync::new(file, disk_cfg.direct, disk_cfg.backing_files) .map_err(DeviceManagerError::CreateQcowDiskSync)?, ) as Box } diff --git a/vmm/src/vm_config.rs b/vmm/src/vm_config.rs index 9c28e536d..a71cca7be 100644 --- a/vmm/src/vm_config.rs +++ b/vmm/src/vm_config.rs @@ -284,6 +284,8 @@ pub struct DiskConfig { pub serial: Option, #[serde(default)] pub queue_affinity: Option>, + #[serde(default)] + pub backing_files: bool, } impl ApplyLandlock for DiskConfig {