Skip to main content

block_server/
async_interface.rs

1// Copyright 2024 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
5use super::{
6    ActiveRequests, DecodedRequest, DeviceInfo, FIFO_MAX_REQUESTS, HandleRequestResult,
7    IntoOrchestrator, KeyRegistry, OffsetMap, Operation, SessionHelper, TraceFlowId,
8};
9use crate::mapper::serve_mapper_session;
10use crate::verifier::Verifier;
11use anyhow::Error;
12use block_protocol::{BlockFifoRequest, BlockFifoResponse, ReadOptions, WriteFlags, WriteOptions};
13use fidl::endpoints::ServerEnd;
14use fidl_fuchsia_storage_block as fblock;
15use fidl_fuchsia_storage_block::DeviceFlag;
16use fuchsia_async as fasync;
17use fuchsia_sync::Mutex;
18use futures::future::{Fuse, FusedFuture, join};
19use futures::stream::FuturesUnordered;
20use futures::{FutureExt, StreamExt, select_biased};
21use mapping::reader::{BlockService, MAX_READ_BUFFER_SIZE};
22use std::borrow::Cow;
23use std::collections::VecDeque;
24use std::future::{Future, poll_fn};
25use std::mem::MaybeUninit;
26use std::pin::pin;
27use std::sync::{Arc, OnceLock};
28use std::task::{Poll, ready};
29use storage_device::buffer::{Buffer, OwnedBuffer};
30use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
31
32pub trait Interface: Send + Sync + Unpin + 'static {
33    /// Runs `stream` to completion.
34    ///
35    /// `offset_map` is provided by the client, and is used to remap requests. The extents in
36    /// `offset_map` are already validated to be within the range of the partition (as determined by
37    /// the [`PartitionInfo::block_count`] field). The implementation is expected to apply these
38    /// mappings to all FIFO requests, and to ensure all FIFO requests fit within the logical
39    /// extents of the offset map. Note that the default implementation does this for you, and that
40    /// is correct for most implementations.
41    ///
42    /// Implementors can override this method if they want to create a passthrough session instead
43    /// (and can use [`PassthroughSession`] below to do so). Generally, a passthrough session would
44    /// open a session to its underlying device with an offset map applied
45    /// (`fuchsia.storage.block.Block/OpenSessionWithOptions`), which remaps and restricts
46    /// requests to the range the partition should have access to.
47    ///
48    /// Nested mappings (i.e. the case when `offset_map` is non-empty, but the implementation
49    /// creates a passthrough session with its own mapping) would need to be composed by the
50    /// implementation. At this time, no implementations of passthrough sessions support nested
51    /// mappings, but we can add support as needed.
52    ///
53    /// If the implementor uses a [`PassthroughSession`], the following Interface methods
54    /// will not be called, and can be stubbed out:
55    ///   - on_attach_vmo
56    ///   - on_detach_vmo
57    ///   - read
58    ///   - write
59    ///   - flush
60    ///   - trim
61    fn open_session(
62        &self,
63        session_manager: Arc<SessionManager<Self>>,
64        stream: fblock::SessionRequestStream,
65        offset_map: OffsetMap,
66        block_size: u32,
67    ) -> impl Future<Output = Result<(), Error>> + Send {
68        // By default, serve the session rather than forwarding it.
69        session_manager.serve_session(
70            stream,
71            offset_map,
72            self.get_info().max_transfer_blocks(),
73            block_size,
74        )
75    }
76
77    /// Opens a mapper session. Implementations that serve mapper requests directly or
78    /// forward them can implement this method.
79    fn open_mapper_session(
80        session_manager: Arc<SessionManager<Self>>,
81        session: ServerEnd<fblock::MapperSessionMarker>,
82        mapping_vmo: zx::Vmo,
83        block_size: u32,
84        port: Option<zx::Port>,
85        delivery_queue: Option<zx::Vmo>,
86    ) -> Result<impl Future<Output = Result<(), Error>> + Send + 'static, zx::Status> {
87        let buffer_source = BufferSource::new(MAX_READ_BUFFER_SIZE * 16);
88        let vmo = buffer_source.vmo().clone();
89        let allocator = Arc::new(BufferAllocator::new(
90            std::cmp::max(block_size as usize, zx::system_get_page_size() as usize),
91            buffer_source,
92        ));
93        let scope = fasync::Scope::new();
94        let service: Arc<dyn BlockService> = Arc::new(AsyncBlockService::new_with_allocator(
95            session_manager.interface.clone(),
96            block_size,
97            allocator,
98            scope.to_handle(),
99        ));
100        let interface = session_manager.interface.clone();
101        let session_fut = serve_mapper_session(
102            Arc::new(move |mapping_vmo: &zx::Vmo, dq: zx::Vmo| {
103                interface.on_open_mapper_session(mapping_vmo, dq)
104            }),
105            service,
106            session,
107            mapping_vmo,
108            port,
109            delivery_queue,
110        )?;
111        Ok(async move {
112            // Hold an active guard on the session scope so cancelling it does not drop `scope`
113            // (which would abort in-flight reads bypassing their active guards) before
114            // `scope.cancel().await` and `on_detach_vmo` complete.
115            let Some(guard) = fasync::Scope::current().active_guard() else {
116                return Ok(());
117            };
118            session_manager.interface.on_attach_vmo(&vmo).await?;
119            scopeguard::defer! {
120                session_manager.interface.on_detach_vmo(&vmo);
121            }
122            let res = futures::select! {
123                res = session_fut.fuse() => res,
124                _ = guard.on_cancel().fuse() => Ok(()),
125            };
126            scope.cancel().await;
127            res
128        })
129    }
130
131    /// Called when a new mapper session is opened.
132    /// Returns the [`Verifier`] for page delivery.
133    fn on_open_mapper_session(
134        &self,
135        _mapping_vmo: &zx::Vmo,
136        delivery_queue: zx::Vmo,
137    ) -> Result<Verifier, zx::Status> {
138        Ok(Verifier::new(delivery_queue))
139    }
140
141    /// Called whenever a VMO is attached, prior to the VMO's usage in any other methods. Whilst
142    /// the VMO is attached, `vmo` will keep the same address so it is safe to use the pointer
143    /// value (as, say, a key into a HashMap).
144    fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> impl Future<Output = Result<(), zx::Status>> + Send {
145        async { Ok(()) }
146    }
147
148    /// Called whenever a VMO is detached.
149    fn on_detach_vmo(&self, _vmo: &zx::Vmo) {}
150
151    /// Called to get block/partition information.
152    fn get_info(&self) -> Cow<'_, DeviceInfo>;
153
154    /// Called for a request to read bytes.
155    ///
156    /// Implementations are responsible for checking that the request block range
157    /// (`[device_block_offset, device_block_offset + block_count)`) falls within valid
158    /// device/partition bounds, and returning `Err(zx::Status::OUT_OF_RANGE)` if it is out of
159    /// bounds.
160    fn read(
161        &self,
162        device_block_offset: u64,
163        block_count: u32,
164        vmo: &Arc<zx::Vmo>,
165        vmo_offset: u64, // *bytes* not blocks
166        opts: ReadOptions,
167        trace_flow_id: TraceFlowId,
168    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
169
170    /// Called for a request to write bytes.
171    ///
172    /// Implementations are responsible for checking that the request block range
173    /// (`[device_block_offset, device_block_offset + block_count)`) falls within valid
174    /// device/partition bounds, and returning `Err(zx::Status::OUT_OF_RANGE)` if it is out of
175    /// bounds.
176    fn write(
177        &self,
178        device_block_offset: u64,
179        block_count: u32,
180        vmo: &Arc<zx::Vmo>,
181        vmo_offset: u64, // *bytes* not blocks
182        opts: WriteOptions,
183        trace_flow_id: TraceFlowId,
184    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
185
186    /// Called to flush the device.
187    fn flush(
188        &self,
189        trace_flow_id: TraceFlowId,
190    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
191
192    /// Called to trim a region.
193    ///
194    /// Implementations are responsible for checking that the request block range
195    /// (`[device_block_offset, device_block_offset + block_count)`) falls within valid
196    /// device/partition bounds, and returning `Err(zx::Status::OUT_OF_RANGE)` if it is out of
197    /// bounds.
198    fn trim(
199        &self,
200        device_block_offset: u64,
201        block_count: u32,
202        trace_flow_id: TraceFlowId,
203    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
204
205    /// Called to handle the GetVolumeInfo FIDL call.
206    fn get_volume_info(
207        &self,
208    ) -> impl Future<Output = Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status>> + Send
209    {
210        async { Err(zx::Status::NOT_SUPPORTED) }
211    }
212
213    /// Called to handle the QuerySlices FIDL call.
214    fn query_slices(
215        &self,
216        _start_slices: &[u64],
217    ) -> impl Future<Output = Result<Vec<fblock::VsliceRange>, zx::Status>> + Send {
218        async { Err(zx::Status::NOT_SUPPORTED) }
219    }
220
221    /// Called to handle the Extend FIDL call.
222    fn extend(
223        &self,
224        _start_slice: u64,
225        _slice_count: u64,
226    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
227        async { Err(zx::Status::NOT_SUPPORTED) }
228    }
229
230    /// Called to handle the Shrink FIDL call.
231    fn shrink(
232        &self,
233        _start_slice: u64,
234        _slice_count: u64,
235    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
236        async { Err(zx::Status::NOT_SUPPORTED) }
237    }
238}
239
240/// A helper object to run a passthrough (proxy) session.
241pub struct PassthroughSession(fblock::SessionProxy);
242
243impl PassthroughSession {
244    pub fn new(proxy: fblock::SessionProxy) -> Self {
245        Self(proxy)
246    }
247
248    async fn handle_request(&self, request: fblock::SessionRequest) -> Result<(), Error> {
249        match request {
250            fblock::SessionRequest::GetFifo { responder } => {
251                responder.send(self.0.get_fifo().await?)?;
252            }
253            fblock::SessionRequest::AttachVmo { vmo, responder } => {
254                responder.send(self.0.attach_vmo(vmo).await?.as_ref().map_err(|s| *s))?;
255            }
256            fblock::SessionRequest::RegisterKey { key_token, responder } => {
257                responder.send(self.0.register_key(key_token).await?)?;
258            }
259            fblock::SessionRequest::Close { responder } => {
260                responder.send(self.0.close().await?)?;
261            }
262        }
263        Ok(())
264    }
265
266    /// Runs `stream` until completion.
267    pub async fn serve(&self, mut stream: fblock::SessionRequestStream) -> Result<(), Error> {
268        while let Some(Ok(request)) = stream.next().await {
269            if let Err(error) = self.handle_request(request).await {
270                log::warn!(error:?; "FIDL error");
271            }
272        }
273        Ok(())
274    }
275}
276
277pub struct SessionManager<I: Interface + ?Sized> {
278    interface: Arc<I>,
279    active_requests: ActiveRequests<usize>,
280    key_registry: KeyRegistry,
281
282    // NOTE: This must be dropped *after* `active_requests` because we store `Buffer<'_>` with an
283    // erased ('static) lifetime in `ActiveRequest`.
284    buffer_allocator: OnceLock<BufferAllocator>,
285}
286
287impl<I: Interface + ?Sized> Drop for SessionManager<I> {
288    fn drop(&mut self) {
289        if let Some(allocator) = self.buffer_allocator.get() {
290            self.interface.on_detach_vmo(allocator.buffer_source().vmo());
291        }
292    }
293}
294
295impl<I: Interface + ?Sized> SessionManager<I> {
296    pub fn new(interface: Arc<I>) -> Self {
297        Self {
298            interface,
299            active_requests: ActiveRequests::default(),
300            key_registry: KeyRegistry::default(),
301            buffer_allocator: OnceLock::new(),
302        }
303    }
304
305    pub fn interface(&self) -> &Arc<I> {
306        &self.interface
307    }
308
309    /// Runs `stream` until completion.
310    pub async fn serve_session(
311        self: Arc<Self>,
312        stream: fblock::SessionRequestStream,
313        offset_map: OffsetMap,
314        max_transfer_blocks: Option<std::num::NonZero<u32>>,
315        block_size: u32,
316    ) -> Result<(), Error> {
317        let (helper, fifo) =
318            SessionHelper::new(self.clone(), offset_map, max_transfer_blocks, block_size)?;
319        let session = Arc::new(Session {
320            helper: Arc::new(helper),
321            interface: self.interface.clone(),
322            close_callback: Mutex::new(None),
323        });
324
325        let (stop_sender, stop_receiver) = futures::channel::oneshot::channel();
326
327        let mut stream = stream.fuse();
328        let scope = fasync::Scope::new();
329        let session_clone = session.clone();
330        let mut fifo_task = scope
331            .spawn(async move {
332                if let Err(status) = session_clone.run_fifo(fifo, stop_receiver).await {
333                    if status != zx::Status::PEER_CLOSED {
334                        log::error!(status:?; "FIFO error");
335                    }
336                }
337            })
338            .fuse();
339
340        // Make sure we detach VMOs when we go out of scope.
341        scopeguard::defer! {
342            for (_, registered_vmo) in session.helper.take_vmos() {
343                self.interface.on_detach_vmo(&registered_vmo.vmo);
344            }
345        }
346
347        let mut closing = false;
348        let mut stop_sender = Some(stop_sender);
349
350        loop {
351            futures::select! {
352                maybe_req = if closing {
353                    futures::future::pending().left_future()
354                } else {
355                    stream.next().right_future()
356                } => {
357                    if let Some(req) = maybe_req {
358                        match session.helper.handle_request(req?).await? {
359                            HandleRequestResult::Ok => {},
360                            HandleRequestResult::Closed(callback) => {
361                                *session.close_callback.lock() = Some(callback);
362                                // Client explicitly closed stream, stop processing.
363                                if let Some(sender) = stop_sender.take() {
364                                    let _ = sender.send(());
365                                }
366                                closing = true;
367                            }
368                        }
369                    } else {
370                        // Client end of stream dropped, stop processing.
371                        if let Some(sender) = stop_sender.take() {
372                            let _ = sender.send(());
373                        }
374                        closing = true;
375                    }
376                }
377                _ = fifo_task => break,
378            }
379        }
380        Ok(())
381    }
382}
383
384pub struct Session<I: Interface + ?Sized> {
385    interface: Arc<I>,
386    helper: Arc<SessionHelper<SessionManager<I>>>,
387    close_callback: Mutex<Option<Box<dyn FnOnce() + Send + 'static>>>,
388}
389
390impl<I: Interface + ?Sized> Session<I> {
391    // A task loop for receiving and responding to FIFO requests.
392    async fn run_fifo(
393        &self,
394        fifo: zx::Fifo<BlockFifoRequest, BlockFifoResponse>,
395        stop_signal: futures::channel::oneshot::Receiver<()>,
396    ) -> Result<(), zx::Status> {
397        scopeguard::defer! {
398            // Ensure that we always clean up active requests for this session upon FIFO
399            // termination.
400            self.helper.drop_active_requests(|session| *session == self as *const _ as usize);
401        }
402
403        // The FIFO has to be processed by a single task due to implementation constraints on
404        // fuchsia_async::Fifo.  Thus, we use an event loop to drive the FIFO.  FIFO reads and
405        // writes can happen in batch, and request processing is parallel.
406        //
407        // The general flow is:
408        //  - Read messages from the FIFO, write into `requests`.
409        //  - Read `requests`, decode them, and spawn a task to process them in `active_requests`,
410        //    which will eventually write them into `responses`.
411        //  - Read `responses` and write out to the FIFO.
412        let mut fifo = fasync::Fifo::from_fifo(fifo);
413        let (mut reader, mut writer) = fifo.async_io();
414        let mut requests = [MaybeUninit::<BlockFifoRequest>::uninit(); FIFO_MAX_REQUESTS];
415        let active_requests = &self.helper.session_manager().active_requests;
416        let mut active_request_futures = FuturesUnordered::new();
417        let mut responses = Vec::new();
418
419        // We map requests using a single future `map_future`.  `pending_mappings` is used to queue
420        // up requests that need to be mapped.  This will serialise how mappings occur which might
421        // make updating mapping caches simpler.  If this proves to be a performance issue, we can
422        // optimise it.
423        let mut map_future = pin!(Fuse::terminated());
424        let mut pending_mappings: VecDeque<DecodedRequest> = VecDeque::new();
425
426        // When `stop_signal` is received, we stop reading from the FIFO and wait for in-flight
427        // tasks to complete.
428        let mut stop_signal = pin!(stop_signal.fuse());
429        let mut is_closed = false;
430
431        loop {
432            let new_requests = {
433                // We provide some flow control by limiting how many in-flight requests we will
434                // allow.
435                let pending_requests = active_request_futures.len() + responses.len();
436
437                if is_closed
438                    && pending_requests == 0
439                    && map_future.is_terminated()
440                    && pending_mappings.is_empty()
441                {
442                    return Ok(());
443                }
444
445                let count = requests.len().saturating_sub(pending_requests);
446                let mut receive_requests = pin!(if count == 0 || is_closed {
447                    Fuse::terminated()
448                } else {
449                    reader.read_entries(&mut requests[..count]).fuse()
450                });
451                let mut send_responses = pin!(if responses.is_empty() {
452                    Fuse::terminated()
453                } else {
454                    poll_fn(|cx| -> Poll<Result<(), zx::Status>> {
455                        match ready!(writer.try_write(cx, &responses[..])) {
456                            Ok(written) => {
457                                responses.drain(..written.get());
458                                Poll::Ready(Ok(()))
459                            }
460                            Err(status) => Poll::Ready(Err(status)),
461                        }
462                    })
463                    .fuse()
464                });
465
466                // Order is important here.  We want to prioritize sending results on the FIFO and
467                // processing FIFO messages over receiving new ones, to provide flow control.
468                select_biased!(
469                    res = send_responses => {
470                        res?;
471                        0
472                    },
473                    response = active_request_futures.select_next_some() => {
474                        responses.extend(response);
475                        0
476                    }
477                    result = map_future => {
478                        match result {
479                            Ok((request, remainder, commit_decompression_buffers)) => {
480                                active_request_futures.push(self.process_fifo_request(
481                                    request,
482                                    commit_decompression_buffers,
483                                ));
484                                if let Some(remainder) = remainder {
485                                    map_future.set(
486                                        self.map_request_or_get_response(remainder).fuse()
487                                    );
488                                }
489                            }
490                            Err(response) => responses.extend(response),
491                        }
492                        if map_future.is_terminated() {
493                            if let Some(request) = pending_mappings.pop_front() {
494                                map_future.set(self.map_request_or_get_response(request).fuse());
495                            }
496                        }
497                        0
498                    }
499                    _ = stop_signal => {
500                        is_closed = true;
501                        0
502                    }
503                    count = receive_requests => {
504                        count?.get()
505                    }
506                )
507            };
508
509            // NB: It is very important that there are no `await`s for the rest of the loop body, as
510            // otherwise active requests might become stalled.
511            for request in &mut requests[..new_requests] {
512                match self.helper.decode_fifo_request(self as *const _ as usize, unsafe {
513                    request.assume_init_mut()
514                }) {
515                    Ok(DecodedRequest {
516                        operation: Operation::CloseVmo, vmo, request_id, ..
517                    }) => {
518                        if let Some(vmo) = vmo {
519                            self.interface.on_detach_vmo(vmo.as_ref());
520                        }
521                        responses.extend(
522                            active_requests
523                                .complete_and_take_response(request_id, Ok(()))
524                                .map(|(_, response)| response),
525                        );
526                    }
527                    Ok(request) => {
528                        if map_future.is_terminated() {
529                            map_future.set(self.map_request_or_get_response(request).fuse());
530                        } else {
531                            pending_mappings.push_back(request);
532                        }
533                    }
534                    Err(None) => {}
535                    Err(Some(response)) => responses.push(response),
536                }
537            }
538        }
539    }
540
541    async fn map_request_or_get_response(
542        &self,
543        request: DecodedRequest,
544    ) -> Result<(DecodedRequest, Option<DecodedRequest>, bool), Option<BlockFifoResponse>> {
545        let request_id = request.request_id;
546        self.map_request(request).await.map_err(|status| {
547            self.helper
548                .orchestrator
549                .active_requests
550                .complete_and_take_response(request_id, Err(status))
551                .map(|(_, r)| r)
552        })
553    }
554
555    // NOTE: The implementation of this currently assumes that we are only processing a single map
556    // request at a time.
557    async fn map_request(
558        &self,
559        mut request: DecodedRequest,
560    ) -> Result<(DecodedRequest, Option<DecodedRequest>, bool), zx::Status> {
561        let mut active_requests;
562        let active_request;
563        let mut commit_decompression_buffers = false;
564        let flags = self.interface.get_info().as_ref().device_flags();
565        // Strip the PRE_BARRIER flag if we don't support it, and simulate the barrier with a
566        // pre-flush.
567        if !flags.contains(DeviceFlag::BARRIER_SUPPORT)
568            && request.operation.take_write_flag(WriteFlags::PRE_BARRIER)
569        {
570            if let Some(id) = request.trace_flow_id {
571                fuchsia_trace::async_instant!(
572                    fuchsia_trace::Id::from(id.get()),
573                    "storage",
574                    "block_server::SimulatedBarrier",
575                    "request_id" => request.request_id.0
576                );
577            }
578            self.interface.flush(request.trace_flow_id).await?;
579        }
580
581        // Handle decompressed read operations by turning them into regular read operations.
582        match request.operation {
583            Operation::StartDecompressedRead {
584                required_buffer_size,
585                device_block_offset,
586                block_count,
587                options,
588            } => {
589                let allocator = match self.helper.session_manager().buffer_allocator.get() {
590                    Some(a) => a,
591                    None => {
592                        // This isn't racy because there should only be one `map_request` future
593                        // running at any one time.
594                        let source = BufferSource::new(fblock::MAX_DECOMPRESSED_BYTES as usize);
595                        self.interface.on_attach_vmo(&source.vmo()).await?;
596                        let allocator = BufferAllocator::new(
597                            std::cmp::max(
598                                self.helper.block_size as usize,
599                                zx::system_get_page_size() as usize,
600                            ),
601                            source,
602                        );
603                        self.helper.session_manager().buffer_allocator.set(allocator).unwrap();
604                        self.helper.session_manager().buffer_allocator.get().unwrap()
605                    }
606                };
607
608                if required_buffer_size > fblock::MAX_DECOMPRESSED_BYTES as usize {
609                    return Err(zx::Status::OUT_OF_RANGE);
610                }
611
612                let buffer = allocator.allocate_buffer(required_buffer_size).await;
613                let vmo_offset = buffer.range().start as u64;
614
615                // # Safety
616                //
617                // See below.
618                unsafe fn remove_lifetime(buffer: Buffer<'_>) -> Buffer<'static> {
619                    unsafe { std::mem::transmute(buffer) }
620                }
621
622                active_requests = self.helper.session_manager().active_requests.0.lock();
623                active_request = &mut active_requests.requests[request.request_id.0];
624
625                // SAFETY: We guarantee that `buffer_allocator` is dropped after `active_requests`,
626                // so this should be safe.
627                active_request.decompression_info.as_mut().unwrap().buffer =
628                    Some(unsafe { remove_lifetime(buffer) });
629
630                request.operation = Operation::Read {
631                    device_block_offset,
632                    block_count,
633                    _unused: 0,
634                    vmo_offset,
635                    options,
636                };
637                request.vmo = Some(allocator.buffer_source().vmo().clone());
638
639                commit_decompression_buffers = true;
640            }
641            Operation::ContinueDecompressedRead {
642                offset,
643                device_block_offset,
644                block_count,
645                options,
646            } => {
647                active_requests = self.helper.session_manager().active_requests.0.lock();
648                active_request = &mut active_requests.requests[request.request_id.0];
649
650                let buffer =
651                    active_request.decompression_info.as_ref().unwrap().buffer.as_ref().unwrap();
652
653                // Make sure this read won't overflow our buffer.
654                if offset >= buffer.len() as u64
655                    || buffer.len() as u64 - offset
656                        < block_count as u64 * self.helper.block_size as u64
657                {
658                    return Err(zx::Status::OUT_OF_RANGE);
659                }
660
661                request.operation = Operation::Read {
662                    device_block_offset,
663                    block_count,
664                    _unused: 0,
665                    vmo_offset: buffer.range().start as u64 + offset,
666                    options,
667                };
668
669                let allocator = self.helper.session_manager().buffer_allocator.get().unwrap();
670                request.vmo = Some(allocator.buffer_source().vmo().clone());
671            }
672            _ => {
673                active_requests = self.helper.session_manager().active_requests.0.lock();
674                active_request = &mut active_requests.requests[request.request_id.0];
675            }
676        }
677
678        // NB: We propagate the FORCE_ACCESS flag to *both* request and remainder, even if we're
679        // using simulated FUA.  However, in `process_fifo_request`, we'll only do the post-flush
680        // once the last request completes.
681        self.helper
682            .map_request(request, active_request)
683            .map(|(request, remainder)| (request, remainder, commit_decompression_buffers))
684    }
685
686    /// Processes a fifo request.
687    async fn process_fifo_request(
688        &self,
689        DecodedRequest { request_id, operation, vmo, trace_flow_id }: DecodedRequest,
690        commit_decompression_buffers: bool,
691    ) -> Option<BlockFifoResponse> {
692        let mut needs_postflush = false;
693        let result = match operation {
694            Operation::Read { device_block_offset, block_count, _unused, vmo_offset, options } => {
695                join(
696                    self.interface.read(
697                        device_block_offset,
698                        block_count,
699                        vmo.as_ref().unwrap(),
700                        vmo_offset,
701                        options,
702                        trace_flow_id,
703                    ),
704                    async {
705                        if commit_decompression_buffers {
706                            let (target_slice, buffer_slice, buffer_range) = {
707                                let active_request = self
708                                    .helper
709                                    .session_manager()
710                                    .active_requests
711                                    .request(request_id);
712                                let info = active_request.decompression_info.as_ref().unwrap();
713                                (
714                                    info.uncompressed_slice(),
715                                    self.helper
716                                        .orchestrator
717                                        .buffer_allocator
718                                        .get()
719                                        .unwrap()
720                                        .buffer_source()
721                                        .slice(),
722                                    info.buffer.as_ref().unwrap().range(),
723                                )
724                            };
725                            let vmar = fuchsia_runtime::vmar_root_self();
726                            // The target slice might not be page aligned.
727                            let addr = target_slice.addr();
728                            let unaligned = addr % zx::system_get_page_size() as usize;
729                            if let Err(error) = vmar.op_range(
730                                zx::VmarOp::COMMIT,
731                                addr - unaligned,
732                                target_slice.len() + unaligned,
733                            ) {
734                                log::warn!(error:?; "Unable to commit target range");
735                            }
736                            // But the buffer range should be.
737                            if let Err(error) = vmar.op_range(
738                                zx::VmarOp::PREFETCH,
739                                buffer_slice.addr() + buffer_range.start,
740                                buffer_range.len(),
741                            ) {
742                                log::warn!(
743                                    error:?,
744                                    buffer_range:?;
745                                    "Unable to prefetch source range",
746                                );
747                            }
748                        }
749                    },
750                )
751                .await
752                .0
753            }
754            Operation::Write {
755                device_block_offset,
756                block_count,
757                _unused,
758                vmo_offset,
759                mut options,
760            } => {
761                // Strip the FORCE_ACCESS flag if we don't support it, and simulate the FUA with a
762                // post-flush.
763                if options.flags.contains(WriteFlags::FORCE_ACCESS) {
764                    let flags = self.interface.get_info().as_ref().device_flags();
765                    if !flags.contains(DeviceFlag::FUA_SUPPORT) {
766                        options.flags.remove(WriteFlags::FORCE_ACCESS);
767                        needs_postflush = true;
768                    }
769                }
770                self.interface
771                    .write(
772                        device_block_offset,
773                        block_count,
774                        vmo.as_ref().unwrap(),
775                        vmo_offset,
776                        options,
777                        trace_flow_id,
778                    )
779                    .await
780            }
781            Operation::Flush => self.interface.flush(trace_flow_id).await,
782            Operation::Trim { device_block_offset, block_count } => {
783                self.interface.trim(device_block_offset, block_count, trace_flow_id).await
784            }
785            Operation::CloseVmo
786            | Operation::StartDecompressedRead { .. }
787            | Operation::ContinueDecompressedRead { .. } => {
788                // Handled in main request loop
789                unreachable!()
790            }
791        };
792        let response = self
793            .helper
794            .orchestrator
795            .active_requests
796            .complete_and_take_response(request_id, result)
797            .map(|(_, r)| r);
798        if let Some(mut response) = response {
799            // Only do the post-flush on the very last request, and only if successful.
800            if zx::Status::ok(response.status).is_ok() && needs_postflush {
801                if let Some(id) = trace_flow_id {
802                    fuchsia_trace::async_instant!(
803                        fuchsia_trace::Id::from(id.get()),
804                        "storage",
805                        "block_server::SimulatedFUA",
806                        "request_id" => request_id.0
807                    );
808                }
809                response.status =
810                    zx::Status::result_into_raw(self.interface.flush(trace_flow_id).await);
811            }
812            Some(response)
813        } else {
814            response
815        }
816    }
817}
818
819impl<I: Interface + ?Sized> super::SessionManager for SessionManager<I> {
820    type Orchestrator = Self;
821
822    const SUPPORTS_DECOMPRESSION: bool = true;
823
824    // We don't need the session, we just need something unique to identify the session.
825    type Session = usize;
826
827    fn session_eq(a: &usize, b: &usize) -> bool {
828        a == b
829    }
830
831    async fn on_attach_vmo(orchestrator: Arc<Self>, vmo: &Arc<zx::Vmo>) -> Result<(), zx::Status> {
832        I::on_attach_vmo(&orchestrator.interface, vmo).await
833    }
834
835    async fn open_session(
836        orchestrator: Arc<Self>,
837        stream: fblock::SessionRequestStream,
838        offset_map: OffsetMap,
839        block_size: u32,
840    ) -> Result<(), Error> {
841        I::open_session(
842            &orchestrator.interface,
843            orchestrator.clone(),
844            stream,
845            offset_map,
846            block_size,
847        )
848        .await
849    }
850
851    fn get_info(&self) -> Cow<'_, DeviceInfo> {
852        self.interface.get_info()
853    }
854
855    async fn get_volume_info(
856        &self,
857    ) -> Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status> {
858        self.interface.get_volume_info().await
859    }
860
861    async fn query_slices(
862        &self,
863        start_slices: &[u64],
864    ) -> Result<Vec<fblock::VsliceRange>, zx::Status> {
865        self.interface.query_slices(start_slices).await
866    }
867
868    async fn extend(&self, start_slice: u64, slice_count: u64) -> Result<(), zx::Status> {
869        self.interface.extend(start_slice, slice_count).await
870    }
871
872    async fn shrink(&self, start_slice: u64, slice_count: u64) -> Result<(), zx::Status> {
873        self.interface.shrink(start_slice, slice_count).await
874    }
875
876    fn open_mapper_session(
877        orchestrator: Arc<Self>,
878        session: fidl::endpoints::ServerEnd<fblock::MapperSessionMarker>,
879        mapping_vmo: zx::Vmo,
880        block_size: u32,
881        port: Option<zx::Port>,
882        delivery_queue: Option<zx::Vmo>,
883    ) -> Result<impl Future<Output = Result<(), Error>> + Send, zx::Status> {
884        I::open_mapper_session(orchestrator, session, mapping_vmo, block_size, port, delivery_queue)
885    }
886
887    fn active_requests(&self) -> &ActiveRequests<Self::Session> {
888        return &self.active_requests;
889    }
890
891    fn key_registry(&self) -> &KeyRegistry {
892        &self.key_registry
893    }
894}
895
896impl<I: Interface + ?Sized> Drop for Session<I> {
897    fn drop(&mut self) {
898        let callback = std::mem::take(&mut *self.close_callback.lock());
899        if let Some(callback) = callback {
900            callback();
901        }
902    }
903}
904
905impl<I: Interface> IntoOrchestrator for Arc<I> {
906    type SM = SessionManager<I>;
907
908    fn into_orchestrator(self) -> Arc<Self::SM> {
909        Arc::new(SessionManager::new(self))
910    }
911}
912
913/// A generic adapter that presents any [`async_interface::Interface`] backend as a
914/// [`BlockService`], using [`BufferAllocator`] for concurrent read buffer management and
915/// [`fasync::ScopeHandle`] for thread-safe task spawning.
916pub struct AsyncBlockService<I: Interface + ?Sized> {
917    interface: Arc<I>,
918    allocator: Arc<BufferAllocator>,
919    block_size: u32,
920    max_transfer_bytes: usize,
921    scope: fasync::ScopeHandle,
922}
923
924impl<I: Interface + ?Sized> AsyncBlockService<I> {
925    pub async fn new(
926        interface: Arc<I>,
927        block_size: u32,
928        pool_capacity: usize,
929        scope: fasync::ScopeHandle,
930    ) -> Result<Self, zx::Status> {
931        let source = BufferSource::new(pool_capacity);
932        interface.on_attach_vmo(&source.vmo()).await?;
933        let allocator = Arc::new(BufferAllocator::new(
934            std::cmp::max(block_size as usize, zx::system_get_page_size() as usize),
935            source,
936        ));
937        Ok(Self::new_with_allocator(interface, block_size, allocator, scope))
938    }
939
940    pub fn new_with_allocator(
941        interface: Arc<I>,
942        block_size: u32,
943        allocator: Arc<BufferAllocator>,
944        scope: fasync::ScopeHandle,
945    ) -> Self {
946        let max_transfer_bytes = interface
947            .get_info()
948            .max_transfer_blocks()
949            .map(|blocks| {
950                let bytes = (blocks.get() as usize).saturating_mul(block_size as usize);
951                std::cmp::max(
952                    bytes & !(mapping::BLOCK_SIZE as usize - 1),
953                    mapping::BLOCK_SIZE as usize,
954                )
955            })
956            .unwrap_or(MAX_READ_BUFFER_SIZE);
957        Self { interface, allocator, block_size, max_transfer_bytes, scope }
958    }
959
960    pub fn interface(&self) -> &Arc<I> {
961        &self.interface
962    }
963
964    pub fn allocator(&self) -> &Arc<BufferAllocator> {
965        &self.allocator
966    }
967}
968
969impl<I: Interface + ?Sized> BlockService for AsyncBlockService<I> {
970    fn allocate_buffer(&self, max_len: usize) -> OwnedBuffer {
971        let max_len = max_len
972            .min(MAX_READ_BUFFER_SIZE)
973            .min(self.max_transfer_bytes)
974            .min(self.allocator.buffer_source().size());
975        self.allocator.allocate_buffer_sync_owned(max_len)
976    }
977
978    fn read_blocks(
979        &self,
980        device_offset: u64,
981        dest_buffer: OwnedBuffer,
982        on_complete: Box<dyn FnOnce(Result<OwnedBuffer, Error>) + Send>,
983    ) -> Result<(), Error> {
984        let block_size = self.block_size as u64;
985        let device_block_offset = device_offset / block_size;
986        let block_count = (dest_buffer.len() as u64 / block_size) as u32;
987
988        let vmo_offset = dest_buffer.range().start as u64;
989        let vmo = self.allocator.buffer_source().vmo().clone();
990        let interface = self.interface.clone();
991        let on_complete = OnComplete(Some(on_complete));
992
993        self.scope.spawn(async move {
994            let res = interface
995                .read(
996                    device_block_offset,
997                    block_count,
998                    &vmo,
999                    vmo_offset,
1000                    ReadOptions::default(),
1001                    TraceFlowId::default(),
1002                )
1003                .await;
1004            match res {
1005                Ok(()) => on_complete.call(Ok(dest_buffer)),
1006                Err(status) => on_complete.call(Err(Error::from(status).context("Read failed"))),
1007            }
1008        });
1009
1010        Ok(())
1011    }
1012}
1013
1014// Invokes `on_complete` with `zx::Status::CANCELED` if the spawned read task is dropped before
1015// completion (e.g. when the mapper session scope is cancelled before the underlying device starts
1016// the request).
1017struct OnComplete(Option<Box<dyn FnOnce(Result<OwnedBuffer, Error>) + Send>>);
1018
1019impl OnComplete {
1020    fn call(mut self, res: Result<OwnedBuffer, Error>) {
1021        if let Some(on_complete) = self.0.take() {
1022            on_complete(res);
1023        }
1024    }
1025}
1026
1027impl Drop for OnComplete {
1028    fn drop(&mut self) {
1029        if let Some(on_complete) = self.0.take() {
1030            on_complete(Err(zx::Status::CANCELED.into()));
1031        }
1032    }
1033}
1034
1035#[cfg(test)]
1036mod tests {
1037    use super::*;
1038    use crate::BlockServer;
1039    use fuchsia_async as fasync;
1040    use mapping::Extents;
1041    use mapping::reader::read_aligned_range;
1042    use std::sync::atomic::{AtomicU64, Ordering};
1043
1044    struct FakeInterface {
1045        data: Vec<u8>,
1046        block_size: u32,
1047        read_count: AtomicU64,
1048    }
1049
1050    impl FakeInterface {
1051        fn new(data: Vec<u8>, block_size: u32) -> Self {
1052            Self { data, block_size, read_count: AtomicU64::new(0) }
1053        }
1054    }
1055
1056    impl Interface for FakeInterface {
1057        fn on_open_mapper_session(
1058            &self,
1059            _mapping_vmo: &zx::Vmo,
1060            delivery_queue: zx::Vmo,
1061        ) -> Result<Verifier, zx::Status> {
1062            Ok(Verifier::new(delivery_queue))
1063        }
1064
1065        fn get_info(&self) -> Cow<'_, DeviceInfo> {
1066            Cow::Owned(DeviceInfo::Block(crate::BlockInfo {
1067                block_count: (self.data.len() / self.block_size as usize) as u64,
1068                max_transfer_blocks: None,
1069                device_flags: fblock::DeviceFlag::empty(),
1070            }))
1071        }
1072
1073        async fn read(
1074            &self,
1075            device_block_offset: u64,
1076            block_count: u32,
1077            vmo: &Arc<zx::Vmo>,
1078            vmo_offset: u64,
1079            _opts: ReadOptions,
1080            _trace_flow_id: TraceFlowId,
1081        ) -> Result<(), zx::Status> {
1082            self.read_count.fetch_add(1, Ordering::Relaxed);
1083            let byte_offset = (device_block_offset * self.block_size as u64) as usize;
1084            let byte_len = (block_count * self.block_size) as usize;
1085            let slice = &self.data[byte_offset..byte_offset + byte_len];
1086            vmo.write(slice, vmo_offset).map_err(|_| zx::Status::IO)?;
1087            Ok(())
1088        }
1089
1090        async fn write(
1091            &self,
1092            _device_block_offset: u64,
1093            _block_count: u32,
1094            _vmo: &Arc<zx::Vmo>,
1095            _vmo_offset: u64,
1096            _opts: WriteOptions,
1097            _trace_flow_id: TraceFlowId,
1098        ) -> Result<(), zx::Status> {
1099            Ok(())
1100        }
1101
1102        async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
1103            Ok(())
1104        }
1105
1106        async fn trim(
1107            &self,
1108            _device_block_offset: u64,
1109            _block_count: u32,
1110            _trace_flow_id: TraceFlowId,
1111        ) -> Result<(), zx::Status> {
1112            Ok(())
1113        }
1114    }
1115
1116    #[fasync::run_singlethreaded(test)]
1117    async fn test_async_block_service_read_aligned_range() {
1118        let block_size = 512;
1119        let test_data: Vec<u8> = (0..8192).map(|i| (i % 255) as u8).collect();
1120        let interface = Arc::new(FakeInterface::new(test_data.clone(), block_size));
1121        let scope = fasync::Scope::current();
1122
1123        let block_service = Arc::new(
1124            AsyncBlockService::new(interface, block_size, 16384, scope)
1125                .await
1126                .expect("Failed to create AsyncBlockService"),
1127        );
1128
1129        let encoded = (1u64 << 32) | 0u64;
1130        let extents = Extents::from_encoded([encoded], 0).unwrap();
1131        let service = block_service.clone();
1132        let (send, recv) = futures::channel::oneshot::channel();
1133        let send = std::sync::Mutex::new(Some(send));
1134        let read_buf = std::sync::Mutex::new(Vec::new());
1135
1136        read_aligned_range(&extents, 0..4096, &*service, move |buffer_result| {
1137            let buffer = buffer_result.unwrap();
1138            let mut read_guard = read_buf.lock().unwrap();
1139            buffer.as_ptr_slice().append_to(&mut read_guard);
1140            if read_guard.len() == 4096 {
1141                if let Some(s) = send.lock().unwrap().take() {
1142                    let _ = s.send(read_guard.clone());
1143                }
1144            }
1145            std::ops::ControlFlow::Continue(())
1146        });
1147
1148        let result = recv.await.unwrap();
1149        assert_eq!(result, &test_data[0..4096]);
1150    }
1151
1152    #[fasync::run_singlethreaded(test)]
1153    async fn test_async_block_service_concurrent_reads_larger_than_pool_capacity() {
1154        let block_size = 512;
1155        let total_size = 65536; // 16 * 4096 bytes
1156        let test_data: Vec<u8> = (0..total_size).map(|i| (i % 251) as u8).collect();
1157        let interface = Arc::new(FakeInterface::new(test_data.clone(), block_size));
1158        let scope = fasync::Scope::current();
1159
1160        // Pool capacity is 8192 bytes (2 * 4096 bytes), while total concurrent reads
1161        // request 65536 bytes.
1162        let pool_capacity = 8192;
1163        let source = BufferSource::new(pool_capacity);
1164        interface.on_attach_vmo(&source.vmo()).await.unwrap();
1165        let allocator = Arc::new(BufferAllocator::new(
1166            std::cmp::max(block_size as usize, zx::system_get_page_size() as usize),
1167            source,
1168        ));
1169        let block_service = Arc::new(AsyncBlockService::new_with_allocator(
1170            interface, block_size, allocator, scope,
1171        ));
1172
1173        let encoded = (16u64 << 32) | 0u64;
1174        let extents = Arc::new(Extents::from_encoded([encoded], 0).unwrap());
1175
1176        // Spawn 4 concurrent read requests on background threads, each requesting 16384 bytes.
1177        let ranges = [0..16384, 16384..32768, 32768..49152, 49152..65536];
1178        let mut receivers = Vec::new();
1179
1180        for range in ranges {
1181            let (send, recv) = futures::channel::oneshot::channel();
1182            let service = block_service.clone();
1183            let extents = extents.clone();
1184
1185            std::thread::spawn(move || {
1186                let send = std::sync::Mutex::new(Some(send));
1187                let read_buf = std::sync::Mutex::new(Vec::new());
1188                let target_len = (range.end - range.start) as usize;
1189
1190                read_aligned_range(&extents, range, &*service, move |buffer_result| {
1191                    let buffer = buffer_result.unwrap();
1192                    let mut read_guard = read_buf.lock().unwrap();
1193                    buffer.as_ptr_slice().append_to(&mut read_guard);
1194                    if read_guard.len() == target_len {
1195                        if let Some(s) = send.lock().unwrap().take() {
1196                            let _ = s.send(read_guard.clone());
1197                        }
1198                    }
1199                    std::ops::ControlFlow::Continue(())
1200                });
1201            });
1202
1203            receivers.push(recv);
1204        }
1205
1206        let results = futures::future::join_all(receivers).await;
1207        for (i, res) in results.into_iter().enumerate() {
1208            let data = res.unwrap();
1209            let start = i * 16384;
1210            let end = start + 16384;
1211            assert_eq!(data, &test_data[start..end]);
1212        }
1213    }
1214
1215    #[fuchsia::test]
1216    async fn test_async_interface_mapper_hierarchical_session() {
1217        use assert_matches::assert_matches;
1218
1219        let block_size = 512;
1220        let test_data = vec![0xCDu8; 8192];
1221        let interface = Arc::new(FakeInterface::new(test_data.clone(), block_size));
1222        let block_server = BlockServer::new(block_size, interface.clone());
1223
1224        let (mapper_proxy, mapper_stream) =
1225            fidl::endpoints::create_proxy_and_stream::<fblock::MapperMarker>();
1226        let scope = fasync::Scope::new();
1227        scope.spawn(async move {
1228            let _ = block_server.handle_mapper_requests(mapper_stream).await;
1229        });
1230
1231        // 1. Open root intermediate mapper session without pager.
1232        let (root_session_proxy, root_session_server) =
1233            fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
1234        let root_mapping_vmo = zx::Vmo::create(65536).unwrap();
1235
1236        let partition_key = 500u64;
1237        let extents =
1238            mapping::Extents::try_new([mapping::Extent::new(0..4096, Some(4096))], 0).unwrap();
1239        let partition_extents: Vec<u64> = mapping::Extents::encode_extents(&extents).collect();
1240        let mut payload_bytes = Vec::new();
1241        for w in &partition_extents {
1242            payload_bytes.extend_from_slice(&w.to_le_bytes());
1243        }
1244        let cmd = mapping::RawMappingCommand {
1245            opcode: mapping::MAPPINGS_COMMAND,
1246            offset: 0,
1247            key: partition_key,
1248            stored_size: 4096,
1249            device_offset: 0,
1250            metadata_count: 0,
1251            extent_count: partition_extents.len() as u32,
1252        };
1253        let mut sender = vmo_fifo::SyncSender::<mapping::RawMappingCommand>::new(
1254            root_mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1255            1024,
1256            256,
1257        )
1258        .unwrap();
1259        let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1260        payload_buf.data().copy_from_slice(&payload_bytes);
1261        payload_buf.commit(cmd).unwrap();
1262
1263        let res = mapper_proxy
1264            .open_session(root_session_server, root_mapping_vmo, None, None)
1265            .await
1266            .unwrap();
1267        assert_matches!(res, Ok(()));
1268
1269        // 2. Open child session with pager through partition key 500.
1270        let pager = Arc::new(zx::Pager::create(zx::PagerOptions::empty()).unwrap());
1271        let port = zx::Port::create();
1272        let child_key = 2002u64;
1273        let paged_vmo = pager.create_vmo(zx::VmoOptions::empty(), &port, child_key, 4096).unwrap();
1274
1275        let child_mapping_vmo = zx::Vmo::create(65536).unwrap();
1276        let delivery_queue = zx::Vmo::create(65536).unwrap();
1277        let vmo_provider = Arc::new(blob_pager_and_verifier::TestVmoProvider::new(
1278            pager.clone(),
1279            delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1280        ));
1281        vmo_provider
1282            .register_vmo(child_key, paged_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap());
1283        let receiver = vmo_fifo::Receiver::<mapping::RawDeliveryCommand>::new(
1284            delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1285            mapping::PENDING_DELIVERY_COMMANDS_CAPACITY,
1286        )
1287        .unwrap();
1288        let _delivery_processor = blob_pager_and_verifier::DeliveryQueueProcessor::spawn(
1289            receiver,
1290            vmo_provider,
1291            delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1292        )
1293        .unwrap();
1294
1295        let extents =
1296            mapping::Extents::try_new([mapping::Extent::new(0..4096, Some(0))], 0).unwrap();
1297        let child_extents: Vec<u64> = mapping::Extents::encode_extents(&extents).collect();
1298        let mut child_payload_bytes = Vec::new();
1299        for w in &child_extents {
1300            child_payload_bytes.extend_from_slice(&w.to_le_bytes());
1301        }
1302        let child_cmd = mapping::RawMappingCommand {
1303            opcode: mapping::MAPPINGS_COMMAND,
1304            offset: 0,
1305            key: child_key,
1306            stored_size: 4096,
1307            device_offset: 0,
1308            metadata_count: 0,
1309            extent_count: child_extents.len() as u32,
1310        };
1311        let mut child_sender = vmo_fifo::SyncSender::<mapping::RawMappingCommand>::new(
1312            child_mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1313            1024,
1314            256,
1315        )
1316        .unwrap();
1317        let mut child_payload_buf =
1318            child_sender.reserve_payload(child_payload_bytes.len()).unwrap();
1319        child_payload_buf.data().copy_from_slice(&child_payload_bytes);
1320        child_payload_buf.commit(child_cmd).unwrap();
1321
1322        let mut child_session = None;
1323        for _ in 0..100 {
1324            let (child_session_proxy, child_session_server) =
1325                fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
1326            let child_res_raw = root_session_proxy
1327                .open_child_session(
1328                    child_session_server,
1329                    child_mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1330                    partition_key,
1331                    Some(port.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1332                    Some(delivery_queue.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap()),
1333                )
1334                .await
1335                .unwrap();
1336            if child_res_raw.is_ok() {
1337                child_session = Some(child_session_proxy);
1338                break;
1339            }
1340            fasync::Timer::new(std::time::Duration::from_millis(10)).await;
1341        }
1342        assert!(child_session.is_some());
1343
1344        let (tx, rx) = futures::channel::oneshot::channel();
1345        std::thread::spawn(move || {
1346            let mut buf = [0u8; 4096];
1347            paged_vmo.read(&mut buf, 0).expect("paged vmo read failed");
1348            let _ = tx.send(buf);
1349        });
1350
1351        let read_bytes = rx.await.unwrap();
1352        assert_eq!(read_bytes.len(), 4096);
1353        assert_eq!(read_bytes, &test_data[4096..8192]);
1354    }
1355
1356    #[fuchsia::test]
1357    async fn test_async_interface_mapper_child_session_closed_when_parent_closed() {
1358        use assert_matches::assert_matches;
1359        use fidl::endpoints::Proxy as _;
1360
1361        let block_size = 512;
1362        let test_data = vec![0u8; 8192];
1363        let interface = Arc::new(FakeInterface::new(test_data, block_size));
1364        let block_server = BlockServer::new(block_size, interface.clone());
1365
1366        let (mapper_proxy, mapper_stream) =
1367            fidl::endpoints::create_proxy_and_stream::<fblock::MapperMarker>();
1368        let scope = fasync::Scope::new();
1369        scope.spawn(async move {
1370            let _ = block_server.handle_mapper_requests(mapper_stream).await;
1371        });
1372
1373        let (root_session_proxy, root_session_server) =
1374            fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
1375        let root_mapping_vmo = zx::Vmo::create(65536).unwrap();
1376
1377        let partition_key = 500u64;
1378        let extents =
1379            mapping::Extents::try_new([mapping::Extent::new(0..4096, Some(4096))], 0).unwrap();
1380        let partition_extents: Vec<u64> = mapping::Extents::encode_extents(&extents).collect();
1381        let mut payload_bytes = Vec::new();
1382        for w in &partition_extents {
1383            payload_bytes.extend_from_slice(&w.to_le_bytes());
1384        }
1385        let cmd = mapping::RawMappingCommand {
1386            opcode: mapping::MAPPINGS_COMMAND,
1387            offset: 0,
1388            key: partition_key,
1389            stored_size: 4096,
1390            device_offset: 0,
1391            metadata_count: 0,
1392            extent_count: partition_extents.len() as u32,
1393        };
1394        let mut sender = vmo_fifo::SyncSender::<mapping::RawMappingCommand>::new(
1395            root_mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1396            1024,
1397            256,
1398        )
1399        .unwrap();
1400        let mut payload_buf = sender.reserve_payload(payload_bytes.len()).unwrap();
1401        payload_buf.data().copy_from_slice(&payload_bytes);
1402        payload_buf.commit(cmd).unwrap();
1403
1404        let res = mapper_proxy
1405            .open_session(root_session_server, root_mapping_vmo, None, None)
1406            .await
1407            .unwrap();
1408        assert_matches!(res, Ok(()));
1409
1410        let child_mapping_vmo = zx::Vmo::create(65536).unwrap();
1411        let mut child_session = None;
1412        for _ in 0..100 {
1413            let (child_session_proxy, child_session_server) =
1414                fidl::endpoints::create_proxy::<fblock::MapperSessionMarker>();
1415            let child_res_raw = root_session_proxy
1416                .open_child_session(
1417                    child_session_server,
1418                    child_mapping_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap(),
1419                    partition_key,
1420                    None,
1421                    None,
1422                )
1423                .await
1424                .unwrap();
1425            if child_res_raw.is_ok() {
1426                child_session = Some(child_session_proxy);
1427                break;
1428            }
1429            fasync::Timer::new(std::time::Duration::from_millis(10)).await;
1430        }
1431        let child_session_proxy = child_session.expect("Failed to open child session");
1432
1433        root_session_proxy.close().await.unwrap().expect("Close parent session failed");
1434        child_session_proxy.on_closed().await.unwrap();
1435    }
1436}