1use fidl_fuchsia_storage_block as block;
13use fidl_fuchsia_storage_block::{MAX_TRANSFER_UNBOUNDED, VMOID_INVALID};
14use fuchsia_async as fasync;
15use fuchsia_sync::Mutex;
16use futures::channel::oneshot;
17use futures::executor::block_on;
18use std::borrow::Borrow;
19use std::collections::HashMap;
20use std::future::Future;
21use std::hash::{Hash, Hasher};
22use std::mem::MaybeUninit;
23use std::num::NonZero;
24use std::ops::{DerefMut, Range};
25use std::pin::Pin;
26use std::sync::atomic::{AtomicU16, Ordering};
27use std::sync::{Arc, LazyLock};
28use std::task::{Context, Poll, Waker};
29use storage_trace as trace;
30use zx::sys::zx_handle_t;
31
32pub use cache::Cache;
33
34pub use block::DeviceFlag as BlockDeviceFlag;
35
36pub use block_protocol::*;
37
38pub mod cache;
39
40const TEMP_VMO_SIZE: usize = 65536;
41
42pub const NO_TRACE_ID: u64 = 0;
44
45pub use fidl_fuchsia_storage_block::{BlockIoFlag, BlockOpcode};
46
47fn fidl_to_status(error: fidl::Error) -> zx::Status {
48 match error {
49 fidl::Error::ClientChannelClosed { status, .. } => status,
50 _ => zx::Status::INTERNAL,
51 }
52}
53
54fn opcode_str(opcode: u8) -> &'static str {
55 match BlockOpcode::from_primitive(opcode) {
56 Some(BlockOpcode::Read) => "read",
57 Some(BlockOpcode::Write) => "write",
58 Some(BlockOpcode::Flush) => "flush",
59 Some(BlockOpcode::Trim) => "trim",
60 Some(BlockOpcode::CloseVmo) => "close_vmo",
61 None => "unknown",
62 }
63}
64
65fn generate_trace_flow_id(request_id: u32) -> u64 {
68 static SELF_HANDLE: LazyLock<zx_handle_t> =
69 LazyLock::new(|| fuchsia_runtime::process_self().raw_handle());
70 *SELF_HANDLE as u64 + (request_id as u64) << 32
71}
72
73pub enum BufferSlice<'a> {
74 VmoId { vmo_id: &'a VmoId, offset: u64, length: u64 },
75 Memory(&'a [u8]),
76}
77
78impl<'a> BufferSlice<'a> {
79 pub fn new_with_vmo_id(vmo_id: &'a VmoId, offset: u64, length: u64) -> Self {
80 BufferSlice::VmoId { vmo_id, offset, length }
81 }
82}
83
84impl<'a> From<&'a [u8]> for BufferSlice<'a> {
85 fn from(buf: &'a [u8]) -> Self {
86 BufferSlice::Memory(buf)
87 }
88}
89
90pub enum MutableBufferSlice<'a> {
91 VmoId { vmo_id: &'a VmoId, offset: u64, length: u64 },
92 Memory(&'a mut [u8]),
93}
94
95impl<'a> MutableBufferSlice<'a> {
96 pub fn new_with_vmo_id(vmo_id: &'a VmoId, offset: u64, length: u64) -> Self {
97 MutableBufferSlice::VmoId { vmo_id, offset, length }
98 }
99}
100
101impl<'a> From<&'a mut [u8]> for MutableBufferSlice<'a> {
102 fn from(buf: &'a mut [u8]) -> Self {
103 MutableBufferSlice::Memory(buf)
104 }
105}
106
107#[derive(Default)]
108struct RequestState {
109 result: Option<zx::Status>,
110 waker: Option<Waker>,
111}
112
113#[derive(Default)]
114struct FifoState {
115 fifo: Option<fasync::Fifo<BlockFifoResponse, BlockFifoRequest>>,
117
118 next_request_id: u32,
120
121 queue: std::collections::VecDeque<BlockFifoRequest>,
123
124 map: HashMap<u32, RequestState>,
126
127 poller_waker: Option<Waker>,
129
130 attach_barrier: bool,
132}
133
134impl FifoState {
135 fn terminate(&mut self) {
136 self.fifo.take();
137 for (_, request_state) in self.map.iter_mut() {
138 request_state.result.get_or_insert(zx::Status::CANCELED);
139 if let Some(waker) = request_state.waker.take() {
140 waker.wake();
141 }
142 }
143 if let Some(waker) = self.poller_waker.take() {
144 waker.wake();
145 }
146 }
147
148 fn poll_send_requests(&mut self, context: &mut Context<'_>) -> bool {
150 let fifo = if let Some(fifo) = self.fifo.as_ref() {
151 fifo
152 } else {
153 return true;
154 };
155
156 loop {
157 let slice = self.queue.as_slices().0;
158 if slice.is_empty() {
159 return false;
160 }
161 match fifo.try_write(context, slice) {
162 Poll::Ready(Ok(sent)) => {
163 self.queue.drain(0..sent);
164 }
165 Poll::Ready(Err(_)) => {
166 self.terminate();
167 return true;
168 }
169 Poll::Pending => {
170 return false;
171 }
172 }
173 }
174 }
175}
176
177type FifoStateRef = Arc<Mutex<FifoState>>;
178
179struct ResponseFuture {
181 request_id: u32,
182 fifo_state: FifoStateRef,
183}
184
185impl ResponseFuture {
186 fn new(fifo_state: FifoStateRef, request_id: u32) -> Self {
187 ResponseFuture { request_id, fifo_state }
188 }
189}
190
191impl Future for ResponseFuture {
192 type Output = Result<(), zx::Status>;
193
194 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
195 let mut state = self.fifo_state.lock();
196 let request_state = state.map.get_mut(&self.request_id).unwrap();
197 if let Some(result) = request_state.result {
198 Poll::Ready(result.into())
199 } else {
200 request_state.waker.replace(context.waker().clone());
201 Poll::Pending
202 }
203 }
204}
205
206impl Drop for ResponseFuture {
207 fn drop(&mut self) {
208 let mut state = self.fifo_state.lock();
209 if let Some(request_state) = state.map.remove(&self.request_id) {
210 if request_state.result.is_none() {
211 state.terminate();
217 }
218 }
219 update_outstanding_requests_counter(state.map.len());
220 }
221}
222
223#[derive(Debug)]
225#[must_use]
226pub struct VmoId(AtomicU16);
227
228impl VmoId {
229 pub fn new(id: u16) -> Self {
231 Self(AtomicU16::new(id))
232 }
233
234 pub fn take(&self) -> Self {
236 Self(AtomicU16::new(self.0.swap(VMOID_INVALID, Ordering::Relaxed)))
237 }
238
239 pub fn is_valid(&self) -> bool {
240 self.id() != VMOID_INVALID
241 }
242
243 #[must_use]
245 pub fn into_id(self) -> u16 {
246 self.0.swap(VMOID_INVALID, Ordering::Relaxed)
247 }
248
249 pub fn id(&self) -> u16 {
250 self.0.load(Ordering::Relaxed)
251 }
252}
253
254impl PartialEq for VmoId {
255 fn eq(&self, other: &Self) -> bool {
256 self.id() == other.id()
257 }
258}
259
260impl Eq for VmoId {}
261
262impl Drop for VmoId {
263 fn drop(&mut self) {
264 assert_eq!(self.0.load(Ordering::Relaxed), VMOID_INVALID, "Did you forget to detach?");
265 }
266}
267
268impl Hash for VmoId {
269 fn hash<H: Hasher>(&self, state: &mut H) {
270 self.id().hash(state);
271 }
272}
273
274pub trait BlockClient: Send + Sync {
278 unsafe fn attach_vmo(
294 &self,
295 vmo: &zx::Vmo,
296 ) -> impl Future<Output = Result<VmoId, zx::Status>> + Send;
297
298 fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> + Send;
300
301 fn read_at(
303 &self,
304 buffer_slice: MutableBufferSlice<'_>,
305 device_offset: u64,
306 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
307 self.read_at_with_opts_traced(buffer_slice, device_offset, ReadOptions::default(), 0)
308 }
309
310 fn read_at_with_opts(
311 &self,
312 buffer_slice: MutableBufferSlice<'_>,
313 device_offset: u64,
314 opts: ReadOptions,
315 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
316 self.read_at_with_opts_traced(buffer_slice, device_offset, opts, 0)
317 }
318
319 fn read_at_with_opts_traced(
320 &self,
321 buffer_slice: MutableBufferSlice<'_>,
322 device_offset: u64,
323 opts: ReadOptions,
324 trace_flow_id: u64,
325 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
326
327 fn write_at(
329 &self,
330 buffer_slice: BufferSlice<'_>,
331 device_offset: u64,
332 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
333 self.write_at_with_opts_traced(
334 buffer_slice,
335 device_offset,
336 WriteOptions::default(),
337 NO_TRACE_ID,
338 )
339 }
340
341 fn write_at_with_opts(
342 &self,
343 buffer_slice: BufferSlice<'_>,
344 device_offset: u64,
345 opts: WriteOptions,
346 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
347 self.write_at_with_opts_traced(buffer_slice, device_offset, opts, NO_TRACE_ID)
348 }
349
350 fn write_at_with_opts_traced(
351 &self,
352 buffer_slice: BufferSlice<'_>,
353 device_offset: u64,
354 opts: WriteOptions,
355 trace_flow_id: u64,
356 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
357
358 fn trim(
360 &self,
361 device_range: Range<u64>,
362 ) -> impl Future<Output = Result<(), zx::Status>> + Send {
363 self.trim_traced(device_range, NO_TRACE_ID)
364 }
365
366 fn trim_traced(
367 &self,
368 device_range: Range<u64>,
369 trace_flow_id: u64,
370 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
371
372 fn barrier(&self);
377
378 fn flush(&self) -> impl Future<Output = Result<(), zx::Status>> + Send {
379 self.flush_traced(NO_TRACE_ID)
380 }
381
382 fn flush_traced(
384 &self,
385 trace_flow_id: u64,
386 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
387
388 fn close(&self) -> impl Future<Output = Result<(), zx::Status>> + Send;
390
391 fn block_size(&self) -> u32;
393
394 fn block_count(&self) -> u64;
396
397 fn max_transfer_blocks(&self) -> Option<NonZero<u32>>;
399
400 fn block_flags(&self) -> BlockDeviceFlag;
402
403 fn is_connected(&self) -> bool;
405}
406
407struct Common {
408 block_size: u32,
409 block_count: u64,
410 max_transfer_blocks: Option<NonZero<u32>>,
411 block_flags: BlockDeviceFlag,
412 fifo_state: FifoStateRef,
413 temp_vmo: futures::lock::Mutex<zx::Vmo>,
414 temp_vmo_id: VmoId,
415}
416
417impl Common {
418 fn new(
419 fifo: fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
420 info: &block::BlockInfo,
421 temp_vmo: zx::Vmo,
422 temp_vmo_id: VmoId,
423 ) -> Self {
424 let fifo_state = Arc::new(Mutex::new(FifoState { fifo: Some(fifo), ..Default::default() }));
425 fasync::Task::spawn(FifoPoller { fifo_state: fifo_state.clone() }).detach();
426 Self {
427 block_size: info.block_size,
428 block_count: info.block_count,
429 max_transfer_blocks: if info.max_transfer_size != MAX_TRANSFER_UNBOUNDED {
430 NonZero::new(info.max_transfer_size / info.block_size)
431 } else {
432 None
433 },
434 block_flags: info.flags,
435 fifo_state,
436 temp_vmo: futures::lock::Mutex::new(temp_vmo),
437 temp_vmo_id,
438 }
439 }
440
441 fn to_blocks(&self, bytes: u64) -> Result<u64, zx::Status> {
442 if bytes % self.block_size as u64 != 0 {
443 Err(zx::Status::INVALID_ARGS)
444 } else {
445 Ok(bytes / self.block_size as u64)
446 }
447 }
448
449 async fn send(&self, mut request: BlockFifoRequest) -> Result<(), zx::Status> {
451 let (request_id, trace_flow_id) = {
452 let mut state = self.fifo_state.lock();
453
454 let mut flags = BlockIoFlag::from_bits_retain(request.command.flags);
455 if BlockOpcode::from_primitive(request.command.opcode) == Some(BlockOpcode::Write)
456 && state.attach_barrier
457 {
458 flags |= BlockIoFlag::PRE_BARRIER;
459 request.command.flags = flags.bits();
460 state.attach_barrier = false;
461 }
462
463 if state.fifo.is_none() {
464 return Err(zx::Status::CANCELED);
466 }
467 trace::duration!(
468 "storage",
469 "block_client::send::start",
470 "op" => opcode_str(request.command.opcode),
471 "len" => request.length * self.block_size
472 );
473 let request_id = state.next_request_id;
474 state.next_request_id = state.next_request_id.overflowing_add(1).0;
475 assert!(
476 state.map.insert(request_id, RequestState::default()).is_none(),
477 "request id in use!"
478 );
479 update_outstanding_requests_counter(state.map.len());
480 request.reqid = request_id;
481 if request.trace_flow_id == NO_TRACE_ID {
482 request.trace_flow_id = generate_trace_flow_id(request_id);
483 }
484 let trace_flow_id = request.trace_flow_id;
485 trace::flow_begin!("storage", "block_client::send", trace_flow_id.into());
486 state.queue.push_back(request);
487 if let Some(waker) = state.poller_waker.clone() {
488 state.poll_send_requests(&mut Context::from_waker(&waker));
489 }
490 (request_id, trace_flow_id)
491 };
492 ResponseFuture::new(self.fifo_state.clone(), request_id).await?;
493 trace::duration!("storage", "block_client::send::end");
494 trace::flow_end!("storage", "block_client::send", trace_flow_id.into());
495 Ok(())
496 }
497
498 async fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
499 self.send(BlockFifoRequest {
500 command: BlockFifoCommand {
501 opcode: BlockOpcode::CloseVmo.into_primitive(),
502 flags: 0,
503 ..Default::default()
504 },
505 vmoid: vmo_id.into_id(),
506 ..Default::default()
507 })
508 .await
509 }
510
511 async fn read_at(
512 &self,
513 buffer_slice: MutableBufferSlice<'_>,
514 device_offset: u64,
515 opts: ReadOptions,
516 trace_flow_id: u64,
517 ) -> Result<(), zx::Status> {
518 let mut flags = BlockIoFlag::empty();
519
520 if opts.inline_crypto.is_enabled {
521 flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
522 }
523
524 match buffer_slice {
525 MutableBufferSlice::VmoId { vmo_id, offset, length } => {
526 self.send(BlockFifoRequest {
527 command: BlockFifoCommand {
528 opcode: BlockOpcode::Read.into_primitive(),
529 flags: flags.bits(),
530 ..Default::default()
531 },
532 vmoid: vmo_id.id(),
533 length: self
534 .to_blocks(length)?
535 .try_into()
536 .map_err(|_| zx::Status::INVALID_ARGS)?,
537 vmo_offset: self.to_blocks(offset)?,
538 dev_offset: self.to_blocks(device_offset)?,
539 trace_flow_id,
540 dun: opts.inline_crypto.dun,
541 slot: opts.inline_crypto.slot,
542 ..Default::default()
543 })
544 .await?
545 }
546 MutableBufferSlice::Memory(mut slice) => {
547 let temp_vmo = self.temp_vmo.lock().await;
548 let mut device_block = self.to_blocks(device_offset)?;
549 loop {
550 let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
551 let block_count = self.to_blocks(to_do as u64)? as u32;
552 self.send(BlockFifoRequest {
553 command: BlockFifoCommand {
554 opcode: BlockOpcode::Read.into_primitive(),
555 flags: flags.bits(),
556 ..Default::default()
557 },
558 vmoid: self.temp_vmo_id.id(),
559 length: block_count,
560 vmo_offset: 0,
561 dev_offset: device_block,
562 trace_flow_id,
563 dun: opts.inline_crypto.dun,
564 slot: opts.inline_crypto.slot,
565 ..Default::default()
566 })
567 .await?;
568 temp_vmo.read(&mut slice[..to_do], 0)?;
569 if to_do == slice.len() {
570 break;
571 }
572 device_block += block_count as u64;
573 slice = &mut slice[to_do..];
574 }
575 }
576 }
577 Ok(())
578 }
579
580 async fn write_at(
581 &self,
582 buffer_slice: BufferSlice<'_>,
583 device_offset: u64,
584 opts: WriteOptions,
585 trace_flow_id: u64,
586 ) -> Result<(), zx::Status> {
587 let mut flags = BlockIoFlag::empty();
588
589 if opts.flags.contains(WriteFlags::FORCE_ACCESS) {
590 flags |= BlockIoFlag::FORCE_ACCESS;
591 }
592
593 if opts.flags.contains(WriteFlags::PRE_BARRIER) {
594 flags |= BlockIoFlag::PRE_BARRIER;
595 }
596
597 if opts.inline_crypto.is_enabled {
598 flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
599 }
600
601 match buffer_slice {
602 BufferSlice::VmoId { vmo_id, offset, length } => {
603 self.send(BlockFifoRequest {
604 command: BlockFifoCommand {
605 opcode: BlockOpcode::Write.into_primitive(),
606 flags: flags.bits(),
607 ..Default::default()
608 },
609 vmoid: vmo_id.id(),
610 length: self
611 .to_blocks(length)?
612 .try_into()
613 .map_err(|_| zx::Status::INVALID_ARGS)?,
614 vmo_offset: self.to_blocks(offset)?,
615 dev_offset: self.to_blocks(device_offset)?,
616 trace_flow_id,
617 dun: opts.inline_crypto.dun,
618 slot: opts.inline_crypto.slot,
619 ..Default::default()
620 })
621 .await?;
622 }
623 BufferSlice::Memory(mut slice) => {
624 let temp_vmo = self.temp_vmo.lock().await;
625 let mut device_block = self.to_blocks(device_offset)?;
626 loop {
627 let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
628 let block_count = self.to_blocks(to_do as u64)? as u32;
629 temp_vmo.write(&slice[..to_do], 0)?;
630 self.send(BlockFifoRequest {
631 command: BlockFifoCommand {
632 opcode: BlockOpcode::Write.into_primitive(),
633 flags: flags.bits(),
634 ..Default::default()
635 },
636 vmoid: self.temp_vmo_id.id(),
637 length: block_count,
638 vmo_offset: 0,
639 dev_offset: device_block,
640 trace_flow_id,
641 dun: opts.inline_crypto.dun,
642 slot: opts.inline_crypto.slot,
643 ..Default::default()
644 })
645 .await?;
646 if to_do == slice.len() {
647 break;
648 }
649 device_block += block_count as u64;
650 slice = &slice[to_do..];
651 }
652 }
653 }
654 Ok(())
655 }
656
657 async fn trim(&self, device_range: Range<u64>, trace_flow_id: u64) -> Result<(), zx::Status> {
658 let length = self.to_blocks(device_range.end - device_range.start)? as u32;
659 let dev_offset = self.to_blocks(device_range.start)?;
660 self.send(BlockFifoRequest {
661 command: BlockFifoCommand {
662 opcode: BlockOpcode::Trim.into_primitive(),
663 flags: 0,
664 ..Default::default()
665 },
666 vmoid: VMOID_INVALID,
667 length,
668 dev_offset,
669 trace_flow_id,
670 ..Default::default()
671 })
672 .await
673 }
674
675 async fn flush(&self, trace_flow_id: u64) -> Result<(), zx::Status> {
676 self.send(BlockFifoRequest {
677 command: BlockFifoCommand {
678 opcode: BlockOpcode::Flush.into_primitive(),
679 flags: 0,
680 ..Default::default()
681 },
682 vmoid: VMOID_INVALID,
683 trace_flow_id,
684 ..Default::default()
685 })
686 .await
687 }
688
689 fn barrier(&self) {
690 self.fifo_state.lock().attach_barrier = true;
691 }
692
693 fn block_size(&self) -> u32 {
694 self.block_size
695 }
696
697 fn block_count(&self) -> u64 {
698 self.block_count
699 }
700
701 fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
702 self.max_transfer_blocks.clone()
703 }
704
705 fn block_flags(&self) -> BlockDeviceFlag {
706 self.block_flags
707 }
708
709 fn is_connected(&self) -> bool {
710 self.fifo_state.lock().fifo.is_some()
711 }
712}
713
714impl Drop for Common {
715 fn drop(&mut self) {
716 let _ = self.temp_vmo_id.take().into_id();
719 self.fifo_state.lock().terminate();
720 }
721}
722
723pub struct RemoteBlockClient {
725 session: block::SessionProxy,
726 common: Common,
727}
728
729impl RemoteBlockClient {
730 pub async fn new(remote: impl Borrow<block::BlockProxy>) -> Result<Self, zx::Status> {
732 let remote = remote.borrow();
733 let info =
734 remote.get_info().await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
735 let (session, server) = fidl::endpoints::create_proxy();
736 let () = remote.open_session(server).map_err(fidl_to_status)?;
737 Self::from_session(info, session).await
738 }
739
740 pub async fn from_session(
741 info: block::BlockInfo,
742 session: block::SessionProxy,
743 ) -> Result<Self, zx::Status> {
744 const SCRATCH_VMO_NAME: zx::Name = zx::Name::new_lossy("block-client-scratch-vmo");
745 let fifo =
746 session.get_fifo().await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
747 let fifo = fasync::Fifo::from_fifo(fifo);
748 let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
749 temp_vmo.set_name(&SCRATCH_VMO_NAME)?;
750 let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
751 let vmo_id =
752 session.attach_vmo(dup).await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
753 let vmo_id = VmoId::new(vmo_id.id);
754 Ok(RemoteBlockClient { session, common: Common::new(fifo, &info, temp_vmo, vmo_id) })
755 }
756}
757
758impl BlockClient for RemoteBlockClient {
759 async unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
760 let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
761 let vmo_id = self
762 .session
763 .attach_vmo(dup)
764 .await
765 .map_err(fidl_to_status)?
766 .map_err(zx::Status::from_raw)?;
767 Ok(VmoId::new(vmo_id.id))
768 }
769
770 async fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
771 self.common.detach_vmo(vmo_id).await
772 }
773
774 async fn read_at_with_opts_traced(
775 &self,
776 buffer_slice: MutableBufferSlice<'_>,
777 device_offset: u64,
778 opts: ReadOptions,
779 trace_flow_id: u64,
780 ) -> Result<(), zx::Status> {
781 self.common.read_at(buffer_slice, device_offset, opts, trace_flow_id).await
782 }
783
784 async fn write_at_with_opts_traced(
785 &self,
786 buffer_slice: BufferSlice<'_>,
787 device_offset: u64,
788 opts: WriteOptions,
789 trace_flow_id: u64,
790 ) -> Result<(), zx::Status> {
791 self.common.write_at(buffer_slice, device_offset, opts, trace_flow_id).await
792 }
793
794 async fn trim_traced(&self, range: Range<u64>, trace_flow_id: u64) -> Result<(), zx::Status> {
795 self.common.trim(range, trace_flow_id).await
796 }
797
798 async fn flush_traced(&self, trace_flow_id: u64) -> Result<(), zx::Status> {
799 self.common.flush(trace_flow_id).await
800 }
801
802 fn barrier(&self) {
803 self.common.barrier()
804 }
805
806 async fn close(&self) -> Result<(), zx::Status> {
807 let () =
808 self.session.close().await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
809 Ok(())
810 }
811
812 fn block_size(&self) -> u32 {
813 self.common.block_size()
814 }
815
816 fn block_count(&self) -> u64 {
817 self.common.block_count()
818 }
819
820 fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
821 self.common.max_transfer_blocks()
822 }
823
824 fn block_flags(&self) -> BlockDeviceFlag {
825 self.common.block_flags()
826 }
827
828 fn is_connected(&self) -> bool {
829 self.common.is_connected()
830 }
831}
832
833pub struct RemoteBlockClientSync {
834 session: block::SessionSynchronousProxy,
835 common: Common,
836}
837
838impl RemoteBlockClientSync {
839 pub fn new(
843 client_end: fidl::endpoints::ClientEnd<block::BlockMarker>,
844 ) -> Result<Self, zx::Status> {
845 let remote = block::BlockSynchronousProxy::new(client_end.into_channel());
846 let info = remote
847 .get_info(zx::MonotonicInstant::INFINITE)
848 .map_err(fidl_to_status)?
849 .map_err(zx::Status::from_raw)?;
850 let (client, server) = fidl::endpoints::create_endpoints();
851 let () = remote.open_session(server).map_err(fidl_to_status)?;
852 let session = block::SessionSynchronousProxy::new(client.into_channel());
853 let fifo = session
854 .get_fifo(zx::MonotonicInstant::INFINITE)
855 .map_err(fidl_to_status)?
856 .map_err(zx::Status::from_raw)?;
857 let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
858 let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
859 let vmo_id = session
860 .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
861 .map_err(fidl_to_status)?
862 .map_err(zx::Status::from_raw)?;
863 let vmo_id = VmoId::new(vmo_id.id);
864
865 let (sender, receiver) = oneshot::channel::<Result<Self, zx::Status>>();
868 std::thread::spawn(move || {
869 let mut executor = fasync::LocalExecutor::default();
870 let fifo = fasync::Fifo::from_fifo(fifo);
871 let common = Common::new(fifo, &info, temp_vmo, vmo_id);
872 let fifo_state = common.fifo_state.clone();
873 let _ = sender.send(Ok(RemoteBlockClientSync { session, common }));
874 executor.run_singlethreaded(FifoPoller { fifo_state });
875 });
876 block_on(receiver).map_err(|_| zx::Status::CANCELED)?
877 }
878
879 pub unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
885 let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
886 let vmo_id = self
887 .session
888 .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
889 .map_err(fidl_to_status)?
890 .map_err(zx::Status::from_raw)?;
891 Ok(VmoId::new(vmo_id.id))
892 }
893
894 pub fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
895 block_on(self.common.detach_vmo(vmo_id))
896 }
897
898 pub fn read_at(
899 &self,
900 buffer_slice: MutableBufferSlice<'_>,
901 device_offset: u64,
902 ) -> Result<(), zx::Status> {
903 block_on(self.common.read_at(
904 buffer_slice,
905 device_offset,
906 ReadOptions::default(),
907 NO_TRACE_ID,
908 ))
909 }
910
911 pub fn write_at(
912 &self,
913 buffer_slice: BufferSlice<'_>,
914 device_offset: u64,
915 ) -> Result<(), zx::Status> {
916 block_on(self.common.write_at(
917 buffer_slice,
918 device_offset,
919 WriteOptions::default(),
920 NO_TRACE_ID,
921 ))
922 }
923
924 pub fn flush(&self) -> Result<(), zx::Status> {
925 block_on(self.common.flush(NO_TRACE_ID))
926 }
927
928 pub fn close(&self) -> Result<(), zx::Status> {
929 let () = self
930 .session
931 .close(zx::MonotonicInstant::INFINITE)
932 .map_err(fidl_to_status)?
933 .map_err(zx::Status::from_raw)?;
934 Ok(())
935 }
936
937 pub fn block_size(&self) -> u32 {
938 self.common.block_size()
939 }
940
941 pub fn block_count(&self) -> u64 {
942 self.common.block_count()
943 }
944
945 pub fn is_connected(&self) -> bool {
946 self.common.is_connected()
947 }
948}
949
950impl Drop for RemoteBlockClientSync {
951 fn drop(&mut self) {
952 let _ = self.close();
954 }
955}
956
957struct FifoPoller {
959 fifo_state: FifoStateRef,
960}
961
962impl Future for FifoPoller {
963 type Output = ();
964
965 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
966 let mut state_lock = self.fifo_state.lock();
967 let state = state_lock.deref_mut(); if state.poll_send_requests(context) {
971 return Poll::Ready(());
972 }
973
974 let fifo = state.fifo.as_ref().unwrap(); loop {
977 let mut response = MaybeUninit::uninit();
978 match fifo.try_read(context, &mut response) {
979 Poll::Pending => {
980 state.poller_waker = Some(context.waker().clone());
981 return Poll::Pending;
982 }
983 Poll::Ready(Ok(_)) => {
984 let response = unsafe { response.assume_init() };
985 let request_id = response.reqid;
986 if let Some(request_state) = state.map.get_mut(&request_id) {
988 request_state.result.replace(zx::Status::from_raw(response.status));
989 if let Some(waker) = request_state.waker.take() {
990 waker.wake();
991 }
992 }
993 }
994 Poll::Ready(Err(_)) => {
995 state.terminate();
996 return Poll::Ready(());
997 }
998 }
999 }
1000 }
1001}
1002
1003fn update_outstanding_requests_counter(outstanding: usize) {
1004 trace::counter!("storage", "block-requests", 0, "outstanding" => outstanding);
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009 use super::{
1010 BlockClient, BlockFifoRequest, BlockFifoResponse, BufferSlice, MutableBufferSlice,
1011 RemoteBlockClient, RemoteBlockClientSync, WriteOptions,
1012 };
1013 use block_protocol::ReadOptions;
1014 use block_server::{BlockServer, DeviceInfo, PartitionInfo};
1015 use fidl::endpoints::RequestStream as _;
1016 use fidl_fuchsia_storage_block as block;
1017 use fuchsia_async as fasync;
1018 use futures::future::{AbortHandle, Abortable, TryFutureExt as _};
1019 use futures::join;
1020 use futures::stream::StreamExt as _;
1021 use futures::stream::futures_unordered::FuturesUnordered;
1022 use ramdevice_client::RamdiskClient;
1023 use std::borrow::Cow;
1024 use std::num::NonZero;
1025 use std::sync::Arc;
1026 use std::sync::atomic::{AtomicBool, Ordering};
1027
1028 const RAMDISK_BLOCK_SIZE: u64 = 1024;
1029 const RAMDISK_BLOCK_COUNT: u64 = 1024;
1030
1031 pub async fn make_ramdisk() -> (RamdiskClient, block::BlockProxy, RemoteBlockClient) {
1032 let ramdisk = RamdiskClient::create(RAMDISK_BLOCK_SIZE, RAMDISK_BLOCK_COUNT)
1033 .await
1034 .expect("RamdiskClient::create failed");
1035 let client_end = ramdisk.open().expect("ramdisk.open failed");
1036 let proxy = client_end.into_proxy();
1037 let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1038 assert_eq!(block_client.block_size(), 1024);
1039 let client_end = ramdisk.open().expect("ramdisk.open failed");
1040 let proxy = client_end.into_proxy();
1041 (ramdisk, proxy, block_client)
1042 }
1043
1044 #[fuchsia::test]
1045 async fn test_against_ram_disk() {
1046 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1047
1048 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1049 vmo.write(b"hello", 5).expect("vmo.write failed");
1050 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1052 block_client
1053 .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1054 .await
1055 .expect("write_at failed");
1056 block_client
1057 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 1024, 2048), 0)
1058 .await
1059 .expect("read_at failed");
1060 let mut buf: [u8; 5] = Default::default();
1061 vmo.read(&mut buf, 1029).expect("vmo.read failed");
1062 assert_eq!(&buf, b"hello");
1063 block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1064 }
1065
1066 #[fuchsia::test]
1067 async fn test_alignment() {
1068 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1069 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1070 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1072 block_client
1073 .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 1)
1074 .await
1075 .expect_err("expected failure due to bad alignment");
1076 block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1077 }
1078
1079 #[fuchsia::test]
1080 async fn test_parallel_io() {
1081 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1082 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1083 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1085 let mut reads = Vec::new();
1086 for _ in 0..1024 {
1087 reads.push(
1088 block_client
1089 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1090 .inspect_err(|e| panic!("read should have succeeded: {}", e)),
1091 );
1092 }
1093 futures::future::join_all(reads).await;
1094 block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1095 }
1096
1097 #[fuchsia::test]
1098 async fn test_closed_device() {
1099 let (ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1100 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1101 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1103 let mut reads = Vec::new();
1104 for _ in 0..1024 {
1105 reads.push(
1106 block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1107 );
1108 }
1109 assert!(block_client.is_connected());
1110 let _ = futures::join!(futures::future::join_all(reads), async {
1111 std::mem::drop(ramdisk);
1112 });
1113 while block_client
1115 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1116 .await
1117 .is_ok()
1118 {}
1119
1120 while block_client.is_connected() {
1123 fasync::Timer::new(fasync::MonotonicInstant::after(
1125 zx::MonotonicDuration::from_millis(500),
1126 ))
1127 .await;
1128 }
1129
1130 assert_eq!(block_client.is_connected(), false);
1132 let _ = block_client.detach_vmo(vmo_id).await;
1133 }
1134
1135 #[fuchsia::test]
1136 async fn test_cancelled_reads() {
1137 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1138 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1139 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1141 {
1142 let mut reads = FuturesUnordered::new();
1143 for _ in 0..1024 {
1144 reads.push(
1145 block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1146 );
1147 }
1148 for _ in 0..500 {
1150 reads.next().await;
1151 }
1152 }
1153
1154 assert_eq!(
1158 block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0).await,
1159 Err(zx::Status::CANCELED)
1160 );
1161 assert_eq!(block_client.detach_vmo(vmo_id).await, Err(zx::Status::CANCELED));
1162 }
1163
1164 #[fuchsia::test]
1165 async fn test_parallel_large_read_and_write_with_memory_succeds() {
1166 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1167 let block_client_ref = &block_client;
1168 let test_one = |offset, len, fill| async move {
1169 let buf = vec![fill; len];
1170 block_client_ref.write_at(buf[..].into(), offset).await.expect("write_at failed");
1171 let mut read_buf = vec![0u8; len + 2 * RAMDISK_BLOCK_SIZE as usize];
1173 block_client_ref
1174 .read_at(read_buf.as_mut_slice().into(), offset - RAMDISK_BLOCK_SIZE)
1175 .await
1176 .expect("read_at failed");
1177 assert_eq!(
1178 &read_buf[0..RAMDISK_BLOCK_SIZE as usize],
1179 &[0; RAMDISK_BLOCK_SIZE as usize][..]
1180 );
1181 assert_eq!(
1182 &read_buf[RAMDISK_BLOCK_SIZE as usize..RAMDISK_BLOCK_SIZE as usize + len],
1183 &buf[..]
1184 );
1185 assert_eq!(
1186 &read_buf[RAMDISK_BLOCK_SIZE as usize + len..],
1187 &[0; RAMDISK_BLOCK_SIZE as usize][..]
1188 );
1189 };
1190 const WRITE_LEN: usize = super::TEMP_VMO_SIZE * 3 + RAMDISK_BLOCK_SIZE as usize;
1191 join!(
1192 test_one(RAMDISK_BLOCK_SIZE, WRITE_LEN, 0xa3u8),
1193 test_one(2 * RAMDISK_BLOCK_SIZE + WRITE_LEN as u64, WRITE_LEN, 0x7fu8)
1194 );
1195 }
1196
1197 struct FakeBlockServer<'a> {
1201 server_channel: Option<fidl::endpoints::ServerEnd<block::BlockMarker>>,
1202 channel_handler: Box<dyn Fn(&block::SessionRequest) -> bool + 'a>,
1203 fifo_handler: Box<dyn Fn(BlockFifoRequest) -> BlockFifoResponse + 'a>,
1204 }
1205
1206 impl<'a> FakeBlockServer<'a> {
1207 fn new(
1219 server_channel: fidl::endpoints::ServerEnd<block::BlockMarker>,
1220 channel_handler: impl Fn(&block::SessionRequest) -> bool + 'a,
1221 fifo_handler: impl Fn(BlockFifoRequest) -> BlockFifoResponse + 'a,
1222 ) -> FakeBlockServer<'a> {
1223 FakeBlockServer {
1224 server_channel: Some(server_channel),
1225 channel_handler: Box::new(channel_handler),
1226 fifo_handler: Box::new(fifo_handler),
1227 }
1228 }
1229
1230 async fn run(&mut self) {
1232 let server = self.server_channel.take().unwrap();
1233
1234 let (server_fifo, client_fifo) =
1236 zx::Fifo::<BlockFifoRequest, BlockFifoResponse>::create(16)
1237 .expect("Fifo::create failed");
1238 let maybe_server_fifo = fuchsia_sync::Mutex::new(Some(client_fifo));
1239
1240 let (fifo_future_abort, fifo_future_abort_registration) = AbortHandle::new_pair();
1241 let fifo_future = Abortable::new(
1242 async {
1243 let mut fifo = fasync::Fifo::from_fifo(server_fifo);
1244 let (mut reader, mut writer) = fifo.async_io();
1245 let mut request = BlockFifoRequest::default();
1246 loop {
1247 match reader.read_entries(&mut request).await {
1248 Ok(1) => {}
1249 Err(zx::Status::PEER_CLOSED) => break,
1250 Err(e) => panic!("read_entry failed {:?}", e),
1251 _ => unreachable!(),
1252 };
1253
1254 let response = self.fifo_handler.as_ref()(request);
1255 writer
1256 .write_entries(std::slice::from_ref(&response))
1257 .await
1258 .expect("write_entries failed");
1259 }
1260 },
1261 fifo_future_abort_registration,
1262 );
1263
1264 let channel_future = async {
1265 server
1266 .into_stream()
1267 .for_each_concurrent(None, |request| async {
1268 let request = request.expect("unexpected fidl error");
1269
1270 match request {
1271 block::BlockRequest::GetInfo { responder } => {
1272 responder
1273 .send(Ok(&block::BlockInfo {
1274 block_count: 1024,
1275 block_size: 512,
1276 max_transfer_size: 1024 * 1024,
1277 flags: block::DeviceFlag::empty(),
1278 }))
1279 .expect("send failed");
1280 }
1281 block::BlockRequest::OpenSession { session, control_handle: _ } => {
1282 let stream = session.into_stream();
1283 stream
1284 .for_each(|request| async {
1285 let request = request.expect("unexpected fidl error");
1286 if self.channel_handler.as_ref()(&request) {
1289 return;
1290 }
1291 match request {
1292 block::SessionRequest::GetFifo { responder } => {
1293 match maybe_server_fifo.lock().take() {
1294 Some(fifo) => {
1295 responder.send(Ok(fifo.downcast()))
1296 }
1297 None => responder.send(Err(
1298 zx::Status::NO_RESOURCES.into_raw(),
1299 )),
1300 }
1301 .expect("send failed")
1302 }
1303 block::SessionRequest::AttachVmo {
1304 vmo: _,
1305 responder,
1306 } => responder
1307 .send(Ok(&block::VmoId { id: 1 }))
1308 .expect("send failed"),
1309 block::SessionRequest::Close { responder } => {
1310 fifo_future_abort.abort();
1311 responder.send(Ok(())).expect("send failed")
1312 }
1313 }
1314 })
1315 .await
1316 }
1317 _ => panic!("Unexpected message"),
1318 }
1319 })
1320 .await;
1321 };
1322
1323 let _result = join!(fifo_future, channel_future);
1324 }
1326 }
1327
1328 #[fuchsia::test]
1329 async fn test_block_close_is_called() {
1330 let close_called = fuchsia_sync::Mutex::new(false);
1331 let (client_end, server) = fidl::endpoints::create_endpoints::<block::BlockMarker>();
1332
1333 std::thread::spawn(move || {
1334 let _block_client =
1335 RemoteBlockClientSync::new(client_end).expect("RemoteBlockClientSync::new failed");
1336 });
1338
1339 let channel_handler = |request: &block::SessionRequest| -> bool {
1340 if let block::SessionRequest::Close { .. } = request {
1341 *close_called.lock() = true;
1342 }
1343 false
1344 };
1345 FakeBlockServer::new(server, channel_handler, |_| unreachable!()).run().await;
1346
1347 assert!(*close_called.lock());
1349 }
1350
1351 #[fuchsia::test]
1352 async fn test_block_flush_is_called() {
1353 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<block::BlockMarker>();
1354
1355 struct Interface {
1356 flush_called: Arc<AtomicBool>,
1357 }
1358 impl block_server::async_interface::Interface for Interface {
1359 fn get_info(&self) -> Cow<'_, DeviceInfo> {
1360 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
1361 device_flags: fidl_fuchsia_storage_block::DeviceFlag::empty(),
1362 max_transfer_blocks: None,
1363 block_range: Some(0..1000),
1364 type_guid: [0; 16],
1365 instance_guid: [0; 16],
1366 name: "foo".to_string(),
1367 flags: 0,
1368 }))
1369 }
1370
1371 async fn read(
1372 &self,
1373 _device_block_offset: u64,
1374 _block_count: u32,
1375 _vmo: &Arc<zx::Vmo>,
1376 _vmo_offset: u64,
1377 _opts: ReadOptions,
1378 _trace_flow_id: Option<NonZero<u64>>,
1379 ) -> Result<(), zx::Status> {
1380 unreachable!();
1381 }
1382
1383 async fn write(
1384 &self,
1385 _device_block_offset: u64,
1386 _block_count: u32,
1387 _vmo: &Arc<zx::Vmo>,
1388 _vmo_offset: u64,
1389 _opts: WriteOptions,
1390 _trace_flow_id: Option<NonZero<u64>>,
1391 ) -> Result<(), zx::Status> {
1392 unreachable!();
1393 }
1394
1395 async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
1396 self.flush_called.store(true, Ordering::Relaxed);
1397 Ok(())
1398 }
1399
1400 async fn trim(
1401 &self,
1402 _device_block_offset: u64,
1403 _block_count: u32,
1404 _trace_flow_id: Option<NonZero<u64>>,
1405 ) -> Result<(), zx::Status> {
1406 unreachable!();
1407 }
1408 }
1409
1410 let flush_called = Arc::new(AtomicBool::new(false));
1411
1412 futures::join!(
1413 async {
1414 let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1415
1416 block_client.flush().await.expect("flush failed");
1417 },
1418 async {
1419 let block_server = BlockServer::new(
1420 512,
1421 Arc::new(Interface { flush_called: flush_called.clone() }),
1422 );
1423 block_server.handle_requests(stream.cast_stream()).await.unwrap();
1424 }
1425 );
1426
1427 assert!(flush_called.load(Ordering::Relaxed));
1428 }
1429
1430 #[fuchsia::test]
1431 async fn test_trace_flow_ids_set() {
1432 let (proxy, server) = fidl::endpoints::create_proxy();
1433
1434 futures::join!(
1435 async {
1436 let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1437 block_client.flush().await.expect("flush failed");
1438 },
1439 async {
1440 let flow_id: fuchsia_sync::Mutex<Option<u64>> = fuchsia_sync::Mutex::new(None);
1441 let fifo_handler = |request: BlockFifoRequest| -> BlockFifoResponse {
1442 if request.trace_flow_id > 0 {
1443 *flow_id.lock() = Some(request.trace_flow_id);
1444 }
1445 BlockFifoResponse {
1446 status: zx::Status::OK.into_raw(),
1447 reqid: request.reqid,
1448 ..Default::default()
1449 }
1450 };
1451 FakeBlockServer::new(server, |_| false, fifo_handler).run().await;
1452 assert!(flow_id.lock().is_some());
1454 }
1455 );
1456 }
1457}