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