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