From 83a294505ef0b444c14836c4fe048c578c73a3fa Mon Sep 17 00:00:00 2001 From: Dylan Reid Date: Tue, 19 May 2026 14:35:06 -0700 Subject: [PATCH] block: Add owned AsyncIo trait methods Add owned data-operation, completion, and batch methods to AsyncIo. These will be used as safe alternatives to the existing, comically unsafe, but marked safe interface. Over the course of the following commits, users are converted to the new interface and after all users of the unsound interfaces are removed, they are removed. Signed-off-by: Dylan Reid --- block/src/async_io.rs | 94 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/block/src/async_io.rs b/block/src/async_io.rs index f54ecb5a7..eb97e8b05 100644 --- a/block/src/async_io.rs +++ b/block/src/async_io.rs @@ -13,6 +13,8 @@ mod owned_io_buffer; #[cfg(feature = "io_uring")] mod uring_data_io; +use std::io; + pub use aio_data_io::AioDataIo; pub use completion::AsyncIoCompletion; pub use guest_memory_target::GuestMemoryTarget; @@ -111,16 +113,106 @@ pub trait AsyncIo: Send { iovecs: &[libc::iovec], user_data: u64, ) -> AsyncIoResult<()>; + + /// Submits one owned data operation. + /// + /// Takes ownership of `op`. + /// Implementations that complete asynchronously must retain it until its + /// completion is returned. + fn submit_data_operation(&mut self, op: AsyncIoOperation) -> AsyncIoResult<()> { + let error = + io::Error::other("owned async I/O operations are not supported by this backend"); + if op.is_read() { + Err(AsyncIoError::ReadVectored(error)) + } else { + Err(AsyncIoError::WriteVectored(error)) + } + } + + /// Submits a read from `offset` into guest memory. + fn read_to_memory( + &mut self, + offset: libc::off_t, + target: GuestMemoryTarget, + user_data: u64, + ) -> AsyncIoResult<()> { + self.submit_data_operation(AsyncIoOperation::read_to_memory(offset, target, user_data)) + } + + /// Submits a write to `offset` from guest memory. + fn write_from_memory( + &mut self, + offset: libc::off_t, + target: GuestMemoryTarget, + user_data: u64, + ) -> AsyncIoResult<()> { + self.submit_data_operation(AsyncIoOperation::write_from_memory( + offset, target, user_data, + )) + } + + /// Submits a read from `offset` into an owned host-memory buffer. + fn read_to_vec( + &mut self, + offset: libc::off_t, + buffer: OwnedIoBuffer, + user_data: u64, + ) -> AsyncIoResult<()> { + self.submit_data_operation(AsyncIoOperation::read_to_vec(offset, buffer, user_data)) + } + + /// Submits a write to `offset` from an owned host-memory buffer. + fn write_from_vec( + &mut self, + offset: libc::off_t, + buffer: OwnedIoBuffer, + user_data: u64, + ) -> AsyncIoResult<()> { + self.submit_data_operation(AsyncIoOperation::write_from_vec(offset, buffer, user_data)) + } + 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)>; + + /// Returns the next owned completion, if one is available. + /// + /// Read completions from owned host-memory buffers return that buffer here. + fn next_completion(&mut self) -> Option { + self.next_completed_request() + .map(|(user_data, result)| AsyncIoCompletion::new(user_data, result, None)) + } + + fn next_completed_request(&mut self) -> Option<(u64, i32)> { + self.next_completion() + .map(|completion| (completion.user_data, completion.result)) + } + fn batch_requests_enabled(&self) -> bool { false } + fn submit_batch_requests(&mut self, _batch_request: &[BatchRequest]) -> AsyncIoResult<()> { Ok(()) } + + /// Submits a batch of owned data operations. + /// + /// Backends either accept the whole batch for eventual completion or return + /// an error before taking ownership of any operation. + fn submit_batch_operations( + &mut self, + batch_request: Vec, + ) -> AsyncIoResult<()> { + if batch_request.is_empty() { + Ok(()) + } else { + Err(AsyncIoError::SubmitBatchRequests(io::Error::other( + "batch requests are not supported by this backend", + ))) + } + } + fn alignment(&self) -> u64 { SECTOR_SIZE }