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<Result<(), 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
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(Err(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.get());
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)
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 flush(&self) -> impl Future<Output = Result<(), zx::Status>> + Send {
373 self.flush_traced(NO_TRACE_ID)
374 }
375
376 fn flush_traced(
378 &self,
379 trace_flow_id: u64,
380 ) -> impl Future<Output = Result<(), zx::Status>> + Send;
381
382 fn close(&self) -> impl Future<Output = Result<(), zx::Status>> + Send;
384
385 fn block_size(&self) -> u32;
387
388 fn block_count(&self) -> u64;
390
391 fn max_transfer_blocks(&self) -> Option<NonZero<u32>>;
393
394 fn block_flags(&self) -> BlockDeviceFlag;
396
397 fn is_connected(&self) -> bool;
399}
400
401struct Common {
402 block_size: u32,
403 block_count: u64,
404 max_transfer_blocks: Option<NonZero<u32>>,
405 block_flags: BlockDeviceFlag,
406 fifo_state: FifoStateRef,
407 temp_vmo: futures::lock::Mutex<zx::Vmo>,
408 temp_vmo_id: VmoId,
409}
410
411impl Common {
412 fn new(
413 fifo: fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
414 info: &block::BlockInfo,
415 temp_vmo: zx::Vmo,
416 temp_vmo_id: VmoId,
417 ) -> Self {
418 let fifo_state = Arc::new(Mutex::new(FifoState { fifo: Some(fifo), ..Default::default() }));
419 fasync::Task::spawn(FifoPoller { fifo_state: fifo_state.clone() }).detach();
420 Self {
421 block_size: info.block_size,
422 block_count: info.block_count,
423 max_transfer_blocks: if info.max_transfer_size != MAX_TRANSFER_UNBOUNDED {
424 NonZero::new(info.max_transfer_size / info.block_size)
425 } else {
426 None
427 },
428 block_flags: info.flags,
429 fifo_state,
430 temp_vmo: futures::lock::Mutex::new(temp_vmo),
431 temp_vmo_id,
432 }
433 }
434
435 fn to_blocks(&self, bytes: u64) -> Result<u64, zx::Status> {
436 if bytes % self.block_size as u64 != 0 {
437 Err(zx::Status::INVALID_ARGS)
438 } else {
439 Ok(bytes / self.block_size as u64)
440 }
441 }
442
443 async fn send(&self, mut request: BlockFifoRequest) -> Result<(), zx::Status> {
445 let (request_id, trace_flow_id) = {
446 let mut state = self.fifo_state.lock();
447
448 if state.fifo.is_none() {
449 return Err(zx::Status::CANCELED);
451 }
452 trace::duration!(
453 "storage",
454 "block_client::send::start",
455 "op" => opcode_str(request.command.opcode),
456 "len" => request.length * self.block_size
457 );
458 let request_id = state.next_request_id;
459 state.next_request_id = state.next_request_id.overflowing_add(1).0;
460 assert!(
461 state.map.insert(request_id, RequestState::default()).is_none(),
462 "request id in use!"
463 );
464 update_outstanding_requests_counter(state.map.len());
465 request.reqid = request_id;
466 if request.trace_flow_id == NO_TRACE_ID {
467 request.trace_flow_id = generate_trace_flow_id(request_id);
468 }
469 let trace_flow_id = request.trace_flow_id;
470 trace::flow_begin!("storage", "block_client::send", trace_flow_id.into());
471 state.queue.push_back(request);
472 if let Some(waker) = state.poller_waker.clone() {
473 state.poll_send_requests(&mut Context::from_waker(&waker));
474 }
475 (request_id, trace_flow_id)
476 };
477 ResponseFuture::new(self.fifo_state.clone(), request_id).await?;
478 trace::duration!("storage", "block_client::send::end");
479 trace::flow_end!("storage", "block_client::send", trace_flow_id.into());
480 Ok(())
481 }
482
483 fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> {
484 self.send(BlockFifoRequest {
485 command: BlockFifoCommand {
486 opcode: BlockOpcode::CloseVmo.into_primitive(),
487 flags: 0,
488 ..Default::default()
489 },
490 vmoid: vmo_id.into_id(),
491 ..Default::default()
492 })
493 }
494
495 async fn read_at(
496 &self,
497 buffer_slice: MutableBufferSlice<'_>,
498 device_offset: u64,
499 opts: ReadOptions,
500 trace_flow_id: u64,
501 ) -> Result<(), zx::Status> {
502 let mut flags = BlockIoFlag::empty();
503
504 if opts.inline_crypto.is_enabled {
505 flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
506 }
507
508 match buffer_slice {
509 MutableBufferSlice::VmoId { vmo_id, offset, length } => {
510 self.send(BlockFifoRequest {
511 command: BlockFifoCommand {
512 opcode: BlockOpcode::Read.into_primitive(),
513 flags: flags.bits(),
514 ..Default::default()
515 },
516 vmoid: vmo_id.id(),
517 length: self
518 .to_blocks(length)?
519 .try_into()
520 .map_err(|_| zx::Status::INVALID_ARGS)?,
521 vmo_offset: self.to_blocks(offset)?,
522 dev_offset: self.to_blocks(device_offset)?,
523 trace_flow_id,
524 dun: opts.inline_crypto.dun,
525 slot: opts.inline_crypto.slot,
526 ..Default::default()
527 })
528 .await?
529 }
530 MutableBufferSlice::Memory(mut slice) => {
531 let temp_vmo = self.temp_vmo.lock().await;
532 let mut device_block = self.to_blocks(device_offset)?;
533 loop {
534 let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
535 let block_count = self.to_blocks(to_do as u64)? as u32;
536 self.send(BlockFifoRequest {
537 command: BlockFifoCommand {
538 opcode: BlockOpcode::Read.into_primitive(),
539 flags: flags.bits(),
540 ..Default::default()
541 },
542 vmoid: self.temp_vmo_id.id(),
543 length: block_count,
544 vmo_offset: 0,
545 dev_offset: device_block,
546 trace_flow_id,
547 dun: opts.inline_crypto.dun,
548 slot: opts.inline_crypto.slot,
549 ..Default::default()
550 })
551 .await?;
552 temp_vmo.read(&mut slice[..to_do], 0)?;
553 if to_do == slice.len() {
554 break;
555 }
556 device_block += block_count as u64;
557 slice = &mut slice[to_do..];
558 }
559 }
560 }
561 Ok(())
562 }
563
564 async fn write_at(
565 &self,
566 buffer_slice: BufferSlice<'_>,
567 device_offset: u64,
568 opts: WriteOptions,
569 trace_flow_id: u64,
570 ) -> Result<(), zx::Status> {
571 let mut flags = BlockIoFlag::empty();
572
573 if opts.flags.contains(WriteFlags::FORCE_ACCESS) {
574 flags |= BlockIoFlag::FORCE_ACCESS;
575 }
576
577 if opts.flags.contains(WriteFlags::PRE_BARRIER) {
578 flags |= BlockIoFlag::PRE_BARRIER;
579 }
580
581 if opts.inline_crypto.is_enabled {
582 flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
583 }
584
585 match buffer_slice {
586 BufferSlice::VmoId { vmo_id, offset, length } => {
587 self.send(BlockFifoRequest {
588 command: BlockFifoCommand {
589 opcode: BlockOpcode::Write.into_primitive(),
590 flags: flags.bits(),
591 ..Default::default()
592 },
593 vmoid: vmo_id.id(),
594 length: self
595 .to_blocks(length)?
596 .try_into()
597 .map_err(|_| zx::Status::INVALID_ARGS)?,
598 vmo_offset: self.to_blocks(offset)?,
599 dev_offset: self.to_blocks(device_offset)?,
600 trace_flow_id,
601 dun: opts.inline_crypto.dun,
602 slot: opts.inline_crypto.slot,
603 ..Default::default()
604 })
605 .await?;
606 }
607 BufferSlice::Memory(mut slice) => {
608 let temp_vmo = self.temp_vmo.lock().await;
609 let mut device_block = self.to_blocks(device_offset)?;
610 loop {
611 let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
612 let block_count = self.to_blocks(to_do as u64)? as u32;
613 temp_vmo.write(&slice[..to_do], 0)?;
614 self.send(BlockFifoRequest {
615 command: BlockFifoCommand {
616 opcode: BlockOpcode::Write.into_primitive(),
617 flags: flags.bits(),
618 ..Default::default()
619 },
620 vmoid: self.temp_vmo_id.id(),
621 length: block_count,
622 vmo_offset: 0,
623 dev_offset: device_block,
624 trace_flow_id,
625 dun: opts.inline_crypto.dun,
626 slot: opts.inline_crypto.slot,
627 ..Default::default()
628 })
629 .await?;
630 if to_do == slice.len() {
631 break;
632 }
633 device_block += block_count as u64;
634 slice = &slice[to_do..];
635 }
636 }
637 }
638 Ok(())
639 }
640
641 async fn trim(&self, device_range: Range<u64>, trace_flow_id: u64) -> Result<(), zx::Status> {
642 let length = self.to_blocks(device_range.end - device_range.start)? as u32;
643 let dev_offset = self.to_blocks(device_range.start)?;
644 self.send(BlockFifoRequest {
645 command: BlockFifoCommand {
646 opcode: BlockOpcode::Trim.into_primitive(),
647 flags: 0,
648 ..Default::default()
649 },
650 vmoid: VMOID_INVALID,
651 length,
652 dev_offset,
653 trace_flow_id,
654 ..Default::default()
655 })
656 .await
657 }
658
659 fn flush(&self, trace_flow_id: u64) -> impl Future<Output = Result<(), zx::Status>> {
660 self.send(BlockFifoRequest {
661 command: BlockFifoCommand {
662 opcode: BlockOpcode::Flush.into_primitive(),
663 flags: 0,
664 ..Default::default()
665 },
666 vmoid: VMOID_INVALID,
667 trace_flow_id,
668 ..Default::default()
669 })
670 }
671
672 fn block_size(&self) -> u32 {
673 self.block_size
674 }
675
676 fn block_count(&self) -> u64 {
677 self.block_count
678 }
679
680 fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
681 self.max_transfer_blocks.clone()
682 }
683
684 fn block_flags(&self) -> BlockDeviceFlag {
685 self.block_flags
686 }
687
688 fn is_connected(&self) -> bool {
689 self.fifo_state.lock().fifo.is_some()
690 }
691}
692
693impl Drop for Common {
694 fn drop(&mut self) {
695 let _ = self.temp_vmo_id.take().into_id();
698 self.fifo_state.lock().terminate();
699 }
700}
701
702pub struct RemoteBlockClient {
704 session: block::SessionProxy,
705 common: Common,
706}
707
708impl RemoteBlockClient {
709 pub async fn new(remote: impl Borrow<block::BlockProxy>) -> Result<Self, zx::Status> {
711 let remote = remote.borrow();
712 let info =
713 remote.get_info().await.map_err(fidl_to_status)?.map_err(zx::Status::err_from_raw)?;
714 let (session, server) = fidl::endpoints::create_proxy();
715 let () = remote.open_session(server).map_err(fidl_to_status)?;
716 Self::from_session(info, session).await
717 }
718
719 pub async fn from_session(
720 info: block::BlockInfo,
721 session: block::SessionProxy,
722 ) -> Result<Self, zx::Status> {
723 const SCRATCH_VMO_NAME: zx::Name = zx::Name::new_lossy("block-client-scratch-vmo");
724 let fifo =
725 session.get_fifo().await.map_err(fidl_to_status)?.map_err(zx::Status::err_from_raw)?;
726 let fifo = fasync::Fifo::from_fifo(fifo);
727 let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
728 temp_vmo.set_name(&SCRATCH_VMO_NAME)?;
729 let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
730 let vmo_id = session
731 .attach_vmo(dup)
732 .await
733 .map_err(fidl_to_status)?
734 .map_err(zx::Status::err_from_raw)?;
735 let vmo_id = VmoId::new(vmo_id.id);
736 Ok(RemoteBlockClient { session, common: Common::new(fifo, &info, temp_vmo, vmo_id) })
737 }
738}
739
740impl BlockClient for RemoteBlockClient {
741 async unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
742 let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
743 let vmo_id = self
744 .session
745 .attach_vmo(dup)
746 .await
747 .map_err(fidl_to_status)?
748 .map_err(zx::Status::err_from_raw)?;
749 Ok(VmoId::new(vmo_id.id))
750 }
751
752 fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> {
753 self.common.detach_vmo(vmo_id)
754 }
755
756 fn read_at_with_opts_traced(
757 &self,
758 buffer_slice: MutableBufferSlice<'_>,
759 device_offset: u64,
760 opts: ReadOptions,
761 trace_flow_id: u64,
762 ) -> impl Future<Output = Result<(), zx::Status>> {
763 self.common.read_at(buffer_slice, device_offset, opts, trace_flow_id)
764 }
765
766 fn write_at_with_opts_traced(
767 &self,
768 buffer_slice: BufferSlice<'_>,
769 device_offset: u64,
770 opts: WriteOptions,
771 trace_flow_id: u64,
772 ) -> impl Future<Output = Result<(), zx::Status>> {
773 self.common.write_at(buffer_slice, device_offset, opts, trace_flow_id)
774 }
775
776 fn trim_traced(
777 &self,
778 range: Range<u64>,
779 trace_flow_id: u64,
780 ) -> impl Future<Output = Result<(), zx::Status>> {
781 self.common.trim(range, trace_flow_id)
782 }
783
784 fn flush_traced(&self, trace_flow_id: u64) -> impl Future<Output = Result<(), zx::Status>> {
785 self.common.flush(trace_flow_id)
786 }
787
788 async fn close(&self) -> Result<(), zx::Status> {
789 let () = self
790 .session
791 .close()
792 .await
793 .map_err(fidl_to_status)?
794 .map_err(zx::Status::err_from_raw)?;
795 Ok(())
796 }
797
798 fn block_size(&self) -> u32 {
799 self.common.block_size()
800 }
801
802 fn block_count(&self) -> u64 {
803 self.common.block_count()
804 }
805
806 fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
807 self.common.max_transfer_blocks()
808 }
809
810 fn block_flags(&self) -> BlockDeviceFlag {
811 self.common.block_flags()
812 }
813
814 fn is_connected(&self) -> bool {
815 self.common.is_connected()
816 }
817}
818
819pub struct RemoteBlockClientSync {
820 session: block::SessionSynchronousProxy,
821 common: Common,
822}
823
824impl RemoteBlockClientSync {
825 pub fn new(
829 client_end: fidl::endpoints::ClientEnd<block::BlockMarker>,
830 ) -> Result<Self, zx::Status> {
831 let remote = block::BlockSynchronousProxy::new(client_end.into_channel());
832 let info = remote
833 .get_info(zx::MonotonicInstant::INFINITE)
834 .map_err(fidl_to_status)?
835 .map_err(zx::Status::err_from_raw)?;
836 let (client, server) = fidl::endpoints::create_endpoints();
837 let () = remote.open_session(server).map_err(fidl_to_status)?;
838 let session = block::SessionSynchronousProxy::new(client.into_channel());
839 let fifo = session
840 .get_fifo(zx::MonotonicInstant::INFINITE)
841 .map_err(fidl_to_status)?
842 .map_err(zx::Status::err_from_raw)?;
843 let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
844 let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
845 let vmo_id = session
846 .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
847 .map_err(fidl_to_status)?
848 .map_err(zx::Status::err_from_raw)?;
849 let vmo_id = VmoId::new(vmo_id.id);
850
851 let (sender, receiver) = oneshot::channel::<Result<Self, zx::Status>>();
854 std::thread::spawn(move || {
855 let mut executor = fasync::LocalExecutor::default();
856 let fifo = fasync::Fifo::from_fifo(fifo);
857 let common = Common::new(fifo, &info, temp_vmo, vmo_id);
858 let fifo_state = common.fifo_state.clone();
859 let _ = sender.send(Ok(RemoteBlockClientSync { session, common }));
860 executor.run_singlethreaded(FifoPoller { fifo_state });
861 });
862 block_on(receiver).map_err(|_| zx::Status::CANCELED)?
863 }
864
865 pub unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
871 let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
872 let vmo_id = self
873 .session
874 .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
875 .map_err(fidl_to_status)?
876 .map_err(zx::Status::err_from_raw)?;
877 Ok(VmoId::new(vmo_id.id))
878 }
879
880 pub fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
881 block_on(self.common.detach_vmo(vmo_id))
882 }
883
884 pub fn read_at(
885 &self,
886 buffer_slice: MutableBufferSlice<'_>,
887 device_offset: u64,
888 ) -> Result<(), zx::Status> {
889 block_on(self.common.read_at(
890 buffer_slice,
891 device_offset,
892 ReadOptions::default(),
893 NO_TRACE_ID,
894 ))
895 }
896
897 pub fn write_at(
898 &self,
899 buffer_slice: BufferSlice<'_>,
900 device_offset: u64,
901 ) -> Result<(), zx::Status> {
902 block_on(self.common.write_at(
903 buffer_slice,
904 device_offset,
905 WriteOptions::default(),
906 NO_TRACE_ID,
907 ))
908 }
909
910 pub fn flush(&self) -> Result<(), zx::Status> {
911 block_on(self.common.flush(NO_TRACE_ID))
912 }
913
914 pub fn close(&self) -> Result<(), zx::Status> {
915 let () = self
916 .session
917 .close(zx::MonotonicInstant::INFINITE)
918 .map_err(fidl_to_status)?
919 .map_err(zx::Status::err_from_raw)?;
920 Ok(())
921 }
922
923 pub fn block_size(&self) -> u32 {
924 self.common.block_size()
925 }
926
927 pub fn block_count(&self) -> u64 {
928 self.common.block_count()
929 }
930
931 pub fn is_connected(&self) -> bool {
932 self.common.is_connected()
933 }
934}
935
936impl Drop for RemoteBlockClientSync {
937 fn drop(&mut self) {
938 let _ = self.close();
940 }
941}
942
943struct FifoPoller {
945 fifo_state: FifoStateRef,
946}
947
948impl Future for FifoPoller {
949 type Output = ();
950
951 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
952 let mut state_lock = self.fifo_state.lock();
953 let state = state_lock.deref_mut(); if state.poll_send_requests(context) {
957 return Poll::Ready(());
958 }
959
960 let fifo = state.fifo.as_ref().unwrap(); loop {
963 let mut response = MaybeUninit::uninit();
964 match fifo.try_read(context, &mut response) {
965 Poll::Pending => {
966 state.poller_waker = Some(context.waker().clone());
967 return Poll::Pending;
968 }
969 Poll::Ready(Ok(_)) => {
970 let response = unsafe { response.assume_init() };
971 let request_id = response.reqid;
972 if let Some(request_state) = state.map.get_mut(&request_id) {
974 request_state.result.replace(zx::Status::ok(response.status));
975 if let Some(waker) = request_state.waker.take() {
976 waker.wake();
977 }
978 }
979 }
980 Poll::Ready(Err(_)) => {
981 state.terminate();
982 return Poll::Ready(());
983 }
984 }
985 }
986 }
987}
988
989fn update_outstanding_requests_counter(outstanding: usize) {
990 trace::counter!("storage", "block-requests", 0, "outstanding" => outstanding);
991}
992
993#[cfg(test)]
994mod tests {
995 use super::{
996 BlockClient, BlockFifoRequest, BlockFifoResponse, BufferSlice, MutableBufferSlice,
997 RemoteBlockClient, RemoteBlockClientSync, WriteOptions,
998 };
999 use block_protocol::ReadOptions;
1000 use block_server::{BlockServer, DeviceInfo, PartitionInfo};
1001 use fidl::endpoints::RequestStream as _;
1002 use fidl_fuchsia_storage_block as block;
1003 use fuchsia_async as fasync;
1004 use futures::future::{AbortHandle, Abortable, TryFutureExt as _};
1005 use futures::join;
1006 use futures::stream::StreamExt as _;
1007 use futures::stream::futures_unordered::FuturesUnordered;
1008 use ramdevice_client::RamdiskClient;
1009 use std::borrow::Cow;
1010 use std::num::NonZero;
1011 use std::sync::Arc;
1012 use std::sync::atomic::{AtomicBool, Ordering};
1013
1014 const RAMDISK_BLOCK_SIZE: u64 = 1024;
1015 const RAMDISK_BLOCK_COUNT: u64 = 1024;
1016
1017 pub async fn make_ramdisk() -> (RamdiskClient, block::BlockProxy, RemoteBlockClient) {
1018 let ramdisk = RamdiskClient::create(RAMDISK_BLOCK_SIZE, RAMDISK_BLOCK_COUNT)
1019 .await
1020 .expect("RamdiskClient::create failed");
1021 let client_end = ramdisk.open().expect("ramdisk.open failed");
1022 let proxy = client_end.into_proxy();
1023 let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1024 assert_eq!(block_client.block_size(), 1024);
1025 let client_end = ramdisk.open().expect("ramdisk.open failed");
1026 let proxy = client_end.into_proxy();
1027 (ramdisk, proxy, block_client)
1028 }
1029
1030 #[fuchsia::test]
1031 async fn test_against_ram_disk() {
1032 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1033
1034 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1035 vmo.write(b"hello", 5).expect("vmo.write failed");
1036 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1038 block_client
1039 .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1040 .await
1041 .expect("write_at failed");
1042 block_client
1043 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 1024, 2048), 0)
1044 .await
1045 .expect("read_at failed");
1046 let mut buf: [u8; 5] = Default::default();
1047 vmo.read(&mut buf, 1029).expect("vmo.read failed");
1048 assert_eq!(&buf, b"hello");
1049 block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1050 }
1051
1052 #[fuchsia::test]
1053 async fn test_alignment() {
1054 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1055 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1056 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1058 block_client
1059 .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 1)
1060 .await
1061 .expect_err("expected failure due to bad alignment");
1062 block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1063 }
1064
1065 #[fuchsia::test]
1066 async fn test_parallel_io() {
1067 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1068 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1069 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1071 let mut reads = Vec::new();
1072 for _ in 0..1024 {
1073 reads.push(
1074 block_client
1075 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1076 .inspect_err(|e| panic!("read should have succeeded: {}", e)),
1077 );
1078 }
1079 futures::future::join_all(reads).await;
1080 block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1081 }
1082
1083 #[fuchsia::test]
1084 async fn test_closed_device() {
1085 let (ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1086 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1087 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1089 let mut reads = Vec::new();
1090 for _ in 0..1024 {
1091 reads.push(
1092 block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1093 );
1094 }
1095 assert!(block_client.is_connected());
1096 let _ = futures::join!(futures::future::join_all(reads), async {
1097 std::mem::drop(ramdisk);
1098 });
1099 while block_client
1101 .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1102 .await
1103 .is_ok()
1104 {}
1105
1106 while block_client.is_connected() {
1109 fasync::Timer::new(fasync::MonotonicInstant::after(
1111 zx::MonotonicDuration::from_millis(500),
1112 ))
1113 .await;
1114 }
1115
1116 assert_eq!(block_client.is_connected(), false);
1118 let _ = block_client.detach_vmo(vmo_id).await;
1119 }
1120
1121 #[fuchsia::test]
1122 async fn test_cancelled_reads() {
1123 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1124 let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1125 let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1127 {
1128 let mut reads = FuturesUnordered::new();
1129 for _ in 0..1024 {
1130 reads.push(
1131 block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1132 );
1133 }
1134 for _ in 0..500 {
1136 reads.next().await;
1137 }
1138 }
1139
1140 assert_eq!(
1144 block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0).await,
1145 Err(zx::Status::CANCELED)
1146 );
1147 assert_eq!(block_client.detach_vmo(vmo_id).await, Err(zx::Status::CANCELED));
1148 }
1149
1150 #[fuchsia::test]
1151 async fn test_parallel_large_read_and_write_with_memory_succeds() {
1152 let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1153 let block_client_ref = &block_client;
1154 let test_one = |offset, len, fill| async move {
1155 let buf = vec![fill; len];
1156 block_client_ref.write_at(buf[..].into(), offset).await.expect("write_at failed");
1157 let mut read_buf = vec![0u8; len + 2 * RAMDISK_BLOCK_SIZE as usize];
1159 block_client_ref
1160 .read_at(read_buf.as_mut_slice().into(), offset - RAMDISK_BLOCK_SIZE)
1161 .await
1162 .expect("read_at failed");
1163 assert_eq!(
1164 &read_buf[0..RAMDISK_BLOCK_SIZE as usize],
1165 &[0; RAMDISK_BLOCK_SIZE as usize][..]
1166 );
1167 assert_eq!(
1168 &read_buf[RAMDISK_BLOCK_SIZE as usize..RAMDISK_BLOCK_SIZE as usize + len],
1169 &buf[..]
1170 );
1171 assert_eq!(
1172 &read_buf[RAMDISK_BLOCK_SIZE as usize + len..],
1173 &[0; RAMDISK_BLOCK_SIZE as usize][..]
1174 );
1175 };
1176 const WRITE_LEN: usize = super::TEMP_VMO_SIZE * 3 + RAMDISK_BLOCK_SIZE as usize;
1177 join!(
1178 test_one(RAMDISK_BLOCK_SIZE, WRITE_LEN, 0xa3u8),
1179 test_one(2 * RAMDISK_BLOCK_SIZE + WRITE_LEN as u64, WRITE_LEN, 0x7fu8)
1180 );
1181 }
1182
1183 struct FakeBlockServer<'a> {
1187 server_channel: Option<fidl::endpoints::ServerEnd<block::BlockMarker>>,
1188 channel_handler: Box<dyn Fn(&block::SessionRequest) -> bool + 'a>,
1189 fifo_handler: Box<dyn Fn(BlockFifoRequest) -> BlockFifoResponse + 'a>,
1190 }
1191
1192 impl<'a> FakeBlockServer<'a> {
1193 fn new(
1205 server_channel: fidl::endpoints::ServerEnd<block::BlockMarker>,
1206 channel_handler: impl Fn(&block::SessionRequest) -> bool + 'a,
1207 fifo_handler: impl Fn(BlockFifoRequest) -> BlockFifoResponse + 'a,
1208 ) -> FakeBlockServer<'a> {
1209 FakeBlockServer {
1210 server_channel: Some(server_channel),
1211 channel_handler: Box::new(channel_handler),
1212 fifo_handler: Box::new(fifo_handler),
1213 }
1214 }
1215
1216 async fn run(&mut self) {
1218 let server = self.server_channel.take().unwrap();
1219
1220 let (server_fifo, client_fifo) =
1222 zx::Fifo::<BlockFifoRequest, BlockFifoResponse>::create(16)
1223 .expect("Fifo::create failed");
1224 let maybe_server_fifo = fuchsia_sync::Mutex::new(Some(client_fifo));
1225
1226 let (fifo_future_abort, fifo_future_abort_registration) = AbortHandle::new_pair();
1227 let fifo_future = Abortable::new(
1228 async {
1229 let mut fifo = fasync::Fifo::from_fifo(server_fifo);
1230 let (mut reader, mut writer) = fifo.async_io();
1231 let mut request = BlockFifoRequest::default();
1232 loop {
1233 match reader.read_entries(&mut request).await {
1234 Ok(n) if n.get() == 1 => {}
1235 Err(zx::Status::PEER_CLOSED) => break,
1236 Err(e) => panic!("read_entry failed {:?}", e),
1237 _ => unreachable!(),
1238 };
1239
1240 let response = self.fifo_handler.as_ref()(request);
1241 writer
1242 .write_entries(std::slice::from_ref(&response))
1243 .await
1244 .expect("write_entries failed");
1245 }
1246 },
1247 fifo_future_abort_registration,
1248 );
1249
1250 let channel_future = async {
1251 server
1252 .into_stream()
1253 .for_each_concurrent(None, |request| async {
1254 let request = request.expect("unexpected fidl error");
1255
1256 match request {
1257 block::BlockRequest::GetInfo { responder } => {
1258 responder
1259 .send(Ok(&block::BlockInfo {
1260 block_count: 1024,
1261 block_size: 512,
1262 max_transfer_size: 1024 * 1024,
1263 flags: block::DeviceFlag::empty(),
1264 }))
1265 .expect("send failed");
1266 }
1267 block::BlockRequest::OpenSession { session, control_handle: _ } => {
1268 let stream = session.into_stream();
1269 stream
1270 .for_each(|request| async {
1271 let request = request.expect("unexpected fidl error");
1272 if self.channel_handler.as_ref()(&request) {
1275 return;
1276 }
1277 match request {
1278 block::SessionRequest::GetFifo { responder } => {
1279 match maybe_server_fifo.lock().take() {
1280 Some(fifo) => {
1281 responder.send(Ok(fifo.downcast()))
1282 }
1283 None => responder.send(Err(
1284 zx::Status::NO_RESOURCES.into_raw(),
1285 )),
1286 }
1287 .expect("send failed")
1288 }
1289 block::SessionRequest::AttachVmo {
1290 vmo: _,
1291 responder,
1292 } => responder
1293 .send(Ok(&block::VmoId { id: 1 }))
1294 .expect("send failed"),
1295 block::SessionRequest::Close { responder } => {
1296 fifo_future_abort.abort();
1297 responder.send(Ok(())).expect("send failed")
1298 }
1299 }
1300 })
1301 .await
1302 }
1303 _ => panic!("Unexpected message"),
1304 }
1305 })
1306 .await;
1307 };
1308
1309 let _result = join!(fifo_future, channel_future);
1310 }
1312 }
1313
1314 #[fuchsia::test]
1315 async fn test_block_close_is_called() {
1316 let close_called = fuchsia_sync::Mutex::new(false);
1317 let (client_end, server) = fidl::endpoints::create_endpoints::<block::BlockMarker>();
1318
1319 std::thread::spawn(move || {
1320 let _block_client =
1321 RemoteBlockClientSync::new(client_end).expect("RemoteBlockClientSync::new failed");
1322 });
1324
1325 let channel_handler = |request: &block::SessionRequest| -> bool {
1326 if let block::SessionRequest::Close { .. } = request {
1327 *close_called.lock() = true;
1328 }
1329 false
1330 };
1331 FakeBlockServer::new(server, channel_handler, |_| unreachable!()).run().await;
1332
1333 assert!(*close_called.lock());
1335 }
1336
1337 #[fuchsia::test]
1338 async fn test_block_flush_is_called() {
1339 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<block::BlockMarker>();
1340
1341 struct Interface {
1342 flush_called: Arc<AtomicBool>,
1343 }
1344 impl block_server::async_interface::Interface for Interface {
1345 fn get_info(&self) -> Cow<'_, DeviceInfo> {
1346 Cow::Owned(DeviceInfo::Partition(PartitionInfo {
1347 device_flags: fidl_fuchsia_storage_block::DeviceFlag::empty(),
1348 max_transfer_blocks: None,
1349 start_block_offset: None,
1350 block_count: 1000,
1351 type_guid: [0; 16],
1352 instance_guid: [0; 16],
1353 name: "foo".to_string(),
1354 ..Default::default()
1355 }))
1356 }
1357
1358 async fn read(
1359 &self,
1360 _device_block_offset: u64,
1361 _block_count: u32,
1362 _vmo: &Arc<zx::Vmo>,
1363 _vmo_offset: u64,
1364 _opts: ReadOptions,
1365 _trace_flow_id: Option<NonZero<u64>>,
1366 ) -> Result<(), zx::Status> {
1367 unreachable!();
1368 }
1369
1370 async fn write(
1371 &self,
1372 _device_block_offset: u64,
1373 _block_count: u32,
1374 _vmo: &Arc<zx::Vmo>,
1375 _vmo_offset: u64,
1376 _opts: WriteOptions,
1377 _trace_flow_id: Option<NonZero<u64>>,
1378 ) -> Result<(), zx::Status> {
1379 unreachable!();
1380 }
1381
1382 async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
1383 self.flush_called.store(true, Ordering::Relaxed);
1384 Ok(())
1385 }
1386
1387 async fn trim(
1388 &self,
1389 _device_block_offset: u64,
1390 _block_count: u32,
1391 _trace_flow_id: Option<NonZero<u64>>,
1392 ) -> Result<(), zx::Status> {
1393 unreachable!();
1394 }
1395 }
1396
1397 let flush_called = Arc::new(AtomicBool::new(false));
1398
1399 futures::join!(
1400 async {
1401 let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1402
1403 block_client.flush().await.expect("flush failed");
1404 },
1405 async {
1406 let block_server = BlockServer::new(
1407 512,
1408 Arc::new(Interface { flush_called: flush_called.clone() }),
1409 );
1410 block_server.handle_requests(stream.cast_stream()).await.unwrap();
1411 }
1412 );
1413
1414 assert!(flush_called.load(Ordering::Relaxed));
1415 }
1416
1417 #[fuchsia::test]
1418 async fn test_trace_flow_ids_set() {
1419 let (proxy, server) = fidl::endpoints::create_proxy();
1420
1421 futures::join!(
1422 async {
1423 let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1424 block_client.flush().await.expect("flush failed");
1425 },
1426 async {
1427 let flow_id: fuchsia_sync::Mutex<Option<u64>> = fuchsia_sync::Mutex::new(None);
1428 let fifo_handler = |request: BlockFifoRequest| -> BlockFifoResponse {
1429 if request.trace_flow_id > 0 {
1430 *flow_id.lock() = Some(request.trace_flow_id);
1431 }
1432 BlockFifoResponse {
1433 status: zx::sys::ZX_OK,
1434 reqid: request.reqid,
1435 ..Default::default()
1436 }
1437 };
1438 FakeBlockServer::new(server, |_| false, fifo_handler).run().await;
1439 assert!(flow_id.lock().is_some());
1441 }
1442 );
1443 }
1444}