block: qcow: Implement punch_hole and write_zeroes for QcowSync

Implement punch_hole and write_zeroes for QcowSync backend by
delegating to QcowFile::punch_hole which triggers cluster
deallocation. write_zeroes delegates to punch_hole as unallocated
clusters read as zeros in QCOW2.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski 2026-02-04 15:55:35 +01:00 committed by Rob Bradford
parent 81c075b317
commit 6c94975c80

View file

@ -9,6 +9,7 @@ use std::os::fd::AsRawFd;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::write_zeroes::PunchHole;
use crate::async_io::{ use crate::async_io::{
AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult, AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFile, DiskFileError, DiskFileResult,
@ -151,15 +152,67 @@ impl AsyncIo for QcowSync {
self.completion_list.pop_front() self.completion_list.pop_front()
} }
fn punch_hole(&mut self, _offset: u64, _length: u64, _user_data: u64) -> AsyncIoResult<()> { fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> {
Err(AsyncIoError::PunchHole(std::io::Error::other( // For QCOW2, punch_hole calls deallocate_cluster
"punch_hole not supported for QCOW sync backend", let result = self
))) .qcow_file
.lock()
.unwrap()
.punch_hole(offset, length)
.map(|_| 0i32)
.map_err(AsyncIoError::PunchHole);
match result {
Ok(res) => {
self.completion_list.push_back((user_data, res));
self.eventfd.write(1).unwrap();
Ok(())
}
Err(e) => {
// CRITICAL: Always signal completion even on error to avoid hangs
let errno = if let AsyncIoError::PunchHole(io_err) = &e {
let err = io_err.raw_os_error().unwrap_or(libc::EIO);
-err
} else {
-libc::EIO
};
self.completion_list.push_back((user_data, errno));
self.eventfd.write(1).unwrap();
Ok(())
}
}
} }
fn write_zeroes(&mut self, _offset: u64, _length: u64, _user_data: u64) -> AsyncIoResult<()> { fn write_zeroes(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> {
Err(AsyncIoError::WriteZeroes(std::io::Error::other( // For QCOW2, write_zeroes is implemented by deallocating clusters via punch_hole.
"write_zeroes not supported for QCOW sync backend", // This is more efficient than writing actual zeros and reduces disk usage.
))) // Unallocated clusters inherently read as zero in the QCOW2 format.
let result = self
.qcow_file
.lock()
.unwrap()
.punch_hole(offset, length)
.map(|_| 0i32)
.map_err(AsyncIoError::WriteZeroes);
match result {
Ok(res) => {
self.completion_list.push_back((user_data, res));
self.eventfd.write(1).unwrap();
Ok(())
}
Err(e) => {
// Always signal completion even on error to avoid hangs
let errno = if let AsyncIoError::WriteZeroes(io_err) = &e {
let err = io_err.raw_os_error().unwrap_or(libc::EIO);
-err
} else {
-libc::EIO
};
self.completion_list.push_back((user_data, errno));
self.eventfd.write(1).unwrap();
Ok(())
}
}
} }
} }