Skip to main content

block_client/
lib.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A Rust client library for the Fuchsia block protocol.
6//!
7//! This crate provides a low-level client for interacting with block devices over the
8//! `fuchsia.hardware.block` FIDL protocol and the FIFO interface for block I/O.
9//!
10//! See the [`BlockClient`] trait.
11
12use 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
42/// If a trace flow ID isn't specified for requests, one will be generated.
43pub 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
68// Generates a trace ID that will be unique across the system (as long as |request_id| isn't
69// reused within this process).
70fn 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    // The fifo.
119    fifo: Option<fasync::Fifo<BlockFifoResponse, BlockFifoRequest>>,
120
121    // The next request ID to be used.
122    next_request_id: u32,
123
124    // A queue of messages to be sent on the fifo.
125    queue: std::collections::VecDeque<BlockFifoRequest>,
126
127    // Map from request ID to RequestState.
128    map: HashMap<u32, RequestState>,
129
130    // The waker for the FifoPoller.
131    poller_waker: Option<Waker>,
132
133    // If set, attach a barrier to the next write request
134    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    // Returns true if polling should be terminated.
152    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
182// A future used for fifo responses.
183struct 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                // Request was still pending!  This is a cancellation which we do not support.  We
215                // do not know the disposition of any VMO that the far end might still be writing
216                // to.  To avoid potential corruption (e.g. a client reuses a buffer that might
217                // still be being used by the driver), terminate the connection to prevent any
218                // future use.
219                state.terminate();
220            }
221        }
222        update_outstanding_requests_counter(state.map.len());
223    }
224}
225
226/// Wraps a vmo-id. Will panic if you forget to detach.
227#[derive(Debug)]
228#[must_use]
229pub struct VmoId(AtomicU16);
230
231impl VmoId {
232    /// VmoIds will normally be vended by attach_vmo, but this might be used in some tests
233    pub fn new(id: u16) -> Self {
234        Self(AtomicU16::new(id))
235    }
236
237    /// Invalidates self and returns a new VmoId with the same underlying ID.
238    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    /// Takes the ID.  The caller assumes responsibility for detaching.
247    #[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
277/// Represents a client connection to a block device. This is a simplified version of the block.fidl
278/// interface.
279/// Most users will use the RemoteBlockClient instantiation of this trait.
280pub trait BlockClient: Send + Sync {
281    /// Wraps AttachVmo from fuchsia.hardware.block::Block.
282    ///
283    /// # Safety
284    ///
285    /// The caller must ensure that no references are held during I/O as this would be
286    /// undefined behavior.  The caller may hold pointers, which does not lead to undefined
287    /// behavior; Rust does not make the same assumptions as references for pointers.
288    ///
289    /// Whilst a connection failure or client-side cancellation does not immediately lead to
290    /// Rust undefined behavior, the caller must thereafter assume the VMO is poisoned.  No
291    /// assumptions can be made regarding what might be written to the VMO by the far end, or
292    /// anything the far end might have delegated access to.  The only safe thing to do is
293    /// discard the VMO and not attempt to use or re-attach it.
294    ///
295    /// Attaching the VMO once for its lifetime is safe, if strictly more than necessary.
296    unsafe fn attach_vmo(
297        &self,
298        vmo: &zx::Vmo,
299    ) -> impl Future<Output = Result<VmoId, zx::Status>> + Send;
300
301    /// Detaches the given vmo-id from the device.
302    fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> + Send;
303
304    /// Reads from the device at |device_offset| into the given buffer slice.
305    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    /// Writes the data in |buffer_slice| to the device.
331    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    /// Trims the given range on the block device.
362    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    /// Attaches a barrier to the next write sent to the underlying block device. This barrier
376    /// method is an alternative to setting the WriteOption::PRE_BARRIER on `write_at_with_opts`.
377    /// This method makes it easier to guarantee that the barrier is attached to the correct
378    /// write operation when subsequent write operations can get reordered.
379    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    /// Sends a flush request to the underlying block device.
386    fn flush_traced(
387        &self,
388        trace_flow_id: u64,
389    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
390
391    /// Closes the fifo.
392    fn close(&self) -> impl Future<Output = Result<(), zx::Status>> + Send;
393
394    /// Returns the block size of the device.
395    fn block_size(&self) -> u32;
396
397    /// Returns the size, in blocks, of the device.
398    fn block_count(&self) -> u64;
399
400    /// Returns the maximum number of blocks which can be transferred in a single request.
401    fn max_transfer_blocks(&self) -> Option<NonZero<u32>>;
402
403    /// Returns the block flags reported by the device.
404    fn block_flags(&self) -> BlockDeviceFlag;
405
406    /// Returns true if the remote fifo is still connected.
407    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    // Sends the request and waits for the response.
453    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                // Fifo has been closed.
468                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        // It's OK to leak the VMO id because the server will dump all VMOs when the fifo is torn
720        // down.
721        let _ = self.temp_vmo_id.take().into_id();
722        self.fifo_state.lock().terminate();
723    }
724}
725
726// RemoteBlockClient is a BlockClient that communicates with a real block device over FIDL.
727pub struct RemoteBlockClient {
728    session: block::SessionProxy,
729    common: Common,
730}
731
732impl RemoteBlockClient {
733    /// Returns a connection to a remote block device via the given channel.
734    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    /// Returns a connection to a remote block device via the given channel, but spawns a separate
843    /// thread for polling the fifo which makes it work in cases where no executor is configured for
844    /// the calling thread.
845    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        // The fifo needs to be instantiated from the thread that has the executor as that's where
869        // the fifo registers for notifications to be delivered.
870        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    /// Wraps AttachVmo from fuchsia.hardware.block::Block.
883    ///
884    /// # Safety
885    ///
886    /// See `BlockClient::attach_vmo`.
887    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        // Ignore errors here as there is not much we can do about it.
956        let _ = self.close();
957    }
958}
959
960// FifoPoller is a future responsible for sending and receiving from the fifo.
961struct 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(); // So that we can split the borrow.
971
972        // Send requests.
973        if state.poll_send_requests(context) {
974            return Poll::Ready(());
975        }
976
977        // Receive responses.
978        let fifo = state.fifo.as_ref().unwrap(); // Safe because poll_send_requests checks.
979        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 the request isn't in the map, assume that it's a cancelled read.
990                    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        // SAFETY: Test code, only attach once, no other mappings.
1054        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        // SAFETY: Test code, only attach once, no other mappings.
1074        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        // SAFETY: Test code, only attach once, no other mappings.
1087        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        // SAFETY: Test code, only attach once, no other mappings.
1105        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        // Destroying the ramdisk is asynchronous. Keep issuing reads until they start failing.
1117        while block_client
1118            .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1119            .await
1120            .is_ok()
1121        {}
1122
1123        // Sometimes the FIFO will start rejecting requests before FIFO is actually closed, so we
1124        // get false-positives from is_connected.
1125        while block_client.is_connected() {
1126            // Sleep for a bit to minimise lock contention.
1127            fasync::Timer::new(fasync::MonotonicInstant::after(
1128                zx::MonotonicDuration::from_millis(500),
1129            ))
1130            .await;
1131        }
1132
1133        // But once is_connected goes negative, it should stay negative.
1134        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        // SAFETY: Test code, only attach once, no other mappings.
1143        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            // Read the first 500 results and then dump the rest.
1152            for _ in 0..500 {
1153                reads.next().await;
1154            }
1155        }
1156
1157        // Since we dropped pending futures (cancellation), the client must forcibly
1158        // terminate the connection to prevent any future unsafe I/O. Thus, further
1159        // calls like `detach_vmo` must fail with CANCELED.
1160        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            // Read back an extra block either side.
1175            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    // Implements dummy server which can be used by test cases to verify whether
1201    // channel messages and fifo operations are being received - by using set_channel_handler or
1202    // set_fifo_hander respectively
1203    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        // Creates a new FakeBlockServer given a channel to listen on.
1211        //
1212        // 'channel_handler' and 'fifo_handler' closures allow for customizing the way how the server
1213        // handles requests received from channel or the fifo respectfully.
1214        //
1215        // 'channel_handler' receives a message before it is handled by the default implementation
1216        // and can return 'true' to indicate all processing is done and no further processing of
1217        // that message is required
1218        //
1219        // 'fifo_handler' takes as input a BlockFifoRequest and produces a response which the
1220        // FakeBlockServer will send over the fifo.
1221        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        // Runs the server
1234        async fn run(&mut self) {
1235            let server = self.server_channel.take().unwrap();
1236
1237            // Set up a mock server.
1238            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                                        // Give a chance for the test to register and potentially
1290                                        // handle the event
1291                                        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            //_result can be Err(Aborted) since FifoClose calls .abort but that's expected
1328        }
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            // The drop here should cause Close to be sent.
1340        });
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        // After the server has finished running, we can check to see that close was called.
1351        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                // After the server has finished running, verify the trace flow ID was set to some value.
1457                assert!(flow_id.lock().is_some());
1458            }
1459        );
1460    }
1461}