From f93340d337bc04133bbcc813b2c9da9f80c622fa Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sun, 8 Feb 2026 21:14:28 +0000 Subject: [PATCH] vmm: Add option to control backing files Backing files (e.g. for QCOW2) interact badly with landlock since they are not obvious from the initial VM configuration. Only enable their use with an explicit option. Signed-off-by: Rob Bradford Signed-off-by: Bo Chen (cherry picked from commit 509832298b6865365b00bda88722e76e41ce7f41) --- block/src/lib.rs | 1 + block/src/qcow_sync.rs | 17 +++++++++++---- cloud-hypervisor/tests/integration.rs | 31 +++++++++++++++++---------- vmm/src/config.rs | 13 +++++++++-- vmm/src/device_manager.rs | 6 +++++- vmm/src/vm_config.rs | 2 ++ 6 files changed, 52 insertions(+), 18 deletions(-) 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 216443795..386e71a3f 100644 --- a/cloud-hypervisor/tests/integration.rs +++ b/cloud-hypervisor/tests/integration.rs @@ -3412,6 +3412,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)); @@ -3432,8 +3433,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!( @@ -3503,17 +3505,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); } /// Uses `qemu-img check` to verify disk image consistency. @@ -3549,22 +3551,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] @@ -3574,6 +3582,7 @@ mod common_parallel { false, false, true, + true, ); } @@ -3599,7 +3608,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] @@ -3623,7 +3632,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 {