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
+74
-3
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
//! QCOW2 async disk backend.
|
//! QCOW2 async disk backend.
|
||||||
|
|
||||||
|
use std::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
@@ -20,10 +21,12 @@ use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, Disk
|
|||||||
use crate::disk_file;
|
use crate::disk_file;
|
||||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||||
use crate::qcow::backing::shared_backing_from;
|
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::qcow_raw_file::QcowRawFile;
|
||||||
use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow};
|
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.
|
/// Device level handle for a QCOW2 image.
|
||||||
///
|
///
|
||||||
@@ -218,13 +221,30 @@ impl AsyncIo for QcowAsync {
|
|||||||
Ok(())
|
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(
|
fn write_vectored(
|
||||||
&mut self,
|
&mut self,
|
||||||
offset: libc::off_t,
|
offset: libc::off_t,
|
||||||
iovecs: &[libc::iovec],
|
iovecs: &[libc::iovec],
|
||||||
user_data: u64,
|
user_data: u64,
|
||||||
) -> AsyncIoResult<()> {
|
) -> 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<()> {
|
fn fsync(&mut self, user_data: Option<u64>) -> AsyncIoResult<()> {
|
||||||
@@ -334,4 +354,55 @@ impl QcowAsync {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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