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 anyhow::Error;
10use block_protocol::{BlockFifoRequest, BlockFifoResponse, ReadOptions, WriteFlags, WriteOptions};
11use fidl_fuchsia_storage_block as fblock;
12use fidl_fuchsia_storage_block::DeviceFlag;
13use fuchsia_async as fasync;
14use fuchsia_sync::Mutex;
15use futures::future::{Fuse, FusedFuture, join};
16use futures::stream::FuturesUnordered;
17use futures::{FutureExt, StreamExt, select_biased};
18use mapping::reader::{BlockService, MAX_READ_BUFFER_SIZE};
19use std::borrow::Cow;
20use std::collections::VecDeque;
21use std::future::{Future, poll_fn};
22use std::mem::MaybeUninit;
23use std::pin::pin;
24use std::sync::{Arc, OnceLock};
25use std::task::{Poll, ready};
26use storage_device::buffer::{Buffer, OwnedBuffer};
27use storage_device::buffer_allocator::{BufferAllocator, BufferSource};
28
29pub trait Interface: Send + Sync + Unpin + 'static {
30    /// Runs `stream` to completion.
31    ///
32    /// `offset_map` is provided by the client, and is used to remap requests. The extents in
33    /// `offset_map` are already validated to be within the range of the partition (as determined by
34    /// the [`PartitionInfo::block_count`] field). The implementation is expected to apply these
35    /// mappings to all FIFO requests, and to ensure all FIFO requests fit within the logical
36    /// extents of the offset map. Note that the default implementation does this for you, and that
37    /// is correct for most implementations.
38    ///
39    /// Implementors can override this method if they want to create a passthrough session instead
40    /// (and can use [`PassthroughSession`] below to do so). Generally, a passthrough session would
41    /// open a session to its underlying device with an offset map applied
42    /// (`fuchsia.storage.block.Block/OpenSessionWithOptions`), which remaps and restricts
43    /// requests to the range the partition should have access to.
44    ///
45    /// Nested mappings (i.e. the case when `offset_map` is non-empty, but the implementation
46    /// creates a passthrough session with its own mapping) would need to be composed by the
47    /// implementation. At this time, no implementations of passthrough sessions support nested
48    /// mappings, but we can add support as needed.
49    ///
50    /// If the implementor uses a [`PassthroughSession`], the following Interface methods
51    /// will not be called, and can be stubbed out:
52    ///   - on_attach_vmo
53    ///   - on_detach_vmo
54    ///   - read
55    ///   - write
56    ///   - flush
57    ///   - trim
58    fn open_session(
59        &self,
60        session_manager: Arc<SessionManager<Self>>,
61        stream: fblock::SessionRequestStream,
62        offset_map: OffsetMap,
63        block_size: u32,
64    ) -> impl Future<Output = Result<(), Error>> + Send {
65        // By default, serve the session rather than forwarding it.
66        session_manager.serve_session(
67            stream,
68            offset_map,
69            self.get_info().max_transfer_blocks(),
70            block_size,
71        )
72    }
73
74    /// Called whenever a VMO is attached, prior to the VMO's usage in any other methods. Whilst
75    /// the VMO is attached, `vmo` will keep the same address so it is safe to use the pointer
76    /// value (as, say, a key into a HashMap).
77    fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> impl Future<Output = Result<(), zx::Status>> + Send {
78        async { Ok(()) }
79    }
80
81    /// Called whenever a VMO is detached.
82    fn on_detach_vmo(&self, _vmo: &zx::Vmo) {}
83
84    /// Called to get block/partition information.
85    fn get_info(&self) -> Cow<'_, DeviceInfo>;
86
87    /// Called for a request to read bytes.
88    ///
89    /// Implementations are responsible for checking that the request block range
90    /// (`[device_block_offset, device_block_offset + block_count)`) falls within valid
91    /// device/partition bounds, and returning `Err(zx::Status::OUT_OF_RANGE)` if it is out of
92    /// bounds.
93    fn read(
94        &self,
95        device_block_offset: u64,
96        block_count: u32,
97        vmo: &Arc<zx::Vmo>,
98        vmo_offset: u64, // *bytes* not blocks
99        opts: ReadOptions,
100        trace_flow_id: TraceFlowId,
101    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
102
103    /// Called for a request to write bytes.
104    ///
105    /// Implementations are responsible for checking that the request block range
106    /// (`[device_block_offset, device_block_offset + block_count)`) falls within valid
107    /// device/partition bounds, and returning `Err(zx::Status::OUT_OF_RANGE)` if it is out of
108    /// bounds.
109    fn write(
110        &self,
111        device_block_offset: u64,
112        block_count: u32,
113        vmo: &Arc<zx::Vmo>,
114        vmo_offset: u64, // *bytes* not blocks
115        opts: WriteOptions,
116        trace_flow_id: TraceFlowId,
117    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
118
119    /// Called to flush the device.
120    fn flush(
121        &self,
122        trace_flow_id: TraceFlowId,
123    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
124
125    /// Called to trim a region.
126    ///
127    /// Implementations are responsible for checking that the request block range
128    /// (`[device_block_offset, device_block_offset + block_count)`) falls within valid
129    /// device/partition bounds, and returning `Err(zx::Status::OUT_OF_RANGE)` if it is out of
130    /// bounds.
131    fn trim(
132        &self,
133        device_block_offset: u64,
134        block_count: u32,
135        trace_flow_id: TraceFlowId,
136    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
137
138    /// Called to handle the GetVolumeInfo FIDL call.
139    fn get_volume_info(
140        &self,
141    ) -> impl Future<Output = Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status>> + Send
142    {
143        async { Err(zx::Status::NOT_SUPPORTED) }
144    }
145
146    /// Called to handle the QuerySlices FIDL call.
147    fn query_slices(
148        &self,
149        _start_slices: &[u64],
150    ) -> impl Future<Output = Result<Vec<fblock::VsliceRange>, zx::Status>> + Send {
151        async { Err(zx::Status::NOT_SUPPORTED) }
152    }
153
154    /// Called to handle the Extend FIDL call.
155    fn extend(
156        &self,
157        _start_slice: u64,
158        _slice_count: u64,
159    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
160        async { Err(zx::Status::NOT_SUPPORTED) }
161    }
162
163    /// Called to handle the Shrink FIDL call.
164    fn shrink(
165        &self,
166        _start_slice: u64,
167        _slice_count: u64,
168    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
169        async { Err(zx::Status::NOT_SUPPORTED) }
170    }
171}
172
173/// A helper object to run a passthrough (proxy) session.
174pub struct PassthroughSession(fblock::SessionProxy);
175
176impl PassthroughSession {
177    pub fn new(proxy: fblock::SessionProxy) -> Self {
178        Self(proxy)
179    }
180
181    async fn handle_request(&self, request: fblock::SessionRequest) -> Result<(), Error> {
182        match request {
183            fblock::SessionRequest::GetFifo { responder } => {
184                responder.send(self.0.get_fifo().await?)?;
185            }
186            fblock::SessionRequest::AttachVmo { vmo, responder } => {
187                responder.send(self.0.attach_vmo(vmo).await?.as_ref().map_err(|s| *s))?;
188            }
189            fblock::SessionRequest::Close { responder } => {
190                responder.send(self.0.close().await?)?;
191            }
192        }
193        Ok(())
194    }
195
196    /// Runs `stream` until completion.
197    pub async fn serve(&self, mut stream: fblock::SessionRequestStream) -> Result<(), Error> {
198        while let Some(Ok(request)) = stream.next().await {
199            if let Err(error) = self.handle_request(request).await {
200                log::warn!(error:?; "FIDL error");
201            }
202        }
203        Ok(())
204    }
205}
206
207pub struct SessionManager<I: Interface + ?Sized> {
208    interface: Arc<I>,
209    active_requests: ActiveRequests<usize>,
210
211    // NOTE: This must be dropped *after* `active_requests` because we store `Buffer<'_>` with an
212    // erased ('static) lifetime in `ActiveRequest`.
213    buffer_allocator: OnceLock<BufferAllocator>,
214}
215
216impl<I: Interface + ?Sized> Drop for SessionManager<I> {
217    fn drop(&mut self) {
218        if let Some(allocator) = self.buffer_allocator.get() {
219            self.interface.on_detach_vmo(allocator.buffer_source().vmo());
220        }
221    }
222}
223
224impl<I: Interface + ?Sized> SessionManager<I> {
225    pub fn new(interface: Arc<I>) -> Self {
226        Self {
227            interface,
228            active_requests: ActiveRequests::default(),
229            buffer_allocator: OnceLock::new(),
230        }
231    }
232
233    pub fn interface(&self) -> &I {
234        self.interface.as_ref()
235    }
236
237    /// Runs `stream` until completion.
238    pub async fn serve_session(
239        self: Arc<Self>,
240        stream: fblock::SessionRequestStream,
241        offset_map: OffsetMap,
242        max_transfer_blocks: Option<std::num::NonZero<u32>>,
243        block_size: u32,
244    ) -> Result<(), Error> {
245        let (helper, fifo) =
246            SessionHelper::new(self.clone(), offset_map, max_transfer_blocks, block_size)?;
247        let session = Arc::new(Session {
248            helper: Arc::new(helper),
249            interface: self.interface.clone(),
250            close_callback: Mutex::new(None),
251        });
252
253        let (stop_sender, stop_receiver) = futures::channel::oneshot::channel();
254
255        let mut stream = stream.fuse();
256        let scope = fasync::Scope::new();
257        let session_clone = session.clone();
258        let mut fifo_task = scope
259            .spawn(async move {
260                if let Err(status) = session_clone.run_fifo(fifo, stop_receiver).await {
261                    if status != zx::Status::PEER_CLOSED {
262                        log::error!(status:?; "FIFO error");
263                    }
264                }
265            })
266            .fuse();
267
268        // Make sure we detach VMOs when we go out of scope.
269        scopeguard::defer! {
270            for (_, registered_vmo) in session.helper.take_vmos() {
271                self.interface.on_detach_vmo(&registered_vmo.vmo);
272            }
273        }
274
275        let mut closing = false;
276        let mut stop_sender = Some(stop_sender);
277
278        loop {
279            futures::select! {
280                maybe_req = if closing {
281                    futures::future::pending().left_future()
282                } else {
283                    stream.next().right_future()
284                } => {
285                    if let Some(req) = maybe_req {
286                        match session.helper.handle_request(req?).await? {
287                            HandleRequestResult::Ok => {},
288                            HandleRequestResult::Closed(callback) => {
289                                *session.close_callback.lock() = Some(callback);
290                                // Client explicitly closed stream, stop processing.
291                                if let Some(sender) = stop_sender.take() {
292                                    let _ = sender.send(());
293                                }
294                                closing = true;
295                            }
296                        }
297                    } else {
298                        // Client end of stream dropped, stop processing.
299                        if let Some(sender) = stop_sender.take() {
300                            let _ = sender.send(());
301                        }
302                        closing = true;
303                    }
304                }
305                _ = fifo_task => break,
306            }
307        }
308        Ok(())
309    }
310}
311
312pub struct Session<I: Interface + ?Sized> {
313    interface: Arc<I>,
314    helper: Arc<SessionHelper<SessionManager<I>>>,
315    close_callback: Mutex<Option<Box<dyn FnOnce() + Send + 'static>>>,
316}
317
318impl<I: Interface + ?Sized> Session<I> {
319    // A task loop for receiving and responding to FIFO requests.
320    async fn run_fifo(
321        &self,
322        fifo: zx::Fifo<BlockFifoRequest, BlockFifoResponse>,
323        stop_signal: futures::channel::oneshot::Receiver<()>,
324    ) -> Result<(), zx::Status> {
325        scopeguard::defer! {
326            // Ensure that we always clean up active requests for this session upon FIFO
327            // termination.
328            self.helper.drop_active_requests(|session| *session == self as *const _ as usize);
329        }
330
331        // The FIFO has to be processed by a single task due to implementation constraints on
332        // fuchsia_async::Fifo.  Thus, we use an event loop to drive the FIFO.  FIFO reads and
333        // writes can happen in batch, and request processing is parallel.
334        //
335        // The general flow is:
336        //  - Read messages from the FIFO, write into `requests`.
337        //  - Read `requests`, decode them, and spawn a task to process them in `active_requests`,
338        //    which will eventually write them into `responses`.
339        //  - Read `responses` and write out to the FIFO.
340        let mut fifo = fasync::Fifo::from_fifo(fifo);
341        let (mut reader, mut writer) = fifo.async_io();
342        let mut requests = [MaybeUninit::<BlockFifoRequest>::uninit(); FIFO_MAX_REQUESTS];
343        let active_requests = &self.helper.session_manager().active_requests;
344        let mut active_request_futures = FuturesUnordered::new();
345        let mut responses = Vec::new();
346
347        // We map requests using a single future `map_future`.  `pending_mappings` is used to queue
348        // up requests that need to be mapped.  This will serialise how mappings occur which might
349        // make updating mapping caches simpler.  If this proves to be a performance issue, we can
350        // optimise it.
351        let mut map_future = pin!(Fuse::terminated());
352        let mut pending_mappings: VecDeque<DecodedRequest> = VecDeque::new();
353
354        // When `stop_signal` is received, we stop reading from the FIFO and wait for in-flight
355        // tasks to complete.
356        let mut stop_signal = pin!(stop_signal.fuse());
357        let mut is_closed = false;
358
359        loop {
360            let new_requests = {
361                // We provide some flow control by limiting how many in-flight requests we will
362                // allow.
363                let pending_requests = active_request_futures.len() + responses.len();
364
365                if is_closed
366                    && pending_requests == 0
367                    && map_future.is_terminated()
368                    && pending_mappings.is_empty()
369                {
370                    return Ok(());
371                }
372
373                let count = requests.len().saturating_sub(pending_requests);
374                let mut receive_requests = pin!(if count == 0 || is_closed {
375                    Fuse::terminated()
376                } else {
377                    reader.read_entries(&mut requests[..count]).fuse()
378                });
379                let mut send_responses = pin!(if responses.is_empty() {
380                    Fuse::terminated()
381                } else {
382                    poll_fn(|cx| -> Poll<Result<(), zx::Status>> {
383                        match ready!(writer.try_write(cx, &responses[..])) {
384                            Ok(written) => {
385                                responses.drain(..written.get());
386                                Poll::Ready(Ok(()))
387                            }
388                            Err(status) => Poll::Ready(Err(status)),
389                        }
390                    })
391                    .fuse()
392                });
393
394                // Order is important here.  We want to prioritize sending results on the FIFO and
395                // processing FIFO messages over receiving new ones, to provide flow control.
396                select_biased!(
397                    res = send_responses => {
398                        res?;
399                        0
400                    },
401                    response = active_request_futures.select_next_some() => {
402                        responses.extend(response);
403                        0
404                    }
405                    result = map_future => {
406                        match result {
407                            Ok((request, remainder, commit_decompression_buffers)) => {
408                                active_request_futures.push(self.process_fifo_request(
409                                    request,
410                                    commit_decompression_buffers,
411                                ));
412                                if let Some(remainder) = remainder {
413                                    map_future.set(
414                                        self.map_request_or_get_response(remainder).fuse()
415                                    );
416                                }
417                            }
418                            Err(response) => responses.extend(response),
419                        }
420                        if map_future.is_terminated() {
421                            if let Some(request) = pending_mappings.pop_front() {
422                                map_future.set(self.map_request_or_get_response(request).fuse());
423                            }
424                        }
425                        0
426                    }
427                    _ = stop_signal => {
428                        is_closed = true;
429                        0
430                    }
431                    count = receive_requests => {
432                        count?.get()
433                    }
434                )
435            };
436
437            // NB: It is very important that there are no `await`s for the rest of the loop body, as
438            // otherwise active requests might become stalled.
439            for request in &mut requests[..new_requests] {
440                match self.helper.decode_fifo_request(self as *const _ as usize, unsafe {
441                    request.assume_init_mut()
442                }) {
443                    Ok(DecodedRequest {
444                        operation: Operation::CloseVmo, vmo, request_id, ..
445                    }) => {
446                        if let Some(vmo) = vmo {
447                            self.interface.on_detach_vmo(vmo.as_ref());
448                        }
449                        responses.extend(
450                            active_requests
451                                .complete_and_take_response(request_id, zx::Status::OK)
452                                .map(|(_, response)| response),
453                        );
454                    }
455                    Ok(request) => {
456                        if map_future.is_terminated() {
457                            map_future.set(self.map_request_or_get_response(request).fuse());
458                        } else {
459                            pending_mappings.push_back(request);
460                        }
461                    }
462                    Err(None) => {}
463                    Err(Some(response)) => responses.push(response),
464                }
465            }
466        }
467    }
468
469    async fn map_request_or_get_response(
470        &self,
471        request: DecodedRequest,
472    ) -> Result<(DecodedRequest, Option<DecodedRequest>, bool), Option<BlockFifoResponse>> {
473        let request_id = request.request_id;
474        self.map_request(request).await.map_err(|status| {
475            self.helper
476                .orchestrator
477                .active_requests
478                .complete_and_take_response(request_id, status)
479                .map(|(_, r)| r)
480        })
481    }
482
483    // NOTE: The implementation of this currently assumes that we are only processing a single map
484    // request at a time.
485    async fn map_request(
486        &self,
487        mut request: DecodedRequest,
488    ) -> Result<(DecodedRequest, Option<DecodedRequest>, bool), zx::Status> {
489        let mut active_requests;
490        let active_request;
491        let mut commit_decompression_buffers = false;
492        let flags = self.interface.get_info().as_ref().device_flags();
493        // Strip the PRE_BARRIER flag if we don't support it, and simulate the barrier with a
494        // pre-flush.
495        if !flags.contains(DeviceFlag::BARRIER_SUPPORT)
496            && request.operation.take_write_flag(WriteFlags::PRE_BARRIER)
497        {
498            if let Some(id) = request.trace_flow_id {
499                fuchsia_trace::async_instant!(
500                    fuchsia_trace::Id::from(id.get()),
501                    "storage",
502                    "block_server::SimulatedBarrier",
503                    "request_id" => request.request_id.0
504                );
505            }
506            self.interface.flush(request.trace_flow_id).await?;
507        }
508
509        // Handle decompressed read operations by turning them into regular read operations.
510        match request.operation {
511            Operation::StartDecompressedRead {
512                required_buffer_size,
513                device_block_offset,
514                block_count,
515                options,
516            } => {
517                let allocator = match self.helper.session_manager().buffer_allocator.get() {
518                    Some(a) => a,
519                    None => {
520                        // This isn't racy because there should only be one `map_request` future
521                        // running at any one time.
522                        let source = BufferSource::new(fblock::MAX_DECOMPRESSED_BYTES as usize);
523                        self.interface.on_attach_vmo(&source.vmo()).await?;
524                        let allocator = BufferAllocator::new(
525                            std::cmp::max(
526                                self.helper.block_size as usize,
527                                zx::system_get_page_size() as usize,
528                            ),
529                            source,
530                        );
531                        self.helper.session_manager().buffer_allocator.set(allocator).unwrap();
532                        self.helper.session_manager().buffer_allocator.get().unwrap()
533                    }
534                };
535
536                if required_buffer_size > fblock::MAX_DECOMPRESSED_BYTES as usize {
537                    return Err(zx::Status::OUT_OF_RANGE);
538                }
539
540                let buffer = allocator.allocate_buffer(required_buffer_size).await;
541                let vmo_offset = buffer.range().start as u64;
542
543                // # Safety
544                //
545                // See below.
546                unsafe fn remove_lifetime(buffer: Buffer<'_>) -> Buffer<'static> {
547                    unsafe { std::mem::transmute(buffer) }
548                }
549
550                active_requests = self.helper.session_manager().active_requests.0.lock();
551                active_request = &mut active_requests.requests[request.request_id.0];
552
553                // SAFETY: We guarantee that `buffer_allocator` is dropped after `active_requests`,
554                // so this should be safe.
555                active_request.decompression_info.as_mut().unwrap().buffer =
556                    Some(unsafe { remove_lifetime(buffer) });
557
558                request.operation = Operation::Read {
559                    device_block_offset,
560                    block_count,
561                    _unused: 0,
562                    vmo_offset,
563                    options,
564                };
565                request.vmo = Some(allocator.buffer_source().vmo().clone());
566
567                commit_decompression_buffers = true;
568            }
569            Operation::ContinueDecompressedRead {
570                offset,
571                device_block_offset,
572                block_count,
573                options,
574            } => {
575                active_requests = self.helper.session_manager().active_requests.0.lock();
576                active_request = &mut active_requests.requests[request.request_id.0];
577
578                let buffer =
579                    active_request.decompression_info.as_ref().unwrap().buffer.as_ref().unwrap();
580
581                // Make sure this read won't overflow our buffer.
582                if offset >= buffer.len() as u64
583                    || buffer.len() as u64 - offset
584                        < block_count as u64 * self.helper.block_size as u64
585                {
586                    return Err(zx::Status::OUT_OF_RANGE);
587                }
588
589                request.operation = Operation::Read {
590                    device_block_offset,
591                    block_count,
592                    _unused: 0,
593                    vmo_offset: buffer.range().start as u64 + offset,
594                    options,
595                };
596
597                let allocator = self.helper.session_manager().buffer_allocator.get().unwrap();
598                request.vmo = Some(allocator.buffer_source().vmo().clone());
599            }
600            _ => {
601                active_requests = self.helper.session_manager().active_requests.0.lock();
602                active_request = &mut active_requests.requests[request.request_id.0];
603            }
604        }
605
606        // NB: We propagate the FORCE_ACCESS flag to *both* request and remainder, even if we're
607        // using simulated FUA.  However, in `process_fifo_request`, we'll only do the post-flush
608        // once the last request completes.
609        self.helper
610            .map_request(request, active_request)
611            .map(|(request, remainder)| (request, remainder, commit_decompression_buffers))
612    }
613
614    /// Processes a fifo request.
615    async fn process_fifo_request(
616        &self,
617        DecodedRequest { request_id, operation, vmo, trace_flow_id }: DecodedRequest,
618        commit_decompression_buffers: bool,
619    ) -> Option<BlockFifoResponse> {
620        let mut needs_postflush = false;
621        let result = match operation {
622            Operation::Read { device_block_offset, block_count, _unused, vmo_offset, options } => {
623                join(
624                    self.interface.read(
625                        device_block_offset,
626                        block_count,
627                        vmo.as_ref().unwrap(),
628                        vmo_offset,
629                        options,
630                        trace_flow_id,
631                    ),
632                    async {
633                        if commit_decompression_buffers {
634                            let (target_slice, buffer_slice, buffer_range) = {
635                                let active_request = self
636                                    .helper
637                                    .session_manager()
638                                    .active_requests
639                                    .request(request_id);
640                                let info = active_request.decompression_info.as_ref().unwrap();
641                                (
642                                    info.uncompressed_slice(),
643                                    self.helper
644                                        .orchestrator
645                                        .buffer_allocator
646                                        .get()
647                                        .unwrap()
648                                        .buffer_source()
649                                        .slice(),
650                                    info.buffer.as_ref().unwrap().range(),
651                                )
652                            };
653                            let vmar = fuchsia_runtime::vmar_root_self();
654                            // The target slice might not be page aligned.
655                            let addr = target_slice.addr();
656                            let unaligned = addr % zx::system_get_page_size() as usize;
657                            if let Err(error) = vmar.op_range(
658                                zx::VmarOp::COMMIT,
659                                addr - unaligned,
660                                target_slice.len() + unaligned,
661                            ) {
662                                log::warn!(error:?; "Unable to commit target range");
663                            }
664                            // But the buffer range should be.
665                            if let Err(error) = vmar.op_range(
666                                zx::VmarOp::PREFETCH,
667                                buffer_slice.addr() + buffer_range.start,
668                                buffer_range.len(),
669                            ) {
670                                log::warn!(
671                                    error:?,
672                                    buffer_range:?;
673                                    "Unable to prefetch source range",
674                                );
675                            }
676                        }
677                    },
678                )
679                .await
680                .0
681            }
682            Operation::Write {
683                device_block_offset,
684                block_count,
685                _unused,
686                vmo_offset,
687                mut options,
688            } => {
689                // Strip the FORCE_ACCESS flag if we don't support it, and simulate the FUA with a
690                // post-flush.
691                if options.flags.contains(WriteFlags::FORCE_ACCESS) {
692                    let flags = self.interface.get_info().as_ref().device_flags();
693                    if !flags.contains(DeviceFlag::FUA_SUPPORT) {
694                        options.flags.remove(WriteFlags::FORCE_ACCESS);
695                        needs_postflush = true;
696                    }
697                }
698                self.interface
699                    .write(
700                        device_block_offset,
701                        block_count,
702                        vmo.as_ref().unwrap(),
703                        vmo_offset,
704                        options,
705                        trace_flow_id,
706                    )
707                    .await
708            }
709            Operation::Flush => self.interface.flush(trace_flow_id).await,
710            Operation::Trim { device_block_offset, block_count } => {
711                self.interface.trim(device_block_offset, block_count, trace_flow_id).await
712            }
713            Operation::CloseVmo
714            | Operation::StartDecompressedRead { .. }
715            | Operation::ContinueDecompressedRead { .. } => {
716                // Handled in main request loop
717                unreachable!()
718            }
719        };
720        let response = self
721            .helper
722            .orchestrator
723            .active_requests
724            .complete_and_take_response(request_id, result.into())
725            .map(|(_, r)| r);
726        if let Some(mut response) = response {
727            // Only do the post-flush on the very last request, and only if successful.
728            if zx::Status::from_raw(response.status) == zx::Status::OK && needs_postflush {
729                if let Some(id) = trace_flow_id {
730                    fuchsia_trace::async_instant!(
731                        fuchsia_trace::Id::from(id.get()),
732                        "storage",
733                        "block_server::SimulatedFUA",
734                        "request_id" => request_id.0
735                    );
736                }
737                response.status =
738                    zx::Status::from(self.interface.flush(trace_flow_id).await).into_raw();
739            }
740            Some(response)
741        } else {
742            response
743        }
744    }
745}
746
747impl<I: Interface + ?Sized> super::SessionManager for SessionManager<I> {
748    type Orchestrator = Self;
749
750    const SUPPORTS_DECOMPRESSION: bool = true;
751
752    // We don't need the session, we just need something unique to identify the session.
753    type Session = usize;
754
755    fn session_eq(a: &usize, b: &usize) -> bool {
756        a == b
757    }
758
759    async fn on_attach_vmo(orchestrator: Arc<Self>, vmo: &Arc<zx::Vmo>) -> Result<(), zx::Status> {
760        I::on_attach_vmo(&orchestrator.interface, vmo).await
761    }
762
763    async fn open_session(
764        orchestrator: Arc<Self>,
765        stream: fblock::SessionRequestStream,
766        offset_map: OffsetMap,
767        block_size: u32,
768    ) -> Result<(), Error> {
769        I::open_session(
770            &orchestrator.interface,
771            orchestrator.clone(),
772            stream,
773            offset_map,
774            block_size,
775        )
776        .await
777    }
778
779    fn get_info(&self) -> Cow<'_, DeviceInfo> {
780        self.interface.get_info()
781    }
782
783    async fn get_volume_info(
784        &self,
785    ) -> Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status> {
786        self.interface.get_volume_info().await
787    }
788
789    async fn query_slices(
790        &self,
791        start_slices: &[u64],
792    ) -> Result<Vec<fblock::VsliceRange>, zx::Status> {
793        self.interface.query_slices(start_slices).await
794    }
795
796    async fn extend(&self, start_slice: u64, slice_count: u64) -> Result<(), zx::Status> {
797        self.interface.extend(start_slice, slice_count).await
798    }
799
800    async fn shrink(&self, start_slice: u64, slice_count: u64) -> Result<(), zx::Status> {
801        self.interface.shrink(start_slice, slice_count).await
802    }
803
804    fn active_requests(&self) -> &ActiveRequests<Self::Session> {
805        return &self.active_requests;
806    }
807}
808
809impl<I: Interface + ?Sized> Drop for Session<I> {
810    fn drop(&mut self) {
811        let callback = std::mem::take(&mut *self.close_callback.lock());
812        if let Some(callback) = callback {
813            callback();
814        }
815    }
816}
817
818impl<I: Interface> IntoOrchestrator for Arc<I> {
819    type SM = SessionManager<I>;
820
821    fn into_orchestrator(self) -> Arc<Self::SM> {
822        Arc::new(SessionManager {
823            interface: self,
824            active_requests: ActiveRequests::default(),
825            buffer_allocator: OnceLock::new(),
826        })
827    }
828}
829
830/// A generic adapter that presents any [`async_interface::Interface`] backend as a
831/// [`BlockService`], using [`BufferAllocator`] for concurrent read buffer management and
832/// [`fasync::ScopeHandle`] for thread-safe task spawning.
833pub struct AsyncBlockService<I: Interface> {
834    interface: Arc<I>,
835    allocator: Arc<BufferAllocator>,
836    block_size: u32,
837    scope: fasync::ScopeHandle,
838}
839
840impl<I: Interface> AsyncBlockService<I> {
841    pub async fn new(
842        interface: Arc<I>,
843        block_size: u32,
844        pool_capacity: usize,
845        scope: fasync::ScopeHandle,
846    ) -> Result<Self, zx::Status> {
847        let source = BufferSource::new(pool_capacity);
848        interface.on_attach_vmo(&source.vmo()).await?;
849        let allocator = Arc::new(BufferAllocator::new(
850            std::cmp::max(block_size as usize, zx::system_get_page_size() as usize),
851            source,
852        ));
853        Ok(Self { interface, allocator, block_size, scope })
854    }
855
856    pub fn new_with_allocator(
857        interface: Arc<I>,
858        block_size: u32,
859        allocator: Arc<BufferAllocator>,
860        scope: fasync::ScopeHandle,
861    ) -> Self {
862        Self { interface, allocator, block_size, scope }
863    }
864
865    pub fn interface(&self) -> &Arc<I> {
866        &self.interface
867    }
868
869    pub fn allocator(&self) -> &Arc<BufferAllocator> {
870        &self.allocator
871    }
872}
873
874impl<I: Interface> BlockService for AsyncBlockService<I> {
875    fn allocate_buffer(&self, max_len: usize) -> OwnedBuffer {
876        let max_len = std::cmp::min(
877            std::cmp::min(max_len, MAX_READ_BUFFER_SIZE),
878            self.allocator.buffer_source().size(),
879        );
880        self.allocator.allocate_buffer_sync_owned(max_len)
881    }
882
883    fn read_blocks(
884        &self,
885        device_offset: u64,
886        dest_buffer: OwnedBuffer,
887        on_complete: Box<dyn FnOnce(Result<OwnedBuffer, Error>) + Send>,
888    ) -> Result<(), Error> {
889        let block_size = self.block_size as u64;
890        let device_block_offset = device_offset / block_size;
891        let block_count = (dest_buffer.len() as u64 / block_size) as u32;
892
893        let vmo_offset = dest_buffer.range().start as u64;
894        let vmo = self.allocator.buffer_source().vmo().clone();
895        let interface = self.interface.clone();
896
897        self.scope.spawn(async move {
898            let res = interface
899                .read(
900                    device_block_offset,
901                    block_count,
902                    &vmo,
903                    vmo_offset,
904                    ReadOptions::default(),
905                    TraceFlowId::default(),
906                )
907                .await;
908            match res {
909                Ok(()) => on_complete(Ok(dest_buffer)),
910                Err(status) => on_complete(Err(anyhow::anyhow!("Read failed: {:?}", status))),
911            }
912        });
913
914        Ok(())
915    }
916}
917
918#[cfg(test)]
919mod tests {
920    use super::*;
921    use fuchsia_async as fasync;
922    use mapping::Extents;
923    use mapping::reader::read_aligned_range;
924    use std::sync::atomic::{AtomicU64, Ordering};
925
926    struct FakeInterface {
927        data: Vec<u8>,
928        block_size: u32,
929        read_count: AtomicU64,
930    }
931
932    impl FakeInterface {
933        fn new(data: Vec<u8>, block_size: u32) -> Self {
934            Self { data, block_size, read_count: AtomicU64::new(0) }
935        }
936    }
937
938    impl Interface for FakeInterface {
939        fn get_info(&self) -> Cow<'_, DeviceInfo> {
940            Cow::Owned(DeviceInfo::Block(crate::BlockInfo {
941                block_count: (self.data.len() / self.block_size as usize) as u64,
942                max_transfer_blocks: None,
943                device_flags: fblock::DeviceFlag::empty(),
944            }))
945        }
946
947        async fn read(
948            &self,
949            device_block_offset: u64,
950            block_count: u32,
951            vmo: &Arc<zx::Vmo>,
952            vmo_offset: u64,
953            _opts: ReadOptions,
954            _trace_flow_id: TraceFlowId,
955        ) -> Result<(), zx::Status> {
956            self.read_count.fetch_add(1, Ordering::Relaxed);
957            let byte_offset = (device_block_offset * self.block_size as u64) as usize;
958            let byte_len = (block_count * self.block_size) as usize;
959            let slice = &self.data[byte_offset..byte_offset + byte_len];
960            vmo.write(slice, vmo_offset).map_err(|_| zx::Status::IO)?;
961            Ok(())
962        }
963
964        async fn write(
965            &self,
966            _device_block_offset: u64,
967            _block_count: u32,
968            _vmo: &Arc<zx::Vmo>,
969            _vmo_offset: u64,
970            _opts: WriteOptions,
971            _trace_flow_id: TraceFlowId,
972        ) -> Result<(), zx::Status> {
973            Ok(())
974        }
975
976        async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
977            Ok(())
978        }
979
980        async fn trim(
981            &self,
982            _device_block_offset: u64,
983            _block_count: u32,
984            _trace_flow_id: TraceFlowId,
985        ) -> Result<(), zx::Status> {
986            Ok(())
987        }
988    }
989
990    #[fasync::run_singlethreaded(test)]
991    async fn test_async_block_service_read_aligned_range() {
992        let block_size = 512;
993        let test_data: Vec<u8> = (0..8192).map(|i| (i % 255) as u8).collect();
994        let interface = Arc::new(FakeInterface::new(test_data.clone(), block_size));
995        let scope = fasync::Scope::current();
996
997        let block_service = Arc::new(
998            AsyncBlockService::new(interface, block_size, 16384, scope)
999                .await
1000                .expect("Failed to create AsyncBlockService"),
1001        );
1002
1003        let encoded = (1u64 << 32) | 0u64;
1004        let extents = Extents::from_encoded(&[encoded]).unwrap();
1005        let service = block_service.clone();
1006        let (send, recv) = futures::channel::oneshot::channel();
1007        let send = std::sync::Mutex::new(Some(send));
1008        let read_buf = std::sync::Mutex::new(Vec::new());
1009
1010        read_aligned_range(&extents, 0..4096, &*service, move |buffer_result| {
1011            let buffer = buffer_result.unwrap();
1012            let mut read_guard = read_buf.lock().unwrap();
1013            buffer.as_ptr_slice().append_to(&mut read_guard);
1014            if read_guard.len() == 4096 {
1015                if let Some(s) = send.lock().unwrap().take() {
1016                    let _ = s.send(read_guard.clone());
1017                }
1018            }
1019            std::ops::ControlFlow::Continue(())
1020        });
1021
1022        let result = recv.await.unwrap();
1023        assert_eq!(result, &test_data[0..4096]);
1024    }
1025
1026    #[fasync::run_singlethreaded(test)]
1027    async fn test_async_block_service_concurrent_reads_larger_than_pool_capacity() {
1028        let block_size = 512;
1029        let total_size = 65536; // 16 * 4096 bytes
1030        let test_data: Vec<u8> = (0..total_size).map(|i| (i % 251) as u8).collect();
1031        let interface = Arc::new(FakeInterface::new(test_data.clone(), block_size));
1032        let scope = fasync::Scope::current();
1033
1034        // Pool capacity is 8192 bytes (2 * 4096 bytes), while total concurrent reads
1035        // request 65536 bytes.
1036        let pool_capacity = 8192;
1037        let source = BufferSource::new(pool_capacity);
1038        interface.on_attach_vmo(&source.vmo()).await.unwrap();
1039        let allocator = Arc::new(BufferAllocator::new(
1040            std::cmp::max(block_size as usize, zx::system_get_page_size() as usize),
1041            source,
1042        ));
1043        let block_service = Arc::new(AsyncBlockService::new_with_allocator(
1044            interface, block_size, allocator, scope,
1045        ));
1046
1047        let encoded = (16u64 << 32) | 0u64;
1048        let extents = Arc::new(Extents::from_encoded(&[encoded]).unwrap());
1049
1050        // Spawn 4 concurrent read requests on background threads, each requesting 16384 bytes.
1051        let ranges = [0..16384, 16384..32768, 32768..49152, 49152..65536];
1052        let mut receivers = Vec::new();
1053
1054        for range in ranges {
1055            let (send, recv) = futures::channel::oneshot::channel();
1056            let service = block_service.clone();
1057            let extents = extents.clone();
1058
1059            std::thread::spawn(move || {
1060                let send = std::sync::Mutex::new(Some(send));
1061                let read_buf = std::sync::Mutex::new(Vec::new());
1062                let target_len = (range.end - range.start) as usize;
1063
1064                read_aligned_range(&extents, range, &*service, move |buffer_result| {
1065                    let buffer = buffer_result.unwrap();
1066                    let mut read_guard = read_buf.lock().unwrap();
1067                    buffer.as_ptr_slice().append_to(&mut read_guard);
1068                    if read_guard.len() == target_len {
1069                        if let Some(s) = send.lock().unwrap().take() {
1070                            let _ = s.send(read_guard.clone());
1071                        }
1072                    }
1073                    std::ops::ControlFlow::Continue(())
1074                });
1075            });
1076
1077            receivers.push(recv);
1078        }
1079
1080        let results = futures::future::join_all(receivers).await;
1081        for (i, res) in results.into_iter().enumerate() {
1082            let data = res.unwrap();
1083            let start = i * 16384;
1084            let end = start + 16384;
1085            assert_eq!(data, &test_data[start..end]);
1086        }
1087    }
1088}