1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::{
16     is_aligned, is_buffer_aligned, BlockInfo, BlockIo, BlockIoError, Result, StorageError,
17 };
18 use core::{marker::PhantomData, mem::swap};
19 
20 /// `IoStatus` represents the status of a non-blocking IO.
21 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
22 pub enum IoStatus {
23     /// The IO request is aborted due to error or user request.
24     Aborted,
25     /// The IO request is completed.
26     Completed,
27     /// The IO request is still pending.
28     Pending,
29     /// The IO request doesn't exist.
30     NotFound,
31 }
32 
33 /// `NonBlockingBlockIo` provides interfaces for performing non-blocking read/write.
34 ///
35 /// # Safety
36 ///
37 /// * Implementation must guarantee that `Self::check_status(buffer)` returns `IoStatus::Pending`
38 ///   if and only if it is retaining `buffer`. Once the implementation stops returning
39 ///   `IoStatus::Pending` for a buffer, it must not retain the buffer again until a new read/write
40 ///   request using the buffer is made.
41 /// * The buffer pointer passed to `Self::check_status(buffer)` should only be used as a key value
42 ///   for looking up previously made read/write IO requests being tracked. Implementation should not
43 ///   attempt to derefernce it without confirming that the corresponding IO exists. If caller passes
44 ///   an invalid buffer pointer, implementation should be able to safely return
45 ///   `IoStatus::NotFound`.
46 /// * If `Self::write_blocks()`/`Self::read_blocks()` returns error, the input buffer must not be
47 ///   retained.
48 pub unsafe trait NonBlockingBlockIo {
49     /// Returns the `BlockInfo` for this block device.
info(&mut self) -> BlockInfo50     fn info(&mut self) -> BlockInfo;
51 
52     /// Perform non-blocking writes of data to the block device.
53     ///
54     /// # Args
55     ///
56     /// * `blk_offset`: Offset in number of blocks.
57     /// * `buffer`: Pointer to the data buffer.
58     ///
59     /// # Returns
60     ///
61     /// * Returns Ok(()) if the IO request is accepted.
62     /// * Returns Err(BlockIoError::MediaBusy) if the device is busy and the caller should try
63     ////  again later.
64     /// * Returns Err(BlockIoError::Others()) for other errors.
65     ///
66     /// # Safety
67     ///
68     /// * Caller must ensure that `buffer` points to a valid buffer.
69     /// * If the method returns Ok(()), caller must ensure that `buffer` remains valid and has no
70     ///   other references until `Self::check_status(buffer)` no longer returns
71     ///   `IoStatus::Pending`.
write_blocks( &mut self, blk_offset: u64, buffer: *mut [u8], ) -> core::result::Result<(), BlockIoError>72     unsafe fn write_blocks(
73         &mut self,
74         blk_offset: u64,
75         buffer: *mut [u8],
76     ) -> core::result::Result<(), BlockIoError>;
77 
78     /// Perform non-blocking read of data from the block device.
79     ///
80     /// # Args
81     ///
82     /// * `blk_offset`: Offset in number of blocks.
83     /// * `buffer`: Pointer to the output buffer.
84     ///
85     /// # Returns
86     ///
87     /// * Returns Ok(()) if the IO request is accepted.
88     /// * Returns Err(BlockIoError::MediaBusy) if the device is busy and the caller should try again
89     ///   later.
90     /// * Returns Err(BlockIoError::Others()) for other errors.
91     ///
92     /// # Safety
93     ///
94     /// * Caller must ensure that `buffer` points to a valid buffer.
95     /// * If the method returns Ok(()), caller must ensure that `buffer` remains valid and has no
96     ///   other references until `Self::check_status(buffer)` no longer returns
97     ///   `IoStatus::Pending`.
read_blocks( &mut self, blk_offset: u64, buffer: *mut [u8], ) -> core::result::Result<(), BlockIoError>98     unsafe fn read_blocks(
99         &mut self,
100         blk_offset: u64,
101         buffer: *mut [u8],
102     ) -> core::result::Result<(), BlockIoError>;
103 
104     /// Checks the status of the non-blocking read/write associated with the given buffer.
105     ///
106     /// # Args
107     ///
108     /// * `buf`: The buffer previously passed to `read_blocks()` / `write_blocks()`.
109     ///
110     /// # Returns
111     ///
112     /// * Returns `IoStatus::NotFound` if the request is not found.
113     /// * Returns `IoStatus::Pending` if the request is still pending.
114     /// * Returns `IoStatus::Completed` if the request has been completed successfully.
115     ///   Implementation can stop tracking the request and should return `IoStatus::NotFound` on
116     ///   subsequent queries until a new read/write request is made with the same buffer.
117     /// * Returns `IoStatus::Aborted` if the request is aborted, due to error or caller invoking
118     ///   `Self::abort()`. Implementation can stop tracking the request and should return
119     ///   `IoStatus::NotFound` on subsequent queries until a new read/write request is made with
120     ///   the same buffer.
check_status(&mut self, buf: *mut [u8]) -> IoStatus121     fn check_status(&mut self, buf: *mut [u8]) -> IoStatus;
122 
123     /// Aborts pending non-blocking IO requests.
124     ///
125     /// For currently pending requests, `Self::check_status(buf)` should eventually return
126     /// `IoStatus::Aborted` at some point in the future. For already completed requests,
127     /// `Self::check_status(buf)` should continue to return `IoStatus::Completed`.
abort(&mut self) -> core::result::Result<(), BlockIoError>128     fn abort(&mut self) -> core::result::Result<(), BlockIoError>;
129 }
130 
131 // Implements the blocking version `BlockIo` for a `&mut dyn NonBlockingBlockIo`
132 impl BlockIo for &mut dyn NonBlockingBlockIo {
info(&mut self) -> BlockInfo133     fn info(&mut self) -> BlockInfo {
134         (*self).info()
135     }
136 
read_blocks( &mut self, blk_offset: u64, out: &mut [u8], ) -> core::result::Result<(), BlockIoError>137     fn read_blocks(
138         &mut self,
139         blk_offset: u64,
140         out: &mut [u8],
141     ) -> core::result::Result<(), BlockIoError> {
142         let ptr = out as *mut [u8];
143         // SAFETY:
144         // * This function blocks until the non-blocking IO is no longer pending.
145         // * Buffer by `ptr` is not used elsewhere.
146         unsafe { (*self).read_blocks(blk_offset, ptr)? };
147         loop {
148             match self.check_status(ptr) {
149                 IoStatus::Pending => {}
150                 IoStatus::Completed => return Ok(()),
151                 IoStatus::Aborted => return Err(BlockIoError::Others(Some("Read aborted"))),
152                 IoStatus::NotFound => panic!("Unexpected IoStatus::NotFound"),
153             }
154         }
155     }
156 
write_blocks( &mut self, blk_offset: u64, data: &mut [u8], ) -> core::result::Result<(), BlockIoError>157     fn write_blocks(
158         &mut self,
159         blk_offset: u64,
160         data: &mut [u8],
161     ) -> core::result::Result<(), BlockIoError> {
162         let ptr = data as *mut [u8];
163         // SAFETY:
164         // * This function blocks until the non-blocking IO is no longer pending.
165         // * Buffer by `ptr` is not used elsewhere.
166         unsafe { (*self).write_blocks(blk_offset, ptr)? };
167         loop {
168             match self.check_status(ptr) {
169                 IoStatus::Pending => {}
170                 IoStatus::Completed => return Ok(()),
171                 IoStatus::Aborted => return Err(BlockIoError::Others(Some("write aborted"))),
172                 IoStatus::NotFound => panic!("Unexpected IoStatus::NotFound"),
173             }
174         }
175     }
176 }
177 
178 /// `BlockDeviceIo` represents either a `BlockIo` or `NonBlockingBlockIo`.
179 pub enum BlockDeviceIo<'a> {
180     Blocking(&'a mut dyn BlockIo),
181     NonBlocking(&'a mut dyn NonBlockingBlockIo),
182 }
183 
184 impl<'a> From<&'a mut dyn BlockIo> for BlockDeviceIo<'a> {
from(val: &'a mut dyn BlockIo) -> Self185     fn from(val: &'a mut dyn BlockIo) -> Self {
186         Self::Blocking(val)
187     }
188 }
189 
190 impl<'a> From<&'a mut dyn NonBlockingBlockIo> for BlockDeviceIo<'a> {
from(val: &'a mut dyn NonBlockingBlockIo) -> Self191     fn from(val: &'a mut dyn NonBlockingBlockIo) -> Self {
192         Self::NonBlocking(val)
193     }
194 }
195 
196 impl<'a> BlockDeviceIo<'a> {
197     /// Casts to a `BlockIo` trait object.
as_block_io(&mut self) -> &mut dyn BlockIo198     fn as_block_io(&mut self) -> &mut dyn BlockIo {
199         match self {
200             Self::Blocking(v) => *v,
201             Self::NonBlocking(v) => v,
202         }
203     }
204 
205     /// Creates a sub-instance that borrows internal fields.
206     ///
207     /// This creates an instance where its lifetime parameter 'a is coerced to the life time of the
208     /// current object. This will be used for creating a local instance for blocking operation.
scoped_instance(&mut self) -> BlockDeviceIo209     fn scoped_instance(&mut self) -> BlockDeviceIo {
210         match self {
211             Self::Blocking(v) => BlockDeviceIo::Blocking(*v),
212             Self::NonBlocking(v) => BlockDeviceIo::NonBlocking(*v),
213         }
214     }
215 }
216 
217 /// `IoBufferState` wraps a raw buffer and keeps track of its use state in non-blocking IO.
218 #[derive(Debug)]
219 enum IoBufferState<'a> {
220     Ready(&'a mut [u8], IoStatus),
221     // (Original buffer &mut [u8], subslice pointer passed to non-blocking IO, phantom)
222     Pending(*mut [u8], *mut [u8], PhantomData<&'a mut [u8]>),
223 }
224 
225 /// `IoBuffer` wraps a `IoBufferState` and implements `Drop` to check that the buffer is not
226 /// pending when going out of scope.
227 #[derive(Debug)]
228 struct IoBuffer<'a>(IoBufferState<'a>);
229 
230 impl Drop for IoBuffer<'_> {
drop(&mut self)231     fn drop(&mut self) {
232         // Panics if an `IoBuffer` goes out of scope in a pending state.
233         // This is merely used for safety reasoning in `read_io_buffer()`/`write_io_buffer()` and
234         // should not be triggered if implementation logic is incorrect. Specifically, `IoBuffer`
235         // is only used internally in `BlockDeviceEx`. When `BlockDeviceEx` goes out of scope, it
236         // performs abort() and sync() to make sure no buffer is pending.
237         assert!(!self.is_pending());
238     }
239 }
240 
241 impl<'a> IoBuffer<'a> {
242     /// Creates a new instance.
new(buffer: &'a mut [u8]) -> Self243     fn new(buffer: &'a mut [u8]) -> Self {
244         Self(IoBufferState::Ready(buffer, IoStatus::Completed))
245     }
246 
247     /// Gets the cached status.
248     ///
249     /// To update the cached status, caller should call `Self::update()` first.
status(&self) -> IoStatus250     fn status(&self) -> IoStatus {
251         match self.0 {
252             IoBufferState::Ready(_, status) => status,
253             _ => IoStatus::Pending,
254         }
255     }
256 
257     /// Returns whether the buffer is pending in a non-blocking IO.
258     ///
259     /// The returned value is based on the cached status. To update the cached status, caller
260     /// should call `Self::update()` first.
is_pending(&self) -> bool261     fn is_pending(&self) -> bool {
262         matches!(self.status(), IoStatus::Pending)
263     }
264 
265     /// Returns whether the corresponding IO is aborted.
266     ///
267     /// The returned value is based on the cached status. To update the cached status, caller
268     /// should call `Self::update()` first.
is_aborted(&self) -> bool269     fn is_aborted(&self) -> bool {
270         matches!(self.status(), IoStatus::Aborted)
271     }
272 
273     /// Sets buffer to the pending state.
274     ///
275     /// Returns the pointer to the specified subslice that can be passed to
276     /// `NonBlockingBlockIo:read_blocks()` and `NonBlockingBlockIo::write_blocks()` interfaces.
set_pending(&mut self, io_offset: usize, io_size: usize) -> *mut [u8]277     fn set_pending(&mut self, io_offset: usize, io_size: usize) -> *mut [u8] {
278         match &mut self.0 {
279             IoBufferState::Ready(b, _) => {
280                 let ptr = &mut b[io_offset..][..io_size] as *mut [u8];
281                 self.0 = IoBufferState::Pending(*b as _, ptr, PhantomData);
282                 ptr
283             }
284             _ => unreachable!(),
285         }
286     }
287 
288     /// Gets the buffer if not pending
get(&mut self) -> &mut [u8]289     fn get(&mut self) -> &mut [u8] {
290         match &mut self.0 {
291             IoBufferState::Ready(buffer, _) => buffer,
292             _ => unreachable!(),
293         }
294     }
295 
296     /// Updates the IO status
update(&mut self, io: &mut dyn NonBlockingBlockIo)297     fn update(&mut self, io: &mut dyn NonBlockingBlockIo) {
298         match &mut self.0 {
299             IoBufferState::Ready(_, _) => {}
300             IoBufferState::Pending(buffer, ptr, _) => {
301                 match io.check_status(*ptr) {
302                     IoStatus::NotFound => unreachable!(), // Logic error.
303                     IoStatus::Pending => {}
304                     v => {
305                         // SAFETY:
306                         // * `buffer` is a valid pointer as it came from
307                         //   `IoBufferState::Ready(buffer, _)`
308                         // * status is no longer pending, buffer is not retained any more.
309                         self.0 = IoBufferState::Ready(unsafe { &mut **buffer }, v);
310                     }
311                 }
312             }
313         }
314     }
315 
316     /// Consumes and returns the raw buffer.
take(mut self) -> &'a mut [u8]317     fn take(mut self) -> &'a mut [u8] {
318         match &mut self.0 {
319             IoBufferState::Ready(buffer, _) => {
320                 // IoBuffer has a drop implementation, thus we can't move buffer out directly.
321                 // The solution is to swap with an empty slice, which is valid for any lifetime.
322                 let mut res = &mut [][..];
323                 swap(&mut res, buffer);
324                 res
325             }
326             _ => unreachable!(), // Logic error.
327         }
328     }
329 }
330 
331 /// `Transaction` tracks the non-blocking read/write IO request made by
332 /// `BlockDeviceEx::read_scoped()` and `BlockDeviceEx::write_scoped()`. It automatically performs
333 /// sync when going out of scope.
334 pub struct Transaction<'a, 'b> {
335     dev: BlockDeviceEx<'a, 'a>,
336     _phantom: PhantomData<&'b mut [u8]>,
337 }
338 
339 impl Transaction<'_, '_> {
340     /// Wait until the IO request is either completed/aborted and consume the transaction.
sync(mut self) -> Result<()>341     pub fn sync(mut self) -> Result<()> {
342         self.do_sync()
343     }
344 
345     /// Helper method for performing the sync.
do_sync(&mut self) -> Result<()>346     fn do_sync(&mut self) -> Result<()> {
347         self.dev.sync()?;
348         match self.dev.is_aborted() {
349             true => Err(StorageError::IoAborted),
350             _ => Ok(()),
351         }
352     }
353 }
354 
355 impl Drop for Transaction<'_, '_> {
drop(&mut self)356     fn drop(&mut self) {
357         // We expect caller to sync() themselves if they expect errors. If not the drop will
358         // perform the sync but panics on error.
359         self.do_sync().unwrap()
360     }
361 }
362 
363 /// `BlockDeviceEx` provides safe APIs for performing blocking/non-blocking read/write.
364 ///
365 /// `'a`: Lifetime of the borrow to BlockIo / NonBlockingBlockIo,
366 /// `'b`: Lifetime of the external user buffers that will be passed to `Self::read()` and
367 ///       `Self::write()`.
368 pub struct BlockDeviceEx<'a, 'b> {
369     io: BlockDeviceIo<'a>,
370     current_io: Option<IoBuffer<'b>>,
371 }
372 
373 impl<'a, 'b> BlockDeviceEx<'a, 'b> {
374     /// Creates a new instance.
new(io: BlockDeviceIo<'a>) -> Self375     pub fn new(io: BlockDeviceIo<'a>) -> Self {
376         Self { io, current_io: None }
377     }
378 
379     /// Checks if any IO buffer is pending.
is_pending(&self) -> bool380     pub fn is_pending(&self) -> bool {
381         self.current_io.as_ref().map(|v| v.is_pending()).unwrap_or(false)
382     }
383 
384     /// Updates the IO status.
update_status(&mut self)385     fn update_status(&mut self) {
386         let BlockDeviceIo::NonBlocking(ref mut io) = self.io else {
387             return;
388         };
389 
390         match self.current_io.as_mut() {
391             Some(buffer) => buffer.update(*io),
392             _ => {}
393         }
394     }
395 
396     /// Polls and updates IO status.
poll(&mut self)397     pub fn poll(&mut self) {
398         if self.current_io.is_some() {
399             self.update_status();
400         }
401     }
402 
403     /// Aborts the current IO.
abort(&mut self) -> Result<()>404     pub fn abort(&mut self) -> Result<()> {
405         match &mut self.io {
406             BlockDeviceIo::NonBlocking(io) => Ok(io.abort()?),
407             _ => Ok(()),
408         }
409     }
410 
411     /// Checks if any IO is aborted.
is_aborted(&self) -> bool412     pub fn is_aborted(&self) -> bool {
413         match self.current_io.as_ref() {
414             Some(buffer) => buffer.is_aborted(),
415             _ => false,
416         }
417     }
418 
419     /// Waits until the IO is completed or aborted.
sync(&mut self) -> Result<()>420     pub fn sync(&mut self) -> Result<()> {
421         while self.is_pending() {
422             self.poll();
423         }
424         Ok(())
425     }
426 
427     /// Checks whether an IO is currently in progress.
check_busy(&self) -> Result<()>428     fn check_busy(&self) -> Result<()> {
429         // No IO implies not pending.
430         match self.current_io.is_some() {
431             true => Err(StorageError::NotReady),
432             _ => Ok(()),
433         }
434     }
435 
436     /// Writes data from `buffer[offset..][..size]` to the block device at offset `dst_offset`.
437     ///
438     /// # Args
439     ///
440     /// * `dst_offset`: Destination offset to write in the block device.
441     /// * `buffer`: On input, it must be a `Some(buffer)` that contains the data to write. On
442     ///   success, the buffer will be taken and it will be set to `None`. On error, `buffer` will
443     ///   remain the same so that caller can continue to access the buffer. When the IO completes
444     ///   or aborts, caller can retrieve the buffer via `Self::take_io_buffer()`.
445     /// * `offset`: Offset of the data to write in `buffer`.
446     /// * `size`: Size of the data to write.
write( &mut self, dst_offset: u64, buffer: &mut Option<&'b mut [u8]>, offset: usize, size: usize, ) -> Result<()>447     pub fn write(
448         &mut self,
449         dst_offset: u64,
450         buffer: &mut Option<&'b mut [u8]>,
451         offset: usize,
452         size: usize,
453     ) -> Result<()> {
454         self.check_busy()?;
455         let blk_size = self.io.as_block_io().block_size();
456         // TODO(b/338439051): Implement support for arbitrarily aligned buffer and read range.
457         assert_eq!(dst_offset % blk_size, 0);
458         let buffer_raw = buffer.take().ok_or(StorageError::InvalidInput)?;
459         let mut io_buffer = IoBuffer::new(buffer_raw);
460         match write_io_buffer(&mut self.io, dst_offset / blk_size, &mut io_buffer, offset, size) {
461             Err(e) => {
462                 // Error. Returns the buffer to caller.
463                 *buffer = Some(io_buffer.take());
464                 Err(e)
465             }
466             Ok(()) => {
467                 self.current_io = Some(io_buffer);
468                 Ok(())
469             }
470         }
471     }
472 
473     /// Reads data from the block device at offset `dst_offset` into `buffer[offset..][..size]`.
474     ///
475     /// # Args
476     ///
477     /// * `dst_offset`: Destination offset to read from the block device.
478     /// * `buffer`: On input, it must be a `Some(buffer)` that contains the output buffer. On
479     ///   success, the buffer will be taken and it will be set to `None`. On error, `buffer` will
480     ///   remain the same so that caller can continue to access the buffer. When the IO completes
481     ///   or aborts, caller can retrieve the buffer via `Self::take_io_buffer()`.
482     /// * `offset`: Offset of `buffer` to read to.
483     /// * `size`: Size of the read.
read( &mut self, dst_offset: u64, buffer: &mut Option<&'b mut [u8]>, offset: usize, size: usize, ) -> Result<()>484     pub fn read(
485         &mut self,
486         dst_offset: u64,
487         buffer: &mut Option<&'b mut [u8]>,
488         offset: usize,
489         size: usize,
490     ) -> Result<()> {
491         self.check_busy()?;
492         let blk_size = self.io.as_block_io().block_size();
493         // TODO(b/338439051): Implement support for arbitrarily aligned buffer and read range.
494         assert_eq!(dst_offset % blk_size, 0);
495         let buffer_raw = buffer.take().ok_or(StorageError::InvalidInput)?;
496         let mut io_buffer = IoBuffer::new(buffer_raw);
497         match read_io_buffer(&mut self.io, dst_offset / blk_size, &mut io_buffer, offset, size) {
498             Err(e) => {
499                 // Error. Returns the buffer to caller.
500                 *buffer = Some(io_buffer.take());
501                 Err(e)
502             }
503             Ok(()) => {
504                 self.current_io = Some(io_buffer);
505                 Ok(())
506             }
507         }
508     }
509 
510     /// Retrieves the IO buffer if it is completed/aborted.
take_io_buffer(&mut self) -> Result<&'b mut [u8]>511     pub fn take_io_buffer(&mut self) -> Result<&'b mut [u8]> {
512         match self.current_io {
513             None => Err(StorageError::NotExist),
514             Some(_) => match !self.is_pending() {
515                 true => Ok(self.current_io.take().unwrap().take()),
516                 _ => Err(StorageError::NotReady),
517             },
518         }
519     }
520 
521     /// Returns an instance that borrows the internal field.
522     ///
523     /// This creates an instance where its lifetime parameter 'a/'b/'c is coerced to the life time
524     /// of the current object. This will be used for creating a local instance for blocking
525     /// operation.
scoped_instance(&mut self) -> Result<BlockDeviceEx>526     fn scoped_instance(&mut self) -> Result<BlockDeviceEx> {
527         self.check_busy()?;
528         Ok(BlockDeviceEx { io: self.io.scoped_instance(), current_io: None })
529     }
530 
531     /// Performs a non-blocking write and returns a `Transanction` object which automatically
532     /// performs sync when going out of scope.
write_scoped<'c, 'd: 'c>( &'c mut self, offset: u64, data: &'d mut [u8], ) -> Result<Transaction<'c, 'd>>533     pub fn write_scoped<'c, 'd: 'c>(
534         &'c mut self,
535         offset: u64,
536         data: &'d mut [u8],
537     ) -> Result<Transaction<'c, 'd>> {
538         let mut dev = self.scoped_instance()?;
539         let len = data.len();
540         dev.write(offset, &mut Some(data), 0, len)?;
541         Ok(Transaction { dev, _phantom: PhantomData })
542     }
543 
544     /// Performs a non-blocking read and returns a `Transanction` object which automatically
545     /// performs sync when going out of scope.
read_scoped<'c, 'd: 'c>( &'c mut self, offset: u64, out: &'d mut [u8], ) -> Result<Transaction<'c, 'd>>546     pub fn read_scoped<'c, 'd: 'c>(
547         &'c mut self,
548         offset: u64,
549         out: &'d mut [u8],
550     ) -> Result<Transaction<'c, 'd>> {
551         let mut dev = self.scoped_instance()?;
552         let len = out.len();
553         dev.read(offset, &mut Some(out), 0, len)?;
554         Ok(Transaction { dev, _phantom: PhantomData })
555     }
556 
557     /// Performs blocking write.
write_blocking(&mut self, offset: u64, data: &mut [u8]) -> Result<()>558     pub fn write_blocking(&mut self, offset: u64, data: &mut [u8]) -> Result<()> {
559         self.write_scoped(offset, data)?.sync()
560     }
561 
562     /// Performs blocking read.
read_blocking(&mut self, offset: u64, out: &mut [u8]) -> Result<()>563     pub fn read_blocking(&mut self, offset: u64, out: &mut [u8]) -> Result<()> {
564         self.read_scoped(offset, out)?.sync()
565     }
566 }
567 
568 impl Drop for BlockDeviceEx<'_, '_> {
drop(&mut self)569     fn drop(&mut self) {
570         self.abort().unwrap();
571         self.sync().unwrap();
572     }
573 }
574 
575 /// A helper to write an IO buffer to the block device.
write_io_buffer( io: &mut BlockDeviceIo, blk_offset: u64, buffer: &mut IoBuffer, offset: usize, size: usize, ) -> Result<()>576 fn write_io_buffer(
577     io: &mut BlockDeviceIo,
578     blk_offset: u64,
579     buffer: &mut IoBuffer,
580     offset: usize,
581     size: usize,
582 ) -> Result<()> {
583     let data = &mut buffer.get()[offset..][..size];
584     assert!(is_buffer_aligned(data, io.as_block_io().alignment().into())?);
585     assert!(is_aligned(size.into(), io.as_block_io().block_size().into())?);
586     Ok(match io {
587         BlockDeviceIo::Blocking(io) => io.write_blocks(blk_offset, data),
588         BlockDeviceIo::NonBlocking(io) => {
589             let ptr = buffer.set_pending(offset, size);
590             // SAFETY:
591             // * `buffer.set_pending()` makes sure that no safe code can use the buffer until it is
592             //   set ready by `IoBuffer::update_status()` when status is no longer
593             //   `IoStatus::Pending`.
594             // * When going out of scope, `IoBuffer` checks whether the buffer is still pending and
595             //   will panic if it is. Thus the buffer will remain valid with no other references
596             //   during the non-blocking IO.
597             unsafe { (*io).write_blocks(blk_offset, ptr) }
598         }
599     }?)
600 }
601 
602 /// A helper to read data from a block device to an IO buffer.
read_io_buffer( io: &mut BlockDeviceIo, blk_offset: u64, buffer: &mut IoBuffer, offset: usize, size: usize, ) -> Result<()>603 fn read_io_buffer(
604     io: &mut BlockDeviceIo,
605     blk_offset: u64,
606     buffer: &mut IoBuffer,
607     offset: usize,
608     size: usize,
609 ) -> Result<()> {
610     let out = &mut buffer.get()[offset..][..size];
611     assert!(is_buffer_aligned(out, io.as_block_io().alignment().into())?);
612     assert!(is_aligned(size.into(), io.as_block_io().block_size().into())?);
613     Ok(match io {
614         BlockDeviceIo::Blocking(io) => io.read_blocks(blk_offset, out),
615         BlockDeviceIo::NonBlocking(io) => {
616             let ptr = buffer.set_pending(offset, size);
617             // SAFETY:
618             // * `buffer.set_pending()` makes sure that no safe code can use the buffer until it is
619             //   set ready by `IoBuffer::update_status()` when status is no longer
620             //   `IoStatus::Pending`.
621             // * When going out of scope, `IoBuffer` checks whether the buffer is still pending and
622             //   will panic if it is. Thus the buffer will remain valid with no other references
623             //   during the non-blocking IO.
624             unsafe { (*io).read_blocks(blk_offset, ptr) }
625         }
626     }?)
627 }
628 
629 #[cfg(test)]
630 mod test {
631     use gbl_storage_testlib::{TestBlockDeviceBuilder, TimestampPauser};
632 
633     #[test]
test_read()634     fn test_read() {
635         let mut blk = TestBlockDeviceBuilder::new()
636             .set_alignment(1)
637             .set_block_size(1)
638             .set_data(&[1, 2, 3, 4])
639             .build();
640         let mut io_buffer = [1, 0, 0, 1];
641         let mut to_write = Some(&mut io_buffer[..]);
642         {
643             let timestamp_pauser = TimestampPauser::new();
644             let mut blk_ex = blk.as_block_device_ex();
645             blk_ex.write(1, &mut to_write, 1, 2).unwrap();
646             assert!(to_write.is_none());
647             // Timestamp paused. IO not being processed. poll() should return false.
648             blk_ex.poll();
649             assert!(blk_ex.is_pending());
650 
651             timestamp_pauser.resume();
652             blk_ex.sync().unwrap();
653             blk_ex.poll();
654         }
655         assert_eq!(blk.io.storage, [1, 0, 0, 4]);
656     }
657 
658     #[test]
test_write()659     fn test_write() {
660         let mut blk = TestBlockDeviceBuilder::new()
661             .set_alignment(1)
662             .set_block_size(1)
663             .set_data(&[1, 2, 3, 4])
664             .build();
665         let mut io_buffer = [1, 0, 0, 1];
666         let mut to_read = Some(&mut io_buffer[..]);
667         {
668             let timestamp_pauser = TimestampPauser::new();
669             let mut blk_ex = blk.as_block_device_ex();
670             blk_ex.read(1, &mut to_read, 1, 2).unwrap();
671             assert!(to_read.is_none());
672             // Timestamp paused. IO not being processed.
673             blk_ex.poll();
674             assert!(blk_ex.is_pending());
675 
676             timestamp_pauser.resume();
677             blk_ex.sync().unwrap();
678             blk_ex.poll();
679         }
680         assert_eq!(io_buffer, [1, 2, 3, 1]);
681     }
682 
683     #[test]
test_read_write_blocking()684     fn test_read_write_blocking() {
685         let mut blk = TestBlockDeviceBuilder::new()
686             .set_alignment(1)
687             .set_block_size(1)
688             .set_data(&[1, 2, 3, 4])
689             .build();
690 
691         let mut io_buffer = [0u8; 2];
692         blk.as_block_device_ex().read_blocking(1, &mut io_buffer[..]).unwrap();
693         assert_eq!(io_buffer, [2, 3]);
694 
695         let mut io_buffer = [0u8; 2];
696         blk.as_block_device_ex().write_blocking(1, &mut io_buffer[..]).unwrap();
697         assert_eq!(blk.io.storage, [1, 0, 0, 4]);
698     }
699 
700     #[test]
test_abort()701     fn test_abort() {
702         let mut blk = TestBlockDeviceBuilder::new()
703             .set_alignment(1)
704             .set_block_size(1)
705             .set_data(&[1, 2, 3, 4])
706             .build();
707         let mut io_buffer = [1, 0, 0, 1];
708         let mut to_write = Some(&mut io_buffer[..]);
709         {
710             let _ = TimestampPauser::new();
711             let mut blk_ex = blk.as_block_device_ex();
712             blk_ex.write(1, &mut to_write, 1, 2).unwrap();
713             blk_ex.abort().unwrap();
714             blk_ex.sync().unwrap();
715             assert!(blk_ex.is_aborted())
716         }
717         assert_eq!(blk.io.storage, [1, 2, 3, 4]);
718 
719         let mut to_read = Some(&mut io_buffer[..]);
720         {
721             let _ = TimestampPauser::new();
722             let mut blk_ex = blk.as_block_device_ex();
723             blk_ex.read(1, &mut to_read, 1, 2).unwrap();
724             blk_ex.abort().unwrap();
725             blk_ex.sync().unwrap();
726             assert!(blk_ex.is_aborted())
727         }
728         assert_eq!(io_buffer, [1, 0, 0, 1]);
729     }
730 
731     #[test]
read_write_error_on_busy()732     fn read_write_error_on_busy() {
733         let mut blk = TestBlockDeviceBuilder::new()
734             .set_alignment(1)
735             .set_block_size(1)
736             .set_data(&[1, 2, 3, 4])
737             .build();
738         let mut io_buffer = [1, 0, 0, 1];
739         let mut to_write = Some(&mut io_buffer[..]);
740 
741         let mut io_buffer_other = [0u8; 4];
742         let mut io_other_ref = Some(&mut io_buffer_other[..]);
743         {
744             let _ = TimestampPauser::new();
745             let mut blk_ex = blk.as_block_device_ex();
746             blk_ex.write(1, &mut to_write, 1, 2).unwrap();
747             assert!(blk_ex.write(1, &mut io_other_ref, 1, 2).is_err());
748             assert!(io_other_ref.is_some());
749             assert!(blk_ex.read(1, &mut io_other_ref, 1, 2).is_err());
750             assert!(io_other_ref.is_some());
751         }
752     }
753 }
754