// Copyright © 2021 Intel Corporation // // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause use std::marker::PhantomData; use std::os::fd::{AsRawFd, OwnedFd, RawFd}; use thiserror::Error; use vmm_sys_util::eventfd::EventFd; use crate::{BatchRequest, DiskTopology, SECTOR_SIZE}; #[derive(Error, Debug)] pub enum DiskFileError { /// Failed getting disk file size. #[error("Failed getting disk file size")] Size(#[source] std::io::Error), /// Failed creating a new AsyncIo. #[error("Failed creating a new AsyncIo")] NewAsyncIo(#[source] std::io::Error), /// Unsupported operation. #[error("Unsupported operation")] Unsupported, /// Resize failed #[error("Resize failed")] ResizeError(#[source] std::io::Error), #[error("Failed cloning disk file")] Clone(#[source] std::io::Error), } pub type DiskFileResult = std::result::Result; /// A wrapper for [`RawFd`] capturing the lifetime of a corresponding [`DiskFile`]. /// /// This fulfills the same role as [`BorrowedFd`] but is tailored to the limitations /// by some implementations of [`DiskFile`], which wrap the effective [`File`] /// in an `Arc>`, making the use of [`BorrowedFd`] impossible. /// /// [`BorrowedFd`]: std::os::fd::BorrowedFd #[derive(Copy, Clone, Debug)] pub struct BorrowedDiskFd<'fd> { raw_fd: RawFd, _lifetime: PhantomData<&'fd OwnedFd>, } impl BorrowedDiskFd<'_> { pub(super) fn new(raw_fd: RawFd) -> Self { Self { raw_fd, _lifetime: PhantomData, } } } impl AsRawFd for BorrowedDiskFd<'_> { fn as_raw_fd(&self) -> RawFd { self.raw_fd } } /// Abstraction over the effective [`File`] backing up a block device, /// with support for synchronous and asynchronous I/O. /// /// This allows abstracting over raw image formats as well as structured /// image formats. pub trait DiskFile: Send { /// Returns the logical disk size a guest will see. /// /// For raw formats, this is equal to [`Self::physical_size`]. For file formats /// that wrap disk images in a container (e.g. QCOW2), this refers to the /// effective size that the guest will see. fn logical_size(&mut self) -> DiskFileResult; /// Returns the physical size of the underlying file. fn physical_size(&mut self) -> DiskFileResult; fn new_async_io(&self, ring_depth: u32) -> DiskFileResult>; fn topology(&mut self) -> DiskTopology { DiskTopology::default() } fn resize(&mut self, _size: u64) -> DiskFileResult<()> { Err(DiskFileError::Unsupported) } /// Indicates support for sparse operations (punch hole, write zeroes, discard). /// Override to return true when supported. fn supports_sparse_operations(&self) -> bool { false } /// Indicates support for zero flag optimization in WRITE_ZEROES. Override /// to return true when supported. fn supports_zero_flag(&self) -> bool { false } /// Returns the file descriptor of the underlying disk image file. /// /// The file descriptor is supposed to be used for `fcntl()` calls but no /// other operation. fn fd(&mut self) -> BorrowedDiskFd<'_>; } #[derive(Error, Debug)] pub enum AsyncIoError { /// Failed vectored reading from file. #[error("Failed vectored reading from file")] ReadVectored(#[source] std::io::Error), /// Failed vectored writing to file. #[error("Failed vectored writing to file")] WriteVectored(#[source] std::io::Error), /// Failed synchronizing file. #[error("Failed synchronizing file")] Fsync(#[source] std::io::Error), /// Failed punching hole. #[error("Failed punching hole")] PunchHole(#[source] std::io::Error), /// Failed writing zeroes. #[error("Failed writing zeroes")] WriteZeroes(#[source] std::io::Error), /// Failed submitting batch requests. #[error("Failed submitting batch requests")] SubmitBatchRequests(#[source] std::io::Error), } pub type AsyncIoResult = std::result::Result; pub trait AsyncIo: Send { fn notifier(&self) -> &EventFd; fn read_vectored( &mut self, offset: libc::off_t, iovecs: &[libc::iovec], user_data: u64, ) -> AsyncIoResult<()>; fn write_vectored( &mut self, offset: libc::off_t, iovecs: &[libc::iovec], user_data: u64, ) -> AsyncIoResult<()>; fn fsync(&mut self, user_data: Option) -> AsyncIoResult<()>; fn punch_hole(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()>; fn write_zeroes(&mut self, offset: u64, length: u64, user_data: u64) -> AsyncIoResult<()>; fn next_completed_request(&mut self) -> Option<(u64, i32)>; fn batch_requests_enabled(&self) -> bool { false } fn submit_batch_requests(&mut self, _batch_request: &[BatchRequest]) -> AsyncIoResult<()> { Ok(()) } fn alignment(&self) -> u64 { SECTOR_SIZE } }