mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Share the synchronous write path between the two engines
Both engines ran the same copy on write loop for a partial cluster write. In the uring engine it was a separate helper, cow_write_sync. In the sync engine the same loop sat directly in the write path. Move cow_write_sync into common.rs and call it from both. The uring write already runs synchronously, so its behavior does not change. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
eeb71091db
commit
25b270010f
@@ -8,14 +8,17 @@
|
|||||||
|
|
||||||
//! Shared helpers for QCOW2 sync and async backends.
|
//! Shared helpers for QCOW2 sync and async backends.
|
||||||
|
|
||||||
|
use std::cmp::min;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::os::unix::fs::FileExt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||||
|
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
use super::metadata::{BackingRead, DeallocAction, QcowMetadata};
|
use super::metadata::{BackingRead, ClusterWriteMapping, DeallocAction, QcowMetadata};
|
||||||
use super::qcow_raw_file::QcowRawFile;
|
use super::qcow_raw_file::QcowRawFile;
|
||||||
use crate::async_io::AsyncIoError;
|
use crate::async_io::{AsyncIoError, AsyncIoOperation, AsyncIoResult};
|
||||||
|
|
||||||
/// Decompress a full QCOW2 cluster from compressed data.
|
/// Decompress a full QCOW2 cluster from compressed data.
|
||||||
///
|
///
|
||||||
@@ -99,6 +102,62 @@ pub(super) fn deallocate_range_result(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes an operation to the data file cluster by cluster, allocating
|
||||||
|
/// and copying up backing data as needed. Writes are synchronous because
|
||||||
|
/// the host offset is only known after the metadata allocation.
|
||||||
|
pub(super) fn cow_write_sync(
|
||||||
|
address: u64,
|
||||||
|
op: &AsyncIoOperation,
|
||||||
|
metadata: &QcowMetadata,
|
||||||
|
data_file: &QcowRawFile,
|
||||||
|
backing_file: &Option<Arc<dyn BackingRead>>,
|
||||||
|
cluster_size: u64,
|
||||||
|
) -> AsyncIoResult<()> {
|
||||||
|
let total_len = op.total_len();
|
||||||
|
let mut buf_offset = 0usize;
|
||||||
|
|
||||||
|
while buf_offset < total_len {
|
||||||
|
let curr_addr = address + buf_offset as u64;
|
||||||
|
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);
|
||||||
|
|
||||||
|
let backing_data = if let Some(backing) = backing_file
|
||||||
|
.as_ref()
|
||||||
|
.filter(|_| intra_offset != 0 || count < cluster_size as usize)
|
||||||
|
{
|
||||||
|
let cluster_begin = curr_addr - intra_offset;
|
||||||
|
let mut data = vec![0u8; cluster_size as usize];
|
||||||
|
backing
|
||||||
|
.read_at(cluster_begin, &mut data)
|
||||||
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
Some(data)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mapping = metadata
|
||||||
|
.map_cluster_for_write(curr_addr, backing_data)
|
||||||
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
|
||||||
|
match mapping {
|
||||||
|
ClusterWriteMapping::Allocated {
|
||||||
|
offset: host_offset,
|
||||||
|
} => {
|
||||||
|
let mut buf = vec![0u8; count];
|
||||||
|
op.read_bytes_at(buf_offset, &mut buf)
|
||||||
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
data_file
|
||||||
|
.file()
|
||||||
|
.write_all_at(&buf, host_offset)
|
||||||
|
.map_err(AsyncIoError::WriteVectored)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf_offset += count;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod unit_tests {
|
pub(crate) mod unit_tests {
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
|||||||
@@ -4,15 +4,14 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||||
|
|
||||||
use std::cmp::min;
|
|
||||||
use std::os::unix::fs::FileExt;
|
use std::os::unix::fs::FileExt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use super::common::{deallocate_range_result, decompress_cluster};
|
use super::common::{cow_write_sync, deallocate_range_result, decompress_cluster};
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
use super::metadata::{BackingRead, ClusterReadMapping, ClusterWriteMapping, QcowMetadata};
|
use super::metadata::{BackingRead, ClusterReadMapping, QcowMetadata};
|
||||||
use super::qcow_raw_file::QcowRawFile;
|
use super::qcow_raw_file::QcowRawFile;
|
||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult, CompletionCommon,
|
AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult, CompletionCommon,
|
||||||
@@ -119,58 +118,6 @@ impl QcowSync {
|
|||||||
|
|
||||||
Ok(total_len)
|
Ok(total_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_operation(&mut self, op: &AsyncIoOperation) -> AsyncIoResult<usize> {
|
|
||||||
let address = op.offset() as u64;
|
|
||||||
let total_len = op.total_len();
|
|
||||||
let mut buf_offset = 0usize;
|
|
||||||
|
|
||||||
while buf_offset < total_len {
|
|
||||||
let curr_addr = address + buf_offset as u64;
|
|
||||||
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
|
|
||||||
// write to an unallocated cluster with a backing file.
|
|
||||||
let backing_data = if let Some(backing) = self
|
|
||||||
.backing_file
|
|
||||||
.as_ref()
|
|
||||||
.filter(|_| intra_offset != 0 || count < self.cluster_size as usize)
|
|
||||||
{
|
|
||||||
let cluster_begin = curr_addr - intra_offset;
|
|
||||||
let mut data = vec![0u8; self.cluster_size as usize];
|
|
||||||
backing
|
|
||||||
.read_at(cluster_begin, &mut data)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
Some(data)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let mapping = self
|
|
||||||
.metadata
|
|
||||||
.map_cluster_for_write(curr_addr, backing_data)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
|
|
||||||
match mapping {
|
|
||||||
ClusterWriteMapping::Allocated {
|
|
||||||
offset: host_offset,
|
|
||||||
} => {
|
|
||||||
let mut buf = vec![0u8; count];
|
|
||||||
op.read_bytes_at(buf_offset, &mut buf)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
self.data_file
|
|
||||||
.file()
|
|
||||||
.write_all_at(&buf, host_offset)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf_offset += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(total_len)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsyncIo for QcowSync {
|
impl AsyncIo for QcowSync {
|
||||||
@@ -179,11 +126,18 @@ impl AsyncIo for QcowSync {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn submit_data_operation(&mut self, mut op: AsyncIoOperation) -> AsyncIoResult<()> {
|
fn submit_data_operation(&mut self, mut op: AsyncIoOperation) -> AsyncIoResult<()> {
|
||||||
let is_read = op.is_read();
|
let total_len = if op.is_read() {
|
||||||
let total_len = if is_read {
|
|
||||||
self.read_operation(&mut op)?
|
self.read_operation(&mut op)?
|
||||||
} else {
|
} else {
|
||||||
self.write_operation(&op)?
|
cow_write_sync(
|
||||||
|
op.offset() as u64,
|
||||||
|
&op,
|
||||||
|
&self.metadata,
|
||||||
|
&self.data_file,
|
||||||
|
&self.backing_file,
|
||||||
|
self.cluster_size,
|
||||||
|
)?;
|
||||||
|
op.total_len()
|
||||||
};
|
};
|
||||||
self.completions
|
self.completions
|
||||||
.complete(AsyncIoCompletion::from_operation(op, total_len as i32));
|
.complete(AsyncIoCompletion::from_operation(op, total_len as i32));
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
//! QCOW2 async disk backend.
|
//! QCOW2 async disk backend.
|
||||||
|
|
||||||
use std::cmp::min;
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::os::unix::fs::FileExt;
|
use std::os::unix::fs::FileExt;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
@@ -16,9 +15,9 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use super::common::{deallocate_range_result, decompress_cluster};
|
use super::common::{cow_write_sync, deallocate_range_result, decompress_cluster};
|
||||||
use super::decoder::Decoder;
|
use super::decoder::Decoder;
|
||||||
use super::metadata::{BackingRead, ClusterReadMapping, ClusterWriteMapping, QcowMetadata};
|
use super::metadata::{BackingRead, ClusterReadMapping, QcowMetadata};
|
||||||
use super::qcow_raw_file::QcowRawFile;
|
use super::qcow_raw_file::QcowRawFile;
|
||||||
use crate::async_io::{
|
use crate::async_io::{
|
||||||
AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult, UringDataIo,
|
AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult, UringDataIo,
|
||||||
@@ -118,7 +117,7 @@ impl QcowAsync {
|
|||||||
// write, L2 commit) with per request buffer lifetime tracking
|
// write, L2 commit) with per request buffer lifetime tracking
|
||||||
// and write ordering.
|
// and write ordering.
|
||||||
let total_len = op.total_len();
|
let total_len = op.total_len();
|
||||||
if let Err(e) = Self::cow_write_sync(
|
if let Err(e) = cow_write_sync(
|
||||||
op.offset() as u64,
|
op.offset() as u64,
|
||||||
&op,
|
&op,
|
||||||
&self.metadata,
|
&self.metadata,
|
||||||
@@ -352,60 +351,6 @@ impl QcowAsync {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write owned operation data cluster-by-cluster with COW from backing file.
|
|
||||||
fn cow_write_sync(
|
|
||||||
address: u64,
|
|
||||||
op: &AsyncIoOperation,
|
|
||||||
metadata: &QcowMetadata,
|
|
||||||
data_file: &QcowRawFile,
|
|
||||||
backing_file: &Option<Arc<dyn BackingRead>>,
|
|
||||||
cluster_size: u64,
|
|
||||||
) -> AsyncIoResult<()> {
|
|
||||||
let total_len = op.total_len();
|
|
||||||
let mut buf_offset = 0usize;
|
|
||||||
|
|
||||||
while buf_offset < total_len {
|
|
||||||
let curr_addr = address + buf_offset as u64;
|
|
||||||
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);
|
|
||||||
|
|
||||||
let backing_data = if let Some(backing) = backing_file
|
|
||||||
.as_ref()
|
|
||||||
.filter(|_| intra_offset != 0 || count < cluster_size as usize)
|
|
||||||
{
|
|
||||||
let cluster_begin = curr_addr - intra_offset;
|
|
||||||
let mut data = vec![0u8; cluster_size as usize];
|
|
||||||
backing
|
|
||||||
.read_at(cluster_begin, &mut data)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
Some(data)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let mapping = metadata
|
|
||||||
.map_cluster_for_write(curr_addr, backing_data)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
|
|
||||||
match mapping {
|
|
||||||
ClusterWriteMapping::Allocated {
|
|
||||||
offset: host_offset,
|
|
||||||
} => {
|
|
||||||
let mut buf = vec![0u8; count];
|
|
||||||
op.read_bytes_at(buf_offset, &mut buf)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
data_file
|
|
||||||
.file()
|
|
||||||
.write_all_at(&buf, host_offset)
|
|
||||||
.map_err(AsyncIoError::WriteVectored)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf_offset += count;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in New Issue
Block a user