Skip to main content

block_server/
callback_interface.rs

1// Copyright 2026 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 crate::verifier::Verifier;
6use crate::{
7    ActiveRequests, DecodedRequest, DeviceInfo, HandleRequestResult, IntoOrchestrator, KeyRegistry,
8    OffsetMap, Operation, RequestId, SessionHelper, TraceFlowId, WriteFlags,
9};
10use anyhow::Error;
11use block_protocol::{BlockFifoRequest, BlockFifoResponse};
12use fidl_fuchsia_storage_block as fblock;
13use fuchsia_sync::{Condvar, Mutex};
14use futures::TryStreamExt as _;
15use futures::stream::{AbortHandle, Abortable};
16use mapping::reader::BlockService;
17use std::borrow::{Borrow, Cow};
18use std::collections::{HashMap, VecDeque};
19use std::mem::MaybeUninit;
20use std::sync::{Arc, OnceLock, Weak};
21
22pub mod block_service;
23pub use block_service::DefaultCallbackBlockService;
24
25/// An in-flight request.
26#[derive(Clone, Debug)]
27pub struct Request {
28    /// The ID that a request is associated with, for later completion in
29    /// [`SessionManager::complete_request`].  Note that this is not necessarily a unique
30    /// identifier, and multiple Requests may have the same request_id (e.g. due to request
31    /// splitting).  The library internally reference-counts requests which use this ID.
32    pub request_id: RequestId,
33    pub operation: Operation,
34    pub trace_flow_id: TraceFlowId,
35    /// `vmo` is always Some for Operation::Read or Operation::Write, and None otherwise.
36    pub vmo: Option<Arc<zx::Vmo>>,
37}
38
39pub trait Interface: Send + Sync + Unpin + 'static {
40    type Orchestrator: Borrow<SessionManager<Self>> + Send + Sync;
41
42    /// Called to get block/partition information.
43    fn get_info(&self) -> Cow<'_, DeviceInfo>;
44
45    /// Start running a new session.  The interface must ensure that [`Session::run`] is called
46    /// on a different thread.
47    fn spawn_session(&self, session: Arc<Session<Self>>);
48
49    /// Starts a batch of requests.  The implementation may block if there are too many in-flight
50    /// requests, providing pushback.  The interface is responsible for eventually calling
51    /// [`SessionManager::complete_request`] for each request (even during server shutdown).
52    ///
53    /// Implementations are responsible for checking that request block ranges fall within valid
54    /// device/partition bounds, and completing with `zx::Status::OUT_OF_RANGE` if out of bounds.
55    fn on_requests(&self, requests: &[Request]);
56
57    /// Returns the BlockService implementation for this interface.
58    ///
59    /// By default, returns the fallback [`DefaultCallbackBlockService`].
60    fn into_block_service(
61        self: Arc<Self>,
62        orchestrator: &Arc<Self::Orchestrator>,
63    ) -> Arc<dyn BlockService> {
64        Arc::new(DefaultCallbackBlockService::<Self>::new(orchestrator))
65    }
66
67    /// Called when a new mapper session is opened.
68    /// Returns the [`Verifier`] for page delivery.
69    fn on_open_mapper_session(
70        &self,
71        _mapping_vmo: &zx::Vmo,
72        delivery_queue: zx::Vmo,
73    ) -> Result<Verifier, zx::Status> {
74        Ok(Verifier::new(delivery_queue))
75    }
76}
77
78/// A bit flag set on [`RequestId`] values for internal requests issued by the server (such as
79/// [`DefaultCallbackBlockService`] reads) rather than external client sessions. This allows
80/// [`SessionManager::complete_request`] to distinguish internal requests and route completions
81/// to internal callback handlers instead of client session FIFOs.
82const INTERNAL_REQUEST_FLAG: usize = 1 << 63;
83
84#[derive(Default)]
85struct InflightRequests {
86    /// Total number of in-flight requests submitted to `Interface::on_requests`.
87    count: usize,
88    /// Completion callbacks for internal requests (e.g. from `DefaultCallbackBlockService`).
89    callbacks: HashMap<RequestId, Box<dyn FnOnce(Result<(), zx::Status>) + Send>>,
90    /// Monotonically increasing counter for allocating internal request IDs.
91    next_internal_id: usize,
92}
93
94struct SessionManagerInner<I: Interface + ?Sized> {
95    open_sessions: HashMap<usize, Weak<Session<I>>>,
96}
97
98// The signals used on the session's FIFO.
99
100/// Signalled on the session's FIFO to wake up the FIFO loop.
101const FIFO_WAKE_SIGNAL: zx::Signals = zx::Signals::USER_0;
102
103/// Signalled on the session's FIFO to terminate the FIFO loop.
104const SHUTDOWN_SIGNAL: zx::Signals = zx::Signals::USER_1;
105
106pub struct SessionManager<I: Interface + ?Sized> {
107    interface: Arc<I>,
108    block_size: u32,
109    // These represent active *client* requests, which correspond to one or more in-flight requests.
110    active_requests: ActiveRequests<Arc<Session<I>>>,
111    key_registry: KeyRegistry,
112    inflight_requests: Mutex<InflightRequests>,
113    no_inflight_requests_condvar: Condvar,
114    inner: Mutex<SessionManagerInner<I>>,
115    no_open_sessions_condvar: Condvar,
116    block_service: OnceLock<Arc<dyn BlockService>>,
117}
118
119impl<I: Interface + ?Sized> super::SessionManager for SessionManager<I> {
120    const SUPPORTS_DECOMPRESSION: bool = false;
121
122    type Orchestrator = I::Orchestrator;
123    type Session = Arc<Session<I>>;
124
125    fn session_eq(a: &Arc<Session<I>>, b: &Arc<Session<I>>) -> bool {
126        Arc::ptr_eq(a, b)
127    }
128
129    async fn on_attach_vmo(
130        _orchestrator: Arc<Self::Orchestrator>,
131        _vmo: &Arc<zx::Vmo>,
132    ) -> Result<(), zx::Status> {
133        Ok(())
134    }
135
136    /// Called when a new session request stream is opened. Constructs a `SessionHelper` with the
137    /// client-provided `offset_map` and device constraints, spawns the session runner via
138    /// `I::spawn_session`, and processes FIFO requests until the session channel is closed or
139    /// aborted.
140    async fn open_session(
141        orchestrator: Arc<Self::Orchestrator>,
142        mut stream: fblock::SessionRequestStream,
143        offset_map: OffsetMap,
144        block_size: u32,
145    ) -> Result<(), Error> {
146        let sm: &SessionManager<I> = orchestrator.as_ref().borrow();
147        let max_blocks = sm.get_info().max_transfer_blocks();
148        let (helper, fifo) =
149            SessionHelper::new(orchestrator.clone(), offset_map, max_blocks, block_size)?;
150        let (abort_handle, registration) = AbortHandle::new_pair();
151        let session = Arc::new(Session {
152            helper,
153            fifo,
154            queue: Mutex::default(),
155            abort_handle,
156            close_callback: Mutex::new(None),
157        });
158        let sm = orchestrator.as_ref().borrow();
159        sm.inner
160            .lock()
161            .open_sessions
162            .insert(Arc::as_ptr(&session) as usize, Arc::downgrade(&session));
163
164        sm.interface.spawn_session(session.clone());
165
166        let result = Abortable::new(
167            async {
168                while let Some(request) = stream.try_next().await? {
169                    match session.helper.handle_request(request).await? {
170                        HandleRequestResult::Ok => {}
171                        HandleRequestResult::Closed(callback) => {
172                            *session.close_callback.lock() = Some(callback);
173                            break;
174                        }
175                    }
176                }
177                Ok(())
178            },
179            registration,
180        )
181        .await
182        .unwrap_or_else(|e| Err(e.into()));
183
184        let _ = session.fifo.signal(zx::Signals::empty(), SHUTDOWN_SIGNAL);
185
186        result
187    }
188
189    fn open_mapper_session(
190        orchestrator: Arc<Self::Orchestrator>,
191        session: fidl::endpoints::ServerEnd<fblock::MapperSessionMarker>,
192        mapping_vmo: zx::Vmo,
193        _block_size: u32,
194        port: Option<zx::Port>,
195        delivery_queue: Option<zx::Vmo>,
196    ) -> Result<impl Future<Output = Result<(), Error>> + Send, zx::Status> {
197        let sm: &SessionManager<I> = orchestrator.as_ref().borrow();
198        let service = sm.into_block_service(&orchestrator);
199        let interface = sm.interface.clone();
200        crate::mapper::serve_mapper_session(
201            Arc::new(move |mapping_vmo: &zx::Vmo, dq: zx::Vmo| {
202                interface.on_open_mapper_session(mapping_vmo, dq)
203            }),
204            service,
205            session,
206            mapping_vmo,
207            port,
208            delivery_queue,
209        )
210    }
211
212    fn get_info(&self) -> Cow<'_, super::DeviceInfo> {
213        self.interface.get_info()
214    }
215
216    fn active_requests(&self) -> &ActiveRequests<Arc<Session<I>>> {
217        &self.active_requests
218    }
219
220    fn key_registry(&self) -> &KeyRegistry {
221        &self.key_registry
222    }
223}
224
225impl<I: Interface + ?Sized> SessionManager<I> {
226    /// Returns the memoized [`BlockService`] for this session manager.
227    pub fn into_block_service(&self, orchestrator: &Arc<I::Orchestrator>) -> Arc<dyn BlockService> {
228        self.block_service
229            .get_or_init(|| self.interface.clone().into_block_service(orchestrator))
230            .clone()
231    }
232}
233
234impl<I: Interface + ?Sized> SessionManager<I> {
235    pub fn new(interface: Arc<I>, block_size: u32) -> Self
236    where
237        I: Sized,
238    {
239        Self {
240            interface,
241            block_size,
242            active_requests: ActiveRequests::default(),
243            key_registry: KeyRegistry::default(),
244            inflight_requests: Mutex::new(InflightRequests::default()),
245            no_inflight_requests_condvar: Condvar::new(),
246            inner: Mutex::new(SessionManagerInner { open_sessions: HashMap::new() }),
247            no_open_sessions_condvar: Condvar::new(),
248            block_service: OnceLock::new(),
249        }
250    }
251
252    pub fn block_size(&self) -> u32 {
253        self.block_size
254    }
255
256    /// Reports the given task as complete with a given status.
257    pub fn complete_request(&self, request_id: RequestId, status: Result<(), zx::Status>) {
258        let (callback, notify) = {
259            let mut inflight = self.inflight_requests.lock();
260            let callback = if request_id.0 & INTERNAL_REQUEST_FLAG != 0 {
261                inflight.callbacks.remove(&request_id)
262            } else {
263                None
264            };
265            inflight.count -= 1;
266            let notify = inflight.count == 0;
267            (callback, notify)
268        };
269        if let Some(callback) = callback {
270            callback(status);
271        } else {
272            self.complete_unsubmitted_request(request_id, status);
273        }
274        if notify {
275            self.no_inflight_requests_condvar.notify_all();
276        }
277    }
278
279    fn submit_internal_request(
280        &self,
281        make_request: impl FnOnce(RequestId) -> Request,
282        callback: Box<dyn FnOnce(Result<(), zx::Status>) + Send>,
283    ) {
284        let req = {
285            let mut inflight = self.inflight_requests.lock();
286            let req_id = RequestId(inflight.next_internal_id | INTERNAL_REQUEST_FLAG);
287            inflight.next_internal_id += 1;
288            inflight.callbacks.insert(req_id, callback);
289            inflight.count += 1;
290            make_request(req_id)
291        };
292        self.interface.on_requests(&[req]);
293    }
294
295    fn submit_requests(&self, requests: &[Request]) {
296        self.inflight_requests.lock().count += requests.len();
297        self.interface.on_requests(requests);
298    }
299
300    /// Waits for there to be no requests in-flight.
301    ///
302    /// NOTE: To void TOCTOUs, this must be called on the same thread which calls
303    /// [`Self::submit_requests`].
304    fn wait_for_no_inflight_requests(&self) {
305        let mut guard = self.inflight_requests.lock();
306        self.no_inflight_requests_condvar.wait_while(&mut guard, |inflight| inflight.count > 0);
307    }
308
309    /// Called instead of `[Self::complete_request]` when a request is completed before it was
310    /// actually submitted.
311    fn complete_unsubmitted_request(&self, request_id: RequestId, status: Result<(), zx::Status>) {
312        if let Some((session, response)) =
313            self.active_requests.complete_and_take_response(request_id, status)
314        {
315            session.send_response(response);
316        }
317    }
318
319    /// Terminates the session manager.  Blocks until all sessions have terminated.
320    pub fn terminate(&self) {
321        {
322            // We must drop references to sessions whilst we're not holding the lock for
323            // `open_sessions` because `Session::drop` needs to take that same lock.
324            #[allow(clippy::collection_is_never_read)]
325            let mut terminated_sessions = Vec::new();
326            for (_, session) in &self.inner.lock().open_sessions {
327                if let Some(session) = session.upgrade() {
328                    session.terminate_async();
329                    terminated_sessions.push(session);
330                }
331            }
332        }
333        let mut guard = self.inner.lock();
334        self.no_open_sessions_condvar.wait_while(&mut guard, |s| !s.open_sessions.is_empty());
335    }
336}
337
338pub struct Session<I: Interface + ?Sized> {
339    helper: SessionHelper<SessionManager<I>>,
340    fifo: zx::Fifo<BlockFifoRequest, BlockFifoResponse>,
341    queue: Mutex<SessionQueue>,
342    abort_handle: AbortHandle,
343    close_callback: Mutex<Option<Box<dyn FnOnce() + Send + 'static>>>,
344}
345
346#[derive(Default)]
347struct SessionQueue {
348    responses: VecDeque<BlockFifoResponse>,
349}
350
351pub const MAX_REQUESTS: usize = super::FIFO_MAX_REQUESTS;
352
353struct DecodedRequests {
354    requests: [MaybeUninit<Request>; MAX_REQUESTS],
355    count: usize,
356}
357
358impl Default for DecodedRequests {
359    fn default() -> Self {
360        Self { requests: [const { MaybeUninit::uninit() }; MAX_REQUESTS], count: 0 }
361    }
362}
363
364impl DecodedRequests {
365    fn push(&mut self, request: Request) {
366        assert!(self.count < MAX_REQUESTS);
367        // To ensure drop runs, we must initialize each element at most once.
368        // As long as we only go via this function, that's satisfied.
369        self.requests[self.count].write(request);
370        self.count += 1;
371    }
372
373    fn is_empty(&self) -> bool {
374        self.count == 0
375    }
376
377    fn is_full(&self) -> bool {
378        self.count == MAX_REQUESTS
379    }
380
381    fn clear(&mut self) {
382        for i in 0..self.count {
383            // SAFETY: We initialized `count` elements via [`Self::push`].
384            unsafe { self.requests[i].assume_init_drop() };
385        }
386        self.count = 0;
387    }
388}
389
390impl Drop for DecodedRequests {
391    fn drop(&mut self) {
392        self.clear();
393    }
394}
395
396impl std::ops::Deref for DecodedRequests {
397    type Target = [Request];
398
399    fn deref(&self) -> &Self::Target {
400        // SAFETY: We wrote the request in [`Self::push`].
401        unsafe { std::slice::from_raw_parts(self.requests[0].as_ptr(), self.count) }
402    }
403}
404
405impl std::ops::DerefMut for DecodedRequests {
406    fn deref_mut(&mut self) -> &mut Self::Target {
407        // SAFETY: We wrote the request in [`Self::push`].
408        unsafe { std::slice::from_raw_parts_mut(self.requests[0].as_mut_ptr(), self.count) }
409    }
410}
411
412impl<I: Interface + ?Sized> Session<I> {
413    /// Begins processing the session's FIFO.  Blocks until completion (either because of
414    /// termination, or due to unrecoverable error).
415    pub fn run(self: &Arc<Self>) {
416        self.fifo_loop();
417        self.abort_handle.abort();
418        // NB: We cannot call [`drop_active_requests`] here, because requests which have already
419        // been submitted are no longer in control by this thread, and will be later completed by
420        // another thread.  If we dropped them here, then they would be completed twice.
421        self.helper.close_active_groups(|s| Arc::ptr_eq(s, self));
422    }
423
424    fn fifo_loop(self: &Arc<Self>) {
425        let mut requests = [MaybeUninit::uninit(); MAX_REQUESTS];
426
427        loop {
428            // Send queued responses.
429            let is_queue_empty = {
430                let mut queue = self.queue.lock();
431                while !queue.responses.is_empty() {
432                    let (front, _) = queue.responses.as_slices();
433                    match self.fifo.write(front) {
434                        Ok(count) => {
435                            let full = count.get() < front.len();
436                            queue.responses.drain(..count.get());
437                            if full {
438                                break;
439                            }
440                        }
441                        Err(zx::Status::SHOULD_WAIT) => break,
442                        Err(_) => return,
443                    }
444                }
445                queue.responses.is_empty()
446            };
447
448            // Process pending reads.
449            match self.fifo.read_uninit(&mut requests) {
450                Ok(valid_requests) => self.handle_requests(valid_requests.iter_mut()),
451                Err(zx::Status::SHOULD_WAIT) => {
452                    let mut signals =
453                        zx::Signals::OBJECT_READABLE | SHUTDOWN_SIGNAL | FIFO_WAKE_SIGNAL;
454                    if !is_queue_empty {
455                        signals |= zx::Signals::OBJECT_WRITABLE;
456                    }
457                    let Ok(signals) =
458                        self.fifo.wait_one(signals, zx::MonotonicInstant::INFINITE).to_result()
459                    else {
460                        return;
461                    };
462                    if signals.contains(SHUTDOWN_SIGNAL) {
463                        return;
464                    }
465                    // Clear FIFO_WAKE_SIGNAL if it's set.
466                    if signals.contains(FIFO_WAKE_SIGNAL) {
467                        let _ = self.fifo.signal(FIFO_WAKE_SIGNAL, zx::Signals::empty());
468                    }
469                }
470                Err(_) => return,
471            }
472        }
473    }
474
475    /// Synchronously performs a device flush.
476    fn pre_flush(self: &Arc<Self>, request_id: RequestId) -> Result<(), zx::Status> {
477        let trace_flow_id = {
478            let mut request = self.helper.session_manager().active_requests.request(request_id);
479            if let Some(id) = request.trace_flow_id {
480                fuchsia_trace::async_instant!(
481                    fuchsia_trace::Id::from(id.get()),
482                    c"storage",
483                    c"block_server::SimulatedBarrier",
484                    "request_id" => request_id.0
485                );
486            }
487            request.count += 1;
488            request.trace_flow_id
489        };
490        self.helper.session_manager().submit_requests(&[Request {
491            request_id,
492            operation: Operation::Flush,
493            trace_flow_id,
494            vmo: None,
495        }]);
496        self.helper.session_manager().wait_for_no_inflight_requests();
497        let status = self.helper.session_manager().active_requests.request(request_id).status;
498        match status {
499            Ok(()) => Ok(()),
500            Err(status) => {
501                // Respond for the unsubmitted request too.
502                self.helper.session_manager().complete_unsubmitted_request(request_id, Err(status));
503                Err(status)
504            }
505        }
506    }
507
508    /// Synchronously completes `decoded_requests`, and inserts a post-flush into `decoded_requests`
509    /// to be submitted later.
510    fn post_flush(self: &Arc<Self>, request_id: RequestId, decoded_requests: &mut DecodedRequests) {
511        if !decoded_requests.is_empty() {
512            self.helper.session_manager().submit_requests(decoded_requests);
513            decoded_requests.clear();
514        }
515        self.helper.session_manager().wait_for_no_inflight_requests();
516        let request = self.helper.session_manager().active_requests.request(request_id);
517        match request.status {
518            Ok(()) => decoded_requests.push(Request {
519                request_id,
520                operation: Operation::Flush,
521                trace_flow_id: request.trace_flow_id,
522                vmo: None,
523            }),
524            Err(status) => {
525                drop(request);
526                self.helper.session_manager().complete_unsubmitted_request(request_id, Err(status))
527            }
528        }
529    }
530
531    fn handle_requests<'a>(
532        self: &Arc<Self>,
533        requests: impl Iterator<Item = &'a mut BlockFifoRequest>,
534    ) {
535        let manager = &self.helper.session_manager();
536        let mut decoded_requests = DecodedRequests::default();
537
538        for request in requests {
539            match self.helper.decode_fifo_request(self.clone(), request) {
540                Ok(DecodedRequest { operation: Operation::CloseVmo, request_id, .. }) => {
541                    manager.complete_unsubmitted_request(request_id, Ok(()));
542                }
543                Ok(mut request) => {
544                    let request_id = request.request_id;
545
546                    // Strip the PRE_BARRIER flag if we don't support it, and simulate the barrier
547                    // with a pre-flush.
548                    if !manager
549                        .interface
550                        .get_info()
551                        .device_flags()
552                        .contains(fblock::DeviceFlag::BARRIER_SUPPORT)
553                        && request.operation.take_write_flag(WriteFlags::PRE_BARRIER)
554                        && self.pre_flush(request_id).is_err()
555                    {
556                        continue;
557                    }
558                    // Strip the FORCE_ACCESS flag if we don't support it, and simulate the FUA with
559                    // a post-flush.
560                    let simulate_fua = !manager
561                        .interface
562                        .get_info()
563                        .device_flags()
564                        .contains(fblock::DeviceFlag::FUA_SUPPORT)
565                        && request.operation.take_write_flag(WriteFlags::FORCE_ACCESS);
566
567                    if simulate_fua {
568                        // Account for the additional request we need at the end.
569                        manager.active_requests.request(request_id).count += 1;
570                    }
571
572                    loop {
573                        let result = self
574                            .helper
575                            .map_request(request, &mut manager.active_requests.request(request_id));
576                        match result {
577                            Ok((
578                                DecodedRequest { request_id, operation, vmo, trace_flow_id },
579                                remainder,
580                            )) => {
581                                decoded_requests.push(Request {
582                                    request_id,
583                                    operation,
584                                    trace_flow_id,
585                                    vmo,
586                                });
587
588                                if decoded_requests.is_full() {
589                                    manager.submit_requests(&*decoded_requests);
590                                    decoded_requests.clear();
591                                }
592
593                                if let Some(r) = remainder {
594                                    request = r;
595                                } else {
596                                    break;
597                                }
598                            }
599                            Err(status) => {
600                                manager.complete_unsubmitted_request(request_id, Err(status));
601                                break;
602                            }
603                        }
604                    }
605
606                    if simulate_fua {
607                        self.post_flush(request_id, &mut decoded_requests);
608                    }
609                }
610                Err(None) => {}
611                Err(Some(response)) => self.send_response(response),
612            }
613        }
614
615        if !decoded_requests.is_empty() {
616            manager.submit_requests(&decoded_requests);
617        }
618    }
619
620    fn send_response(&self, response: BlockFifoResponse) {
621        let mut queue = self.queue.lock();
622        if queue.responses.is_empty() {
623            match self.fifo.write_one(&response) {
624                Ok(()) => {
625                    return;
626                }
627                Err(_) => {
628                    // Wake the FIFO loop up so that we can send the response later.
629                    let _ = self.fifo.signal(zx::Signals::empty(), FIFO_WAKE_SIGNAL);
630                }
631            }
632        }
633        queue.responses.push_back(response);
634    }
635
636    /// Asynchronously request to terminate the Session.  The session's thread will eventually stop
637    /// running.
638    pub fn terminate_async(&self) {
639        let _ = self.fifo.signal(zx::Signals::empty(), SHUTDOWN_SIGNAL);
640        self.abort_handle.abort();
641    }
642}
643
644impl<I: Interface + ?Sized> Drop for Session<I> {
645    fn drop(&mut self) {
646        let callback = std::mem::take(&mut *self.close_callback.lock());
647        if let Some(callback) = callback {
648            callback();
649        }
650        let notify = {
651            let mut inner = self.helper.session_manager().inner.lock();
652            inner.open_sessions.remove(&(self as *const _ as usize));
653            inner.open_sessions.is_empty()
654        };
655        if notify {
656            self.helper.session_manager().no_open_sessions_condvar.notify_all();
657        }
658    }
659}
660
661impl<I: Interface + ?Sized> Drop for SessionManager<I> {
662    fn drop(&mut self) {
663        self.terminate();
664    }
665}
666
667impl<I: Interface<Orchestrator = SessionManager<I>>> IntoOrchestrator for Arc<SessionManager<I>> {
668    type SM = SessionManager<I>;
669
670    fn into_orchestrator(self) -> Arc<I::Orchestrator> {
671        self
672    }
673}
674
675#[cfg(test)]
676mod tests {
677    use super::*;
678    use crate::testing::MockInterface;
679    use block_protocol::{BlockFifoCommand, BlockFifoRequest, BlockFifoResponse};
680    use fidl::endpoints::create_proxy_and_stream;
681    use fidl_fuchsia_storage_block as fblock;
682    use fuchsia_async as fasync;
683
684    const BLOCK_SIZE: u32 = 512;
685
686    #[fuchsia::test]
687    async fn test_basic_request() {
688        let (tx, rx) = std::sync::mpsc::channel();
689        let interface = Arc::new(MockInterface::new(tx));
690        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
691
692        let sm_clone = session_manager.clone();
693        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
694        let _server_task = fasync::Task::spawn(async move {
695            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
696            server.handle_requests(stream).await.unwrap();
697        })
698        .detach();
699
700        let (session_proxy, session_server_end) =
701            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
702        proxy.open_session(session_server_end).unwrap();
703
704        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
705        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
706
707        let vmo = zx::Vmo::create(8192).unwrap();
708        let vmo_id = session_proxy
709            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
710            .await
711            .unwrap()
712            .unwrap();
713
714        let req = BlockFifoRequest {
715            command: BlockFifoCommand {
716                opcode: fblock::BlockOpcode::Read.into_primitive(),
717                ..Default::default()
718            },
719            reqid: 123,
720            group: 0,
721            vmoid: vmo_id.id,
722            length: 1,
723            vmo_offset: 0,
724            dev_offset: 0,
725            trace_flow_id: 0,
726            ..Default::default()
727        };
728        fifo.write(&[req]).unwrap();
729
730        let r = rx.recv().unwrap();
731        assert_eq!(r.request_id.0, 0);
732        assert!(matches!(r.operation, Operation::Read { .. }));
733
734        session_manager.complete_request(r.request_id, Ok(()));
735
736        let signals =
737            fifo.wait_one(zx::Signals::FIFO_READABLE, zx::MonotonicInstant::INFINITE).unwrap();
738        assert!(signals.contains(zx::Signals::FIFO_READABLE));
739
740        let mut resp = [BlockFifoResponse::default()];
741        fifo.read(&mut resp).unwrap();
742        assert_eq!(resp[0].reqid, 123);
743        assert_eq!(resp[0].status, zx::sys::ZX_OK);
744
745        std::mem::drop(proxy);
746    }
747    #[fuchsia::test]
748    async fn test_write_request() {
749        let (tx, rx) = std::sync::mpsc::channel();
750        let interface = Arc::new(MockInterface::new(tx));
751        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
752
753        let sm_clone = session_manager.clone();
754        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
755        let _server_task = fasync::Task::spawn(async move {
756            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
757            server.handle_requests(stream).await.unwrap();
758        })
759        .detach();
760
761        let (session_proxy, session_server_end) =
762            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
763        proxy.open_session(session_server_end).unwrap();
764
765        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
766        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
767
768        let vmo = zx::Vmo::create(8192).unwrap();
769        let vmo_id = session_proxy
770            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
771            .await
772            .unwrap()
773            .unwrap();
774
775        let req = BlockFifoRequest {
776            command: BlockFifoCommand {
777                opcode: fblock::BlockOpcode::Write.into_primitive(),
778                ..Default::default()
779            },
780            reqid: 124,
781            group: 0,
782            vmoid: vmo_id.id,
783            length: 1,
784            vmo_offset: 0,
785            dev_offset: 0,
786            trace_flow_id: 0,
787            ..Default::default()
788        };
789        fifo.write(&[req]).unwrap();
790
791        let r = rx.recv().unwrap();
792        assert_eq!(r.request_id.0, 0);
793        assert!(matches!(r.operation, Operation::Write { .. }));
794
795        session_manager.complete_request(r.request_id, Ok(()));
796
797        let signals =
798            fifo.wait_one(zx::Signals::FIFO_READABLE, zx::MonotonicInstant::INFINITE).unwrap();
799        assert!(signals.contains(zx::Signals::FIFO_READABLE));
800
801        let mut resp = [BlockFifoResponse::default()];
802        fifo.read(&mut resp).unwrap();
803        assert_eq!(resp[0].reqid, 124);
804        assert_eq!(resp[0].status, zx::sys::ZX_OK);
805    }
806
807    #[fuchsia::test]
808    async fn test_flush_request() {
809        let (tx, rx) = std::sync::mpsc::channel();
810        let interface = Arc::new(MockInterface::new(tx));
811        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
812
813        let sm_clone = session_manager.clone();
814        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
815        let _server_task = fasync::Task::spawn(async move {
816            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
817            server.handle_requests(stream).await.unwrap();
818        })
819        .detach();
820
821        let (session_proxy, session_server_end) =
822            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
823        proxy.open_session(session_server_end).unwrap();
824
825        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
826        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
827
828        let req = BlockFifoRequest {
829            command: BlockFifoCommand {
830                opcode: fblock::BlockOpcode::Flush.into_primitive(),
831                ..Default::default()
832            },
833            reqid: 125,
834            group: 0,
835            vmoid: fblock::VMOID_INVALID,
836            length: 0,
837            vmo_offset: 0,
838            dev_offset: 0,
839            trace_flow_id: 0,
840            ..Default::default()
841        };
842        fifo.write(&[req]).unwrap();
843
844        let r = rx.recv().unwrap();
845        assert_eq!(r.request_id.0, 0);
846        assert!(matches!(r.operation, Operation::Flush { .. }));
847
848        session_manager.complete_request(r.request_id, Ok(()));
849
850        let signals =
851            fifo.wait_one(zx::Signals::FIFO_READABLE, zx::MonotonicInstant::INFINITE).unwrap();
852        assert!(signals.contains(zx::Signals::FIFO_READABLE));
853
854        let mut resp = [BlockFifoResponse::default()];
855        fifo.read(&mut resp).unwrap();
856        assert_eq!(resp[0].reqid, 125);
857        assert_eq!(resp[0].status, zx::sys::ZX_OK);
858    }
859
860    #[fuchsia::test]
861    async fn test_trim_request() {
862        let (tx, rx) = std::sync::mpsc::channel();
863        let interface = Arc::new(MockInterface::new(tx));
864        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
865
866        let sm_clone = session_manager.clone();
867        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
868        let _server_task = fasync::Task::spawn(async move {
869            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
870            server.handle_requests(stream).await.unwrap();
871        })
872        .detach();
873
874        let (session_proxy, session_server_end) =
875            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
876        proxy.open_session(session_server_end).unwrap();
877
878        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
879        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
880
881        let req = BlockFifoRequest {
882            command: BlockFifoCommand {
883                opcode: fblock::BlockOpcode::Trim.into_primitive(),
884                ..Default::default()
885            },
886            reqid: 126,
887            group: 0,
888            vmoid: fblock::VMOID_INVALID,
889            length: 1,
890            vmo_offset: 0,
891            dev_offset: 0,
892            trace_flow_id: 0,
893            ..Default::default()
894        };
895        fifo.write(&[req]).unwrap();
896
897        let r = rx.recv().unwrap();
898        assert_eq!(r.request_id.0, 0);
899        assert!(matches!(r.operation, Operation::Trim { .. }));
900
901        session_manager.complete_request(r.request_id, Ok(()));
902
903        let signals =
904            fifo.wait_one(zx::Signals::FIFO_READABLE, zx::MonotonicInstant::INFINITE).unwrap();
905        assert!(signals.contains(zx::Signals::FIFO_READABLE));
906
907        let mut resp = [BlockFifoResponse::default()];
908        fifo.read(&mut resp).unwrap();
909        assert_eq!(resp[0].reqid, 126);
910        assert_eq!(resp[0].status, zx::sys::ZX_OK);
911    }
912
913    #[fuchsia::test]
914    async fn test_close_vmo() {
915        let (tx, rx) = std::sync::mpsc::channel();
916        let interface = Arc::new(MockInterface::new(tx));
917        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
918
919        let sm_clone = session_manager.clone();
920        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
921        let _server_task = fasync::Task::spawn(async move {
922            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
923            server.handle_requests(stream).await.unwrap();
924        })
925        .detach();
926
927        let (session_proxy, session_server_end) =
928            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
929        proxy.open_session(session_server_end).unwrap();
930
931        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
932        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
933
934        let vmo = zx::Vmo::create(8192).unwrap();
935        let vmo_id = session_proxy
936            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
937            .await
938            .unwrap()
939            .unwrap();
940
941        let req = BlockFifoRequest {
942            command: BlockFifoCommand {
943                opcode: fblock::BlockOpcode::CloseVmo.into_primitive(),
944                ..Default::default()
945            },
946            reqid: 127,
947            group: 0,
948            vmoid: vmo_id.id,
949            length: 0,
950            vmo_offset: 0,
951            dev_offset: 0,
952            trace_flow_id: 0,
953            ..Default::default()
954        };
955        fifo.write(&[req]).unwrap();
956
957        let signals =
958            fifo.wait_one(zx::Signals::FIFO_READABLE, zx::MonotonicInstant::INFINITE).unwrap();
959        assert!(signals.contains(zx::Signals::FIFO_READABLE));
960
961        let mut resp = [BlockFifoResponse::default()];
962        fifo.read(&mut resp).unwrap();
963        assert_eq!(resp[0].reqid, 127);
964        assert_eq!(resp[0].status, zx::sys::ZX_OK);
965
966        // CloseVmo is handled automatically. Interface should not receive anything.
967        assert!(rx.try_recv().is_err());
968    }
969
970    #[fuchsia::test]
971    async fn test_error() {
972        let (tx, rx) = std::sync::mpsc::channel();
973        let interface = Arc::new(MockInterface::new(tx));
974        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
975
976        let sm_clone = session_manager.clone();
977        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
978        let _server_task = fasync::Task::spawn(async move {
979            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
980            server.handle_requests(stream).await.unwrap();
981        })
982        .detach();
983
984        let (session_proxy, session_server_end) =
985            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
986        proxy.open_session(session_server_end).unwrap();
987
988        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
989        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
990
991        let req = BlockFifoRequest {
992            command: BlockFifoCommand {
993                opcode: fblock::BlockOpcode::Flush.into_primitive(),
994                ..Default::default()
995            },
996            reqid: 128,
997            group: 0,
998            vmoid: fblock::VMOID_INVALID,
999            length: 0,
1000            vmo_offset: 0,
1001            dev_offset: 0,
1002            trace_flow_id: 0,
1003            ..Default::default()
1004        };
1005        fifo.write(&[req]).unwrap();
1006
1007        let r = rx.recv().unwrap();
1008        session_manager.complete_request(r.request_id, Err(zx::Status::IO));
1009
1010        let signals =
1011            fifo.wait_one(zx::Signals::FIFO_READABLE, zx::MonotonicInstant::INFINITE).unwrap();
1012        assert!(signals.contains(zx::Signals::FIFO_READABLE));
1013
1014        let mut resp = [BlockFifoResponse::default()];
1015        fifo.read(&mut resp).unwrap();
1016        assert_eq!(resp[0].reqid, 128);
1017        assert_eq!(resp[0].status, zx::sys::ZX_ERR_IO);
1018    }
1019
1020    #[fuchsia::test]
1021    async fn test_teardown_with_active_requests() {
1022        let (tx, rx) = std::sync::mpsc::channel();
1023        let interface = Arc::new(MockInterface::new(tx));
1024        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
1025
1026        let sm_clone = session_manager.clone();
1027        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
1028        let server_task = fasync::Task::spawn(async move {
1029            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
1030            server.handle_requests(stream).await.unwrap();
1031        });
1032
1033        let (session_proxy, session_server_end) =
1034            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
1035        proxy.open_session(session_server_end).unwrap();
1036
1037        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
1038        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
1039
1040        let vmo = zx::Vmo::create(8192).unwrap();
1041        let vmo_id = session_proxy
1042            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1043            .await
1044            .unwrap()
1045            .unwrap();
1046
1047        let req = BlockFifoRequest {
1048            command: BlockFifoCommand {
1049                opcode: fblock::BlockOpcode::Read.into_primitive(),
1050                ..Default::default()
1051            },
1052            reqid: 129,
1053            group: 0,
1054            vmoid: vmo_id.id,
1055            length: 1,
1056            vmo_offset: 0,
1057            dev_offset: 0,
1058            trace_flow_id: 0,
1059            ..Default::default()
1060        };
1061        // Start a request and wait until it's been submitted.
1062        fifo.write(&[req]).unwrap();
1063        let r = rx.recv().unwrap();
1064
1065        // Close the client, which will eventually cause the FIFO loop to exit.
1066        drop(session_proxy);
1067        fasync::Timer::new(std::time::Duration::from_millis(50)).await;
1068
1069        // Complete the request, simulating a completion after the FIFO loop has exited.
1070        session_manager.complete_request(r.request_id, Ok(()));
1071
1072        drop(proxy);
1073        fasync::unblock(move || session_manager.terminate()).await;
1074        server_task.await;
1075    }
1076
1077    #[fuchsia::test]
1078    async fn test_teardown_with_active_grouped_requests() {
1079        let (tx, rx) = std::sync::mpsc::channel();
1080        let interface = Arc::new(MockInterface::new(tx));
1081        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
1082
1083        let sm_clone = session_manager.clone();
1084        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
1085        let server_task = fasync::Task::spawn(async move {
1086            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
1087            server.handle_requests(stream).await.unwrap();
1088        });
1089
1090        let (session_proxy, session_server_end) =
1091            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
1092        proxy.open_session(session_server_end).unwrap();
1093
1094        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
1095        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
1096
1097        let vmo = zx::Vmo::create(8192).unwrap();
1098        let vmo_id = session_proxy
1099            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1100            .await
1101            .unwrap()
1102            .unwrap();
1103
1104        let mut req = BlockFifoRequest {
1105            command: BlockFifoCommand {
1106                opcode: fblock::BlockOpcode::Read.into_primitive(),
1107                flags: fblock::BlockIoFlag::GROUP_ITEM.bits(),
1108                ..Default::default()
1109            },
1110            reqid: 1,
1111            group: 1,
1112            vmoid: vmo_id.id,
1113            length: 1,
1114            vmo_offset: 0,
1115            dev_offset: 0,
1116            trace_flow_id: 0,
1117            ..Default::default()
1118        };
1119
1120        // Start two groups of requests and wait until they're been submitted.
1121        fifo.write(&[req]).unwrap();
1122        req.reqid = 2;
1123        req.group = 2;
1124        fifo.write(&[req]).unwrap();
1125        let r1 = rx.recv().unwrap();
1126        let r2 = rx.recv().unwrap();
1127        req.reqid = 3;
1128        req.group = 2;
1129        fifo.write(&[req]).unwrap();
1130        let r3 = rx.recv().unwrap();
1131
1132        // Complete requests 1,2.  This leaves two groups in the following states:
1133        // - Group 1: 0 active requests, still waiting for END
1134        // - Group 2: 1 active request, still waiting for END
1135        // Neither group will be able to complete yet.
1136        session_manager.complete_request(r1.request_id, Ok(()));
1137        session_manager.complete_request(r2.request_id, Ok(()));
1138
1139        // Close the client, which will eventually cause the FIFO loop to exit.
1140        // Group 1 should complete now.  Group 2 can't yet.
1141        drop(session_proxy);
1142        fasync::Timer::new(std::time::Duration::from_millis(50)).await;
1143
1144        // At some later time, complete request 3, which should complete group 2.
1145        session_manager.complete_request(r3.request_id, Ok(()));
1146
1147        drop(proxy);
1148
1149        fasync::unblock(move || session_manager.terminate()).await;
1150        server_task.await;
1151    }
1152
1153    #[fuchsia::test]
1154    async fn test_session_close_is_synchronous() {
1155        use futures::FutureExt as _;
1156
1157        let (tx, rx) = std::sync::mpsc::channel();
1158        let interface = Arc::new(MockInterface::new(tx));
1159        let session_manager = Arc::new(SessionManager::new(interface.clone(), BLOCK_SIZE));
1160
1161        let sm_clone = session_manager.clone();
1162        let (proxy, stream) = create_proxy_and_stream::<fblock::BlockMarker>();
1163        let server_task = fasync::Task::spawn(async move {
1164            let server = crate::BlockServer::new(BLOCK_SIZE, sm_clone);
1165            server.handle_requests(stream).await.unwrap();
1166        });
1167
1168        let (session_proxy, session_server_end) =
1169            fidl::endpoints::create_proxy::<fblock::SessionMarker>();
1170        proxy.open_session(session_server_end).unwrap();
1171
1172        let fifo_handle = session_proxy.get_fifo().await.unwrap().unwrap();
1173        let fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest> = zx::Fifo::from(fifo_handle);
1174
1175        let vmo = zx::Vmo::create(8192).unwrap();
1176        let vmo_id = session_proxy
1177            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1178            .await
1179            .unwrap()
1180            .unwrap();
1181
1182        let req = BlockFifoRequest {
1183            command: BlockFifoCommand {
1184                opcode: fblock::BlockOpcode::Read.into_primitive(),
1185                ..Default::default()
1186            },
1187            reqid: 123,
1188            group: 0,
1189            vmoid: vmo_id.id,
1190            length: 1,
1191            vmo_offset: 0,
1192            dev_offset: 0,
1193            trace_flow_id: 0,
1194            ..Default::default()
1195        };
1196        fifo.write(&[req]).unwrap();
1197
1198        let r = rx.recv().unwrap();
1199
1200        // The close request shouldn't complete yet because the read is still hanging.
1201        let mut close_fut = std::pin::pin!(session_proxy.close().fuse());
1202        let mut timer_fut =
1203            std::pin::pin!(fasync::Timer::new(std::time::Duration::from_millis(100)).fuse());
1204        futures::select! {
1205            res = close_fut => panic!("close completed too early: {res:?}"),
1206            _ = timer_fut => {}
1207        }
1208
1209        session_manager.complete_request(r.request_id, Ok(()));
1210
1211        // Verify that close() now completes.
1212        close_fut.await.unwrap().unwrap();
1213
1214        std::mem::drop(proxy);
1215        server_task.await;
1216    }
1217}