diff --git a/block/src/lib.rs b/block/src/lib.rs index d86b40129..a58c445ef 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -21,10 +21,10 @@ pub mod fixed_vhd_disk; pub mod fixed_vhd_sync; pub mod qcow; #[cfg(feature = "io_uring")] -pub mod qcow_async; +pub(crate) mod qcow_async; pub(crate) mod qcow_common; pub mod qcow_disk; -pub mod qcow_sync; +pub(crate) mod qcow_sync; #[cfg(feature = "io_uring")] pub(crate) mod raw_async; pub(crate) mod raw_async_aio; diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index 32c2b0648..b9a63d1f9 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -372,8 +372,7 @@ impl Debug for BackingFile { /// Parses and validates a QCOW2 image file, returning the metadata, backing /// file and sparse flag. /// -/// This shared constructor is used by both QcowFile for sequential I/O -/// and QcowDiskSync for lock based parallel I/O. +/// Used by [`QcowFile`] and [`QcowDisk`] constructors. pub(crate) fn parse_qcow( mut file: RawFile, max_nesting_depth: u32, diff --git a/block/src/qcow_async.rs b/block/src/qcow_async.rs index 8da8b1ebe..bf435105f 100644 --- a/block/src/qcow_async.rs +++ b/block/src/qcow_async.rs @@ -8,157 +8,25 @@ use std::cmp::{max, min}; use std::collections::VecDeque; -use std::fs::File; -use std::io::Error; -use std::os::fd::{AsFd, AsRawFd}; +use std::io; +use std::os::unix::io::AsRawFd; use std::sync::Arc; -use std::{fmt, io}; use io_uring::{IoUring, opcode, types}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt}; -use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFileError}; -use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp}; -use crate::qcow::backing::shared_backing_from; +use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult}; use crate::qcow::decoder::Decoder; use crate::qcow::metadata::{ BackingRead, ClusterReadMapping, ClusterWriteMapping, DeallocAction, QcowMetadata, }; use crate::qcow::qcow_raw_file::QcowRawFile; -use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow}; use crate::qcow_common::{ AlignedBuf, aligned_pread, aligned_pwrite, decompress_cluster, gather_from_iovecs_into, pread_alloc, pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs, }; -use crate::{BatchRequest, RequestType, SECTOR_SIZE, disk_file}; - -/// Device level handle for a QCOW2 image. -/// -/// Owns the parsed metadata and backing file chain. One instance is -/// created per disk and shared across virtio queues. -pub struct QcowDiskAsync { - metadata: Arc, - backing_file: Option>, - sparse: bool, - data_raw_file: QcowRawFile, -} - -impl fmt::Debug for QcowDiskAsync { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("QcowDiskAsync") - .field("sparse", &self.sparse) - .field("has_backing", &self.backing_file.is_some()) - .finish_non_exhaustive() - } -} - -impl QcowDiskAsync { - pub fn new( - file: File, - direct_io: bool, - backing_files: bool, - sparse: bool, - ) -> BlockResult { - let max_nesting_depth = if backing_files { MAX_NESTING_DEPTH } else { 0 }; - let (inner, backing_file, sparse) = - parse_qcow(RawFile::new(file, direct_io), max_nesting_depth, sparse).map_err(|e| { - let e = if !backing_files && matches!(e.kind(), BlockErrorKind::Overflow) { - e.with_kind(BlockErrorKind::UnsupportedFeature) - } else { - e - }; - e.with_op(ErrorOp::Open) - })?; - let data_raw_file = inner.raw_file.clone(); - Ok(QcowDiskAsync { - metadata: Arc::new(QcowMetadata::new(inner)), - backing_file: backing_file.map(shared_backing_from).transpose()?, - sparse, - data_raw_file, - }) - } -} - -impl Drop for QcowDiskAsync { - fn drop(&mut self) { - self.metadata.shutdown(); - } -} - -impl disk_file::DiskSize for QcowDiskAsync { - fn logical_size(&self) -> BlockResult { - Ok(self.metadata.virtual_size()) - } -} - -impl disk_file::PhysicalSize for QcowDiskAsync { - fn physical_size(&self) -> BlockResult { - Ok(self.data_raw_file.physical_size()?) - } -} - -impl disk_file::DiskFd for QcowDiskAsync { - fn fd(&self) -> BorrowedDiskFd<'_> { - BorrowedDiskFd::new(self.data_raw_file.as_fd().as_raw_fd()) - } -} - -impl disk_file::Geometry for QcowDiskAsync {} - -impl disk_file::SparseCapable for QcowDiskAsync { - fn supports_sparse_operations(&self) -> bool { - true - } - - fn supports_zero_flag(&self) -> bool { - true - } -} - -impl disk_file::Resizable for QcowDiskAsync { - fn resize(&mut self, size: u64) -> BlockResult<()> { - if self.backing_file.is_some() { - return Err(BlockError::new( - BlockErrorKind::UnsupportedFeature, - DiskFileError::ResizeError(io::Error::other( - "resize not supported with backing file", - )), - ) - .with_op(ErrorOp::Resize)); - } - self.metadata.resize(size).map_err(|e| { - BlockError::new(BlockErrorKind::Io, DiskFileError::ResizeError(e)) - .with_op(ErrorOp::Resize) - }) - } -} - -impl disk_file::DiskFile for QcowDiskAsync {} - -impl disk_file::AsyncDiskFile for QcowDiskAsync { - fn try_clone(&self) -> BlockResult> { - Ok(Box::new(QcowDiskAsync { - metadata: Arc::clone(&self.metadata), - backing_file: self.backing_file.as_ref().map(Arc::clone), - sparse: self.sparse, - data_raw_file: self.data_raw_file.clone(), - })) - } - - fn create_async_io(&self, ring_depth: u32) -> BlockResult> { - Ok(Box::new( - QcowAsync::new( - Arc::clone(&self.metadata), - self.data_raw_file.clone(), - self.backing_file.as_ref().map(Arc::clone), - self.sparse, - ring_depth, - ) - .map_err(|e| BlockError::new(BlockErrorKind::Io, DiskFileError::NewAsyncIo(e)))?, - )) - } -} +use crate::{BatchRequest, RequestType, SECTOR_SIZE}; /// Per queue QCOW2 I/O worker using io_uring. /// @@ -271,7 +139,7 @@ impl AsyncIo for QcowAsync { .user_data(user_data), ) .map_err(|_| { - AsyncIoError::ReadVectored(Error::other("Submission queue is full")) + AsyncIoError::ReadVectored(io::Error::other("Submission queue is full")) })?; }; @@ -418,7 +286,9 @@ impl AsyncIo for QcowAsync { .user_data(req.user_data), ) .map_err(|_| { - AsyncIoError::ReadVectored(Error::other("Submission queue is full")) + AsyncIoError::ReadVectored(io::Error::other( + "Submission queue is full", + )) })?; } needs_submit = true; diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs index 53701cfd4..9e183dee1 100644 --- a/block/src/qcow_sync.rs +++ b/block/src/qcow_sync.rs @@ -4,156 +4,28 @@ use std::cmp::min; use std::collections::VecDeque; -use std::fs::File; -use std::os::fd::{AsFd, AsRawFd}; +use std::os::unix::io::AsRawFd; use std::sync::Arc; -use std::{fmt, io}; use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt}; -use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult, BorrowedDiskFd, DiskFileError}; -use crate::disk_file; -use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp}; -use crate::qcow::backing::shared_backing_from; +use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoResult}; use crate::qcow::decoder::Decoder; use crate::qcow::metadata::{ BackingRead, ClusterReadMapping, ClusterWriteMapping, DeallocAction, QcowMetadata, }; use crate::qcow::qcow_raw_file::QcowRawFile; -use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow}; use crate::qcow_common::{ AlignedBuf, aligned_pread, aligned_pwrite, decompress_cluster, gather_from_iovecs, gather_from_iovecs_into, pread_alloc, pread_exact, pwrite_all, scatter_to_iovecs, zero_fill_iovecs, }; -pub struct QcowDiskSync { - metadata: Arc, - /// Shared across queues, resolved once at construction. - backing_file: Option>, - sparse: bool, - data_raw_file: QcowRawFile, -} - -impl fmt::Debug for QcowDiskSync { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("QcowDiskSync") - .field("sparse", &self.sparse) - .field("has_backing", &self.backing_file.is_some()) - .finish_non_exhaustive() - } -} - -impl QcowDiskSync { - pub fn new( - file: File, - direct_io: bool, - backing_files: bool, - sparse: bool, - ) -> BlockResult { - let max_nesting_depth = if backing_files { MAX_NESTING_DEPTH } else { 0 }; - let (inner, backing_file, sparse) = - parse_qcow(RawFile::new(file, direct_io), max_nesting_depth, sparse).map_err(|e| { - let e = if !backing_files && matches!(e.kind(), BlockErrorKind::Overflow) { - e.with_kind(BlockErrorKind::UnsupportedFeature) - } else { - e - }; - e.with_op(ErrorOp::Open) - })?; - let data_raw_file = inner.raw_file.clone(); - Ok(QcowDiskSync { - metadata: Arc::new(QcowMetadata::new(inner)), - backing_file: backing_file.map(shared_backing_from).transpose()?, - sparse, - data_raw_file, - }) - } -} - -impl Drop for QcowDiskSync { - fn drop(&mut self) { - self.metadata.shutdown(); - } -} - -impl disk_file::DiskSize for QcowDiskSync { - fn logical_size(&self) -> BlockResult { - Ok(self.metadata.virtual_size()) - } -} - -impl disk_file::PhysicalSize for QcowDiskSync { - fn physical_size(&self) -> BlockResult { - Ok(self.data_raw_file.physical_size()?) - } -} - -impl disk_file::DiskFd for QcowDiskSync { - fn fd(&self) -> BorrowedDiskFd<'_> { - BorrowedDiskFd::new(self.data_raw_file.as_fd().as_raw_fd()) - } -} - -impl disk_file::Geometry for QcowDiskSync {} - -impl disk_file::SparseCapable for QcowDiskSync { - fn supports_sparse_operations(&self) -> bool { - true - } - - fn supports_zero_flag(&self) -> bool { - true - } -} - -impl disk_file::Resizable for QcowDiskSync { - fn resize(&mut self, size: u64) -> BlockResult<()> { - if self.backing_file.is_some() { - return Err(BlockError::new( - BlockErrorKind::UnsupportedFeature, - DiskFileError::ResizeError(io::Error::other( - "resize not supported with backing file", - )), - ) - .with_op(ErrorOp::Resize)); - } - self.metadata.resize(size).map_err(|e| { - BlockError::new(BlockErrorKind::Io, DiskFileError::ResizeError(e)) - .with_op(ErrorOp::Resize) - }) - } -} - -impl disk_file::DiskFile for QcowDiskSync {} - -impl disk_file::AsyncDiskFile for QcowDiskSync { - fn try_clone(&self) -> BlockResult> { - Ok(Box::new(QcowDiskSync { - metadata: Arc::clone(&self.metadata), - backing_file: self.backing_file.as_ref().map(Arc::clone), - sparse: self.sparse, - data_raw_file: self.data_raw_file.clone(), - })) - } - - // ring_depth is unused - this sync backend performs blocking I/O - // instead of submitting to an async ring. - fn create_async_io(&self, _ring_depth: u32) -> BlockResult> { - Ok(Box::new(QcowSync::new( - Arc::clone(&self.metadata), - self.data_raw_file.clone(), - self.backing_file.as_ref().map(Arc::clone), - self.sparse, - ))) - } -} - pub struct QcowSync { metadata: Arc, data_file: QcowRawFile, - /// See the backing_file field on QcowDiskSync. + /// See the backing_file field on QcowDisk. backing_file: Option>, sparse: bool, /// O_DIRECT alignment requirement (0 = no alignment needed).