mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Switch virtio-blk request construction and the users to the owned AsyncIo data path added in the series. Read bounce buffers now return through AsyncIoCompletion before being copied back to guest memory. This makes the main virtio async block I/O path use retained request memory. qcow still has raw-iovec fallback paths at this point; those are removed in follow-up commits. Leave the legacy borrowed iovec trait methods in place for a follow-up cleanup commit to minimize single-commit churn. Signed-off-by: Dylan Reid <dgreid@fb.com>
243 lines
7.8 KiB
Rust
243 lines
7.8 KiB
Rust
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
|
|
//
|
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use std::fs::File;
|
|
use std::io;
|
|
use std::os::unix::io::AsRawFd;
|
|
|
|
use crate::async_io::{AsyncIo, BorrowedDiskFd, DiskFileError};
|
|
use crate::disk_file::DiskSize;
|
|
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
|
use crate::fixed_vhd::FixedVhd;
|
|
#[cfg(feature = "io_uring")]
|
|
use crate::fixed_vhd_async::FixedVhdAsync;
|
|
use crate::fixed_vhd_sync::FixedVhdSync;
|
|
use crate::{BlockBackend, Error, disk_file};
|
|
|
|
#[derive(Debug)]
|
|
pub struct FixedVhdDisk {
|
|
inner: FixedVhd,
|
|
use_io_uring: bool,
|
|
}
|
|
|
|
impl FixedVhdDisk {
|
|
pub fn new(file: File, use_io_uring: bool) -> BlockResult<Self> {
|
|
#[cfg(not(feature = "io_uring"))]
|
|
if use_io_uring {
|
|
return Err(BlockError::new(
|
|
BlockErrorKind::UnsupportedFeature,
|
|
DiskFileError::NewAsyncIo(io::Error::other(
|
|
"io_uring requested but feature is not enabled",
|
|
)),
|
|
));
|
|
}
|
|
|
|
Ok(Self {
|
|
inner: FixedVhd::new(file).map_err(|e| BlockError::from(e).with_op(ErrorOp::Open))?,
|
|
use_io_uring,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl disk_file::DiskSize for FixedVhdDisk {
|
|
fn logical_size(&self) -> BlockResult<u64> {
|
|
self.inner
|
|
.logical_size()
|
|
.map_err(|e| BlockError::new(BlockErrorKind::Io, e))
|
|
}
|
|
}
|
|
|
|
impl disk_file::PhysicalSize for FixedVhdDisk {
|
|
fn physical_size(&self) -> BlockResult<u64> {
|
|
self.inner.physical_size().map_err(|e| match e {
|
|
Error::GetFileMetadata(io) => {
|
|
BlockError::new(BlockErrorKind::Io, Error::GetFileMetadata(io))
|
|
}
|
|
_ => unreachable!("unexpected error from FixedVhd::physical_size(): {e}"),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl disk_file::DiskFd for FixedVhdDisk {
|
|
fn fd(&self) -> BorrowedDiskFd<'_> {
|
|
BorrowedDiskFd::new(self.inner.as_raw_fd())
|
|
}
|
|
}
|
|
|
|
impl disk_file::Geometry for FixedVhdDisk {}
|
|
|
|
impl disk_file::SparseCapable for FixedVhdDisk {}
|
|
|
|
impl disk_file::Resizable for FixedVhdDisk {
|
|
fn resize(&mut self, _size: u64) -> BlockResult<()> {
|
|
Err(BlockError::new(
|
|
BlockErrorKind::UnsupportedFeature,
|
|
DiskFileError::ResizeError(io::Error::other("resize not supported for fixed VHD")),
|
|
)
|
|
.with_op(ErrorOp::Resize))
|
|
}
|
|
}
|
|
|
|
impl disk_file::DiskFile for FixedVhdDisk {}
|
|
|
|
impl disk_file::AsyncDiskFile for FixedVhdDisk {
|
|
fn try_clone(&self) -> BlockResult<Box<dyn disk_file::AsyncDiskFile>> {
|
|
Ok(Box::new(FixedVhdDisk {
|
|
inner: self.inner.clone(),
|
|
use_io_uring: self.use_io_uring,
|
|
}))
|
|
}
|
|
|
|
fn create_async_io(&self, ring_depth: u32) -> BlockResult<Box<dyn AsyncIo>> {
|
|
let size = self.logical_size()?;
|
|
|
|
if self.use_io_uring {
|
|
#[cfg(feature = "io_uring")]
|
|
{
|
|
return Ok(Box::new(FixedVhdAsync::new(
|
|
self.inner.as_raw_fd(),
|
|
ring_depth,
|
|
size,
|
|
)?));
|
|
}
|
|
|
|
#[cfg(not(feature = "io_uring"))]
|
|
unreachable!("use_io_uring is set but io_uring feature is not enabled");
|
|
}
|
|
|
|
let _ = ring_depth;
|
|
Ok(Box::new(
|
|
FixedVhdSync::new(self.inner.as_raw_fd(), size).map_err(|e| {
|
|
BlockError::new(BlockErrorKind::Io, DiskFileError::NewAsyncIo(e))
|
|
.with_op(ErrorOp::Open)
|
|
})?,
|
|
))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod unit_tests {
|
|
use std::fs::File;
|
|
use std::io::{Seek, SeekFrom, Write};
|
|
#[cfg(feature = "io_uring")]
|
|
use std::os::fd::AsRawFd;
|
|
|
|
use vmm_sys_util::tempfile::TempFile;
|
|
|
|
use super::*;
|
|
use crate::async_io::AsyncIo;
|
|
#[cfg(feature = "io_uring")]
|
|
use crate::async_io::{AsyncIoOperation, OwnedIoBuffer};
|
|
use crate::disk_file::{AsyncDiskFile, DiskSize, PhysicalSize, Resizable};
|
|
|
|
/// Minimal fixed VHD footer (disk type = 2, current_size = 0x11223344).
|
|
fn fixed_vhd_footer() -> &'static [u8] {
|
|
&[
|
|
0x63, 0x6f, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x78, // cookie
|
|
0x00, 0x00, 0x00, 0x02, // features
|
|
0x00, 0x01, 0x00, 0x00, // file format version
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // data offset
|
|
0x27, 0xa6, 0xa6, 0x5d, // time stamp
|
|
0x71, 0x65, 0x6d, 0x75, // creator application
|
|
0x00, 0x05, 0x00, 0x03, // creator version
|
|
0x57, 0x69, 0x32, 0x6b, // creator host os
|
|
0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, // original size
|
|
0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, // current size
|
|
0x11, 0xe0, 0x10, 0x3f, // disk geometry
|
|
0x00, 0x00, 0x00, 0x02, // disk type
|
|
0x00, 0x00, 0x00, 0x00, // checksum
|
|
0x98, 0x7b, 0xb1, 0xcd, 0x84, 0x14, 0x41, 0xfc, // unique id
|
|
0xa4, 0xab, 0xd0, 0x69, 0x45, 0x2b, 0xf2, 0x23, 0x00, // saved state
|
|
]
|
|
}
|
|
|
|
fn make_vhd_file() -> File {
|
|
let mut file: File = TempFile::new().unwrap().into_file();
|
|
let data_size: u64 = 0x1122_3344;
|
|
file.set_len(data_size + 0x200).unwrap();
|
|
file.seek(SeekFrom::Start(data_size)).unwrap();
|
|
file.write_all(fixed_vhd_footer()).unwrap();
|
|
file
|
|
}
|
|
|
|
#[test]
|
|
fn new_sync_returns_correct_size() {
|
|
let file = make_vhd_file();
|
|
let disk = FixedVhdDisk::new(file, false).unwrap();
|
|
assert_eq!(disk.logical_size().unwrap(), 0x1122_3344);
|
|
}
|
|
|
|
fn assert_async_io_from_dyn(disk: &dyn AsyncDiskFile, expect_batch: bool) {
|
|
let io: Box<dyn AsyncIo> = disk.create_async_io(128).unwrap();
|
|
assert_eq!(io.batch_requests_enabled(), expect_batch);
|
|
}
|
|
|
|
fn assert_async_io(disk: &FixedVhdDisk, expect_batch: bool) {
|
|
assert_async_io_from_dyn(disk, expect_batch);
|
|
}
|
|
|
|
#[test]
|
|
fn sync_backend_disables_batch_requests() {
|
|
let file = make_vhd_file();
|
|
let disk = FixedVhdDisk::new(file, false).unwrap();
|
|
assert_async_io(&disk, false);
|
|
}
|
|
|
|
#[cfg(feature = "io_uring")]
|
|
#[test]
|
|
fn io_uring_backend_enables_batch_requests() {
|
|
let file = make_vhd_file();
|
|
let disk = FixedVhdDisk::new(file, true).unwrap();
|
|
assert_async_io(&disk, true);
|
|
}
|
|
|
|
#[cfg(feature = "io_uring")]
|
|
#[test]
|
|
fn io_uring_batch_rejects_request_past_logical_size() {
|
|
let file = TempFile::new().unwrap().into_file();
|
|
file.set_len(0x2000).unwrap();
|
|
let mut async_io = FixedVhdAsync::new(file.as_raw_fd(), 8, 0x1000).unwrap();
|
|
let op = AsyncIoOperation::read_to_vec(0x800, OwnedIoBuffer::from_vec(vec![0; 0x900]), 1);
|
|
|
|
assert!(matches!(
|
|
async_io.submit_batch_operations(vec![op]),
|
|
Err(crate::async_io::AsyncIoError::ReadVectored(_))
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn try_clone_preserves_sync_dispatch() {
|
|
let file = make_vhd_file();
|
|
let disk = FixedVhdDisk::new(file, false).unwrap();
|
|
let cloned = disk.try_clone().unwrap();
|
|
assert_async_io_from_dyn(cloned.as_ref(), false);
|
|
}
|
|
|
|
#[cfg(feature = "io_uring")]
|
|
#[test]
|
|
fn try_clone_preserves_io_uring_dispatch() {
|
|
let file = make_vhd_file();
|
|
let disk = FixedVhdDisk::new(file, true).unwrap();
|
|
let cloned = disk.try_clone().unwrap();
|
|
assert_async_io_from_dyn(cloned.as_ref(), true);
|
|
}
|
|
|
|
#[test]
|
|
fn resize_returns_error() {
|
|
let file = make_vhd_file();
|
|
let mut disk = FixedVhdDisk::new(file, false).unwrap();
|
|
assert!(disk.resize(0x2000_0000).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn physical_size_includes_footer() {
|
|
let file = make_vhd_file();
|
|
let disk = FixedVhdDisk::new(file, false).unwrap();
|
|
// Data region (0x1122_3344) + VHD footer (0x200).
|
|
assert_eq!(disk.physical_size().unwrap(), 0x1122_3344 + 0x200);
|
|
}
|
|
}
|