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