From 37d71fa038191b44d2206cb8b0b9e57986aa5f52 Mon Sep 17 00:00:00 2001 From: Thomas Prescher Date: Tue, 11 Nov 2025 09:40:16 +0100 Subject: [PATCH] vmm: disk resize infrastructure Add basic infrastructure so resize events are propagated to the underlying disk implementation. On-behalf-of: SAP thomas.prescher@sap.com Signed-off-by: Thomas Prescher --- block/src/async_io.rs | 10 +++++++++ fuzz/fuzz_targets/http_api.rs | 4 ++++ virtio-devices/src/block.rs | 40 ++++++++++++++++++++++++++++++++++- vmm/src/api/mod.rs | 2 ++ vmm/src/device_manager.rs | 16 ++++++++++++++ vmm/src/lib.rs | 10 +++++++++ vmm/src/vm.rs | 13 ++++++++++++ 7 files changed, 94 insertions(+), 1 deletion(-) diff --git a/block/src/async_io.rs b/block/src/async_io.rs index 52f642908..bd4c7bbe5 100644 --- a/block/src/async_io.rs +++ b/block/src/async_io.rs @@ -18,6 +18,12 @@ pub enum DiskFileError { /// Failed creating a new AsyncIo. #[error("Failed creating a new AsyncIo")] NewAsyncIo(#[source] std::io::Error), + /// Unsupported operation. + #[error("Unsupported operation")] + Unsupported, + /// Resize failed + #[error("Resize failed")] + ResizeError(#[source] std::io::Error), } pub type DiskFileResult = std::result::Result; @@ -68,6 +74,10 @@ pub trait DiskFile: Send { fn topology(&mut self) -> DiskTopology { DiskTopology::default() } + fn resize(&mut self, _size: u64) -> DiskFileResult<()> { + Err(DiskFileError::Unsupported) + } + /// Returns the file descriptor of the underlying disk image file. /// /// The file descriptor is supposed to be used for `fcntl()` calls but no diff --git a/fuzz/fuzz_targets/http_api.rs b/fuzz/fuzz_targets/http_api.rs index 6c00216a0..ee9dd62f1 100644 --- a/fuzz/fuzz_targets/http_api.rs +++ b/fuzz/fuzz_targets/http_api.rs @@ -108,6 +108,10 @@ impl RequestHandler for StubApiRequestHandler { Ok(()) } + fn vm_resize_disk(&mut self, _: String, _: u64) -> Result<(), VmError> { + Ok(()) + } + #[cfg(target_arch = "x86_64")] fn vm_coredump(&mut self, _: &str) -> Result<(), VmError> { Ok(()) diff --git a/virtio-devices/src/block.rs b/virtio-devices/src/block.rs index 2ede4c15a..d3a0e6df3 100644 --- a/virtio-devices/src/block.rs +++ b/virtio-devices/src/block.rs @@ -18,7 +18,7 @@ use std::sync::{Arc, Barrier}; use std::{io, result}; use anyhow::anyhow; -use block::async_io::{AsyncIo, AsyncIoError, DiskFile}; +use block::async_io::{AsyncIo, AsyncIoError, DiskFile, DiskFileError}; use block::fcntl::{LockError, LockGranularity, LockType, get_lock_state}; use block::{ ExecuteAsync, ExecuteError, Request, RequestType, VirtioBlockConfig, build_serial, fcntl, @@ -95,6 +95,16 @@ pub enum Error { /// The path of the disk image. path: PathBuf, }, + #[error("Disk image size is not a multiple of {}", SECTOR_SIZE)] + InvalidSize, + #[error("Failed to pause vcpus")] + PauseVcpus(#[source] MigratableError), + #[error("Failed to resume vcpus")] + ResumeVcpus(#[source] MigratableError), + #[error("Failed signal config interrupt")] + ConfigChange(#[source] io::Error), + #[error("Disk resize failed")] + DiskResize(#[source] DiskFileError), } pub type Result = result::Result; @@ -870,6 +880,34 @@ impl Block { self.writeback.store(writeback, Ordering::Release); } + pub fn resize(&mut self, new_size: u64) -> Result<()> { + if !new_size.is_multiple_of(SECTOR_SIZE) { + return Err(Error::InvalidSize); + } + + self.disk_image + .resize(new_size) + .map_err(Error::DiskResize)?; + + let nsectors = new_size / SECTOR_SIZE; + + self.common.pause().map_err(Error::PauseVcpus)?; + + self.disk_nsectors.store(nsectors, Ordering::SeqCst); + self.config.capacity = nsectors; + self.state().disk_nsectors = nsectors; + + self.common.resume().map_err(Error::ResumeVcpus)?; + + if let Some(interrupt_cb) = self.common.interrupt_cb.as_ref() { + interrupt_cb + .trigger(VirtioInterruptType::Config) + .map_err(Error::ConfigChange) + } else { + Ok(()) + } + } + #[cfg(fuzzing)] pub fn wait_for_epoll_threads(&mut self) { self.common.wait_for_epoll_threads(); diff --git a/vmm/src/api/mod.rs b/vmm/src/api/mod.rs index a0b090542..3b5fe83dd 100644 --- a/vmm/src/api/mod.rs +++ b/vmm/src/api/mod.rs @@ -315,6 +315,8 @@ pub trait RequestHandler { fn vm_resize_zone(&mut self, id: String, desired_ram: u64) -> Result<(), VmError>; + fn vm_resize_disk(&mut self, id: String, desired_size: u64) -> Result<(), VmError>; + fn vm_add_device(&mut self, device_cfg: DeviceConfig) -> Result>, VmError>; fn vm_add_user_device( diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 077c4ec7e..6d465047b 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -670,6 +670,10 @@ pub enum DeviceManagerError { /// Error adding fw_cfg to bus. #[error("Error adding fw_cfg to bus")] ErrorAddingFwCfgToBus(#[source] vm_device::BusError), + + /// Disk resizing failed. + #[error("Disk resize error")] + DiskResize(#[source] virtio_devices::block::Error), } pub type DeviceManagerResult = result::Result; @@ -4901,6 +4905,18 @@ impl DeviceManager { 0 } + pub fn resize_disk(&mut self, device_id: &str, new_size: u64) -> DeviceManagerResult<()> { + for dev in &self.block_devices { + let mut disk = dev.lock().unwrap(); + if disk.id() == device_id { + return disk + .resize(new_size) + .map_err(DeviceManagerError::DiskResize); + } + } + Err(DeviceManagerError::UnknownDeviceId(device_id.to_string())) + } + pub fn device_tree(&self) -> Arc> { self.device_tree.clone() } diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 863e95ce8..6917e005e 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -1962,6 +1962,16 @@ impl RequestHandler for Vmm { } } + fn vm_resize_disk(&mut self, id: String, desired_size: u64) -> result::Result<(), VmError> { + self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?; + + if let Some(ref mut vm) = self.vm { + return vm.resize_disk(&id, desired_size); + } + + Err(VmError::ResizeDisk) + } + fn vm_resize_zone(&mut self, id: String, desired_ram: u64) -> result::Result<(), VmError> { self.vm_config.as_ref().ok_or(VmError::VmNotCreated)?; diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 721c490b8..536481885 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -246,6 +246,9 @@ pub enum Error { #[error("Failed resizing a memory zone")] ResizeZone, + #[error("Failed resizing a disk image")] + ResizeDisk, + #[error("Cannot activate virtio devices")] ActivateVirtioDevices(#[source] DeviceManagerError), @@ -1706,6 +1709,16 @@ impl Vm { Ok(()) } + pub fn resize_disk(&mut self, id: &str, desired_size: u64) -> Result<()> { + self.device_manager + .lock() + .unwrap() + .resize_disk(id, desired_size) + .map_err(Error::DeviceManager)?; + + Ok(()) + } + pub fn resize_zone(&mut self, id: &str, desired_memory: u64) -> Result<()> { let memory_config = &mut self.config.lock().unwrap().memory;