mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow_async: impl write_vectored for QcowAsync
Synchronous per cluster write path - gather guest data from iovecs, map each cluster through QcowMetadata, and pwrite to the allocated host offset. Partial cluster writes with a backing file read the backing data first so map_cluster_for_write can perform COW. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
e7621abfbd
commit
a97260c5e0
@@ -6,6 +6,7 @@
|
||||
|
||||
//! QCOW2 async disk backend.
|
||||
|
||||
use std::cmp::min;
|
||||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::Error;
|
||||
@@ -20,10 +21,12 @@ use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, Disk
|
||||
use crate::disk_file;
|
||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||
use crate::qcow::backing::shared_backing_from;
|
||||
use crate::qcow::metadata::{BackingRead, ClusterReadMapping, QcowMetadata};
|
||||
use crate::qcow::metadata::{BackingRead, ClusterReadMapping, ClusterWriteMapping, QcowMetadata};
|
||||
use crate::qcow::qcow_raw_file::QcowRawFile;
|
||||
use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow};
|
||||
use crate::qcow_common::{pread_exact, scatter_to_iovecs, zero_fill_iovecs};
|
||||
use crate::qcow_common::{
|
||||
gather_from_iovecs, pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs,
|
||||
};
|
||||
|
||||
/// Device level handle for a QCOW2 image.
|
||||
///
|
||||
@@ -218,13 +221,30 @@ impl AsyncIo for QcowAsync {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO Make writes async.
|
||||
// Writes are synchronous. Async writes require a multi step
|
||||
// state machine for COW (backing read, cluster allocation, data
|
||||
// write, L2 commit) with per request buffer lifetime tracking
|
||||
// and write ordering.
|
||||
fn write_vectored(
|
||||
&mut self,
|
||||
offset: libc::off_t,
|
||||
iovecs: &[libc::iovec],
|
||||
user_data: u64,
|
||||
) -> AsyncIoResult<()> {
|
||||
unimplemented!()
|
||||
Self::cow_write_sync(
|
||||
offset as u64,
|
||||
iovecs,
|
||||
&self.metadata,
|
||||
&self.data_file,
|
||||
&self.backing_file,
|
||||
)?;
|
||||
|
||||
let total_len: usize = iovecs.iter().map(|v| v.iov_len).sum();
|
||||
self.completion_list
|
||||
.push_back((user_data, total_len as i32));
|
||||
self.eventfd.write(1).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||
@@ -334,4 +354,55 @@ impl QcowAsync {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write iovec data cluster-by-cluster with COW from backing file.
|
||||
fn cow_write_sync(
|
||||
address: u64,
|
||||
iovecs: &[libc::iovec],
|
||||
metadata: &QcowMetadata,
|
||||
data_file: &QcowRawFile,
|
||||
backing_file: &Option<Arc<dyn BackingRead>>,
|
||||
) -> 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 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,
|
||||
} => {
|
||||
// SAFETY: iovecs point to valid guest memory buffers.
|
||||
let buf = unsafe { gather_from_iovecs(iovecs, buf_offset, count) };
|
||||
pwrite_all(data_file.as_raw_fd(), &buf, host_offset)
|
||||
.map_err(AsyncIoError::WriteVectored)?;
|
||||
}
|
||||
}
|
||||
buf_offset += count;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user