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