block: qcow: Cache cluster_size in per queue structs

Cache the immutable cluster_size value at construction time in
QcowAsync, QcowSync, and Qcow2Backing. This avoids repeated RwLock
read acquisitions on the hot write and deallocation paths.

Replace QcowMetadata::cluster_offset() calls with inline bitmask
operations using the cached cluster_size. Remove the now unused
cluster_offset() method from QcowMetadata.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-04-17 20:53:54 +02:00
committed by Rob Bradford
parent d9b188c1be
commit 659f7c17e5
3 changed files with 14 additions and 14 deletions

View File

@@ -333,11 +333,6 @@ impl QcowMetadata {
pub fn cluster_size(&self) -> u64 {
self.inner.read().unwrap().raw_file.cluster_size()
}
/// Returns the intra cluster byte offset for a given guest address.
pub fn cluster_offset(&self, address: u64) -> u64 {
self.inner.read().unwrap().raw_file.cluster_offset(address)
}
}
impl QcowState {

View File

@@ -177,6 +177,7 @@ pub struct QcowAsync {
alignment: usize,
/// I/O alignment for the AsyncIo trait (at least SECTOR_SIZE).
io_alignment: u64,
cluster_size: u64,
io_uring: IoUring,
eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>,
@@ -197,6 +198,7 @@ impl QcowAsync {
io_uring.submitter().register_eventfd(eventfd.as_raw_fd())?;
Ok(QcowAsync {
cluster_size: metadata.cluster_size(),
metadata,
data_file,
backing_file,
@@ -296,6 +298,7 @@ impl AsyncIo for QcowAsync {
&self.data_file,
&self.backing_file,
self.alignment,
self.cluster_size,
)?;
let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum();
@@ -325,7 +328,7 @@ impl AsyncIo for QcowAsync {
fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> {
let virtual_size = self.metadata.virtual_size();
let cluster_size = self.metadata.cluster_size();
let cluster_size = self.cluster_size;
let result = self
.metadata
@@ -425,6 +428,7 @@ impl AsyncIo for QcowAsync {
&self.data_file,
&self.backing_file,
self.alignment,
self.cluster_size,
)?;
sync_completions.push((req.user_data, total_len as i32));
}
@@ -571,14 +575,14 @@ impl QcowAsync {
data_file: &QcowRawFile,
backing_file: &Option<Arc<dyn BackingRead>>,
alignment: usize,
cluster_size: u64,
) -> AsyncIoResult<()> {
let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum();
let cluster_size = metadata.cluster_size();
let mut buf_offset = 0usize;
while buf_offset < total_len {
let curr_addr = address + buf_offset as u64;
let intra_offset = metadata.cluster_offset(curr_addr);
let intra_offset = curr_addr & (cluster_size - 1);
let remaining_in_cluster = (cluster_size - intra_offset) as usize;
let count = min(total_len - buf_offset, remaining_in_cluster);

View File

@@ -156,6 +156,7 @@ pub struct QcowSync {
sparse: bool,
/// O_DIRECT alignment requirement (0 = no alignment needed).
alignment: usize,
cluster_size: u64,
eventfd: EventFd,
completion_list: VecDeque<(u64, i32)>,
}
@@ -169,6 +170,7 @@ impl QcowSync {
) -> Self {
let alignment = data_file.file().alignment();
QcowSync {
cluster_size: metadata.cluster_size(),
metadata,
data_file,
backing_file,
@@ -278,9 +280,8 @@ impl AsyncIo for QcowSync {
while buf_offset < total_len {
let curr_addr = address + buf_offset as u64;
let cluster_size = self.metadata.cluster_size();
let intra_offset = self.metadata.cluster_offset(curr_addr);
let remaining_in_cluster = (cluster_size - intra_offset) as usize;
let intra_offset = curr_addr & (self.cluster_size - 1);
let remaining_in_cluster = (self.cluster_size - intra_offset) as usize;
let count = min(total_len - buf_offset, remaining_in_cluster);
// Read backing data for COW if this is a partial cluster
@@ -288,10 +289,10 @@ impl AsyncIo for QcowSync {
let backing_data = if let Some(backing) = self
.backing_file
.as_ref()
.filter(|_| intra_offset != 0 || count < cluster_size as usize)
.filter(|_| intra_offset != 0 || count < self.cluster_size as usize)
{
let cluster_begin = curr_addr - intra_offset;
let mut data = vec![0u8; cluster_size as usize];
let mut data = vec![0u8; self.cluster_size as usize];
backing
.read_at(cluster_begin, &mut data)
.map_err(AsyncIoError::WriteVectored)?;
@@ -357,7 +358,7 @@ impl AsyncIo for QcowSync {
fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()> {
let virtual_size = self.metadata.virtual_size();
let cluster_size = self.metadata.cluster_size();
let cluster_size = self.cluster_size;
let result = self
.metadata