Skip to main content

netdevice_client/session/
mod.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Fuchsia netdevice session.
6
7mod buffer;
8mod tx;
9
10use std::fmt::Debug;
11use std::mem::MaybeUninit;
12use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize, TryFromIntError};
13use std::ops::Range;
14use std::pin::Pin;
15use std::sync::Arc;
16use std::sync::atomic::{self, AtomicUsize};
17use std::task::Waker;
18
19use explicit::{PollExt as _, ResultExt as _};
20use fidl_fuchsia_hardware_network as netdev;
21use fidl_fuchsia_hardware_network::DelegatedRxLease;
22use fidl_table_validation::ValidFidlTable;
23use fuchsia_async as fasync;
24use fuchsia_sync::Mutex;
25use futures::future::{Future, poll_fn};
26use futures::task::{Context, Poll};
27use futures::{Stream, StreamExt as _, ready};
28
29use crate::error::{Error, Result};
30use buffer::pool::{CreatedPool, Pool, RxLeaseWatcher};
31use buffer::{
32    AllocKind, DescId, NETWORK_DEVICE_DESCRIPTOR_LENGTH, NETWORK_DEVICE_DESCRIPTOR_VERSION,
33};
34pub use buffer::{
35    Buffer, ChecksumRxOffloading, Rx, RxMetadata, SinglePartTxBuffer, Tx, TxMetadataMut,
36};
37use tx::{BufferUsageEstimator, TxState};
38
39// TODO(https://fxbug.dev/438527741): This is the VMO ID used for single VMO
40// clients (Rx + Tx in the same VMO). When VMO split is applied everywhere,
41// remove this constant.
42const DEFAULT_VMO_ID: u8 = 0;
43
44/// A session between network device client and driver.
45#[derive(Clone)]
46pub struct Session {
47    inner: Arc<Inner>,
48}
49
50impl Debug for Session {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        let Self { inner } = self;
53        let Inner { name, .. } = &**inner;
54        f.debug_struct("Session").field("name", &name).finish_non_exhaustive()
55    }
56}
57
58impl Session {
59    /// Creates a new session with the given `name` and `config`.
60    pub async fn new(
61        device: &netdev::DeviceProxy,
62        name: &str,
63        config: Config,
64    ) -> Result<(Self, Task)> {
65        let sample_interval = config.buffer_usage_sample_interval;
66        let inner = Inner::new(device, name, config).await?;
67        let buffer_usage_sampler = inner
68            .pool
69            .has_decommittable_tx_vmo()
70            .then(|| (fasync::Interval::new(sample_interval.into()), BufferUsageEstimator::new()));
71
72        Ok((Session { inner: Arc::clone(&inner) }, Task { inner, buffer_usage_sampler }))
73    }
74
75    /// Sends a [`Buffer`] to the network device in this session.
76    pub fn send(&self, buffer: Buffer<Tx>) {
77        self.inner.send(buffer)
78    }
79
80    /// Creates a new [`RxReadyStorage`] suitable for receiving buffers in this
81    /// session.
82    ///
83    /// The buffer capacity defaults to the session's configured
84    /// `num_rx_buffers`.
85    pub fn new_rx_ready_storage(&self) -> RxReadyStorage {
86        RxReadyStorage::new(self.inner.num_rx_buffers)
87    }
88
89    /// Receives completed [`Buffer`]s from the network device in this session
90    /// into `ready_storage` and returns an iterator over the results.
91    pub async fn recv<'a>(
92        &'a self,
93        ready_storage: &'a mut RxReadyStorage,
94    ) -> Result<impl ExactSizeIterator<Item = Result<Buffer<Rx>>> + 'a> {
95        self.inner.recv(ready_storage).await
96    }
97
98    /// Allocates a [`Buffer`] that may later be queued to the network device.
99    ///
100    /// The returned buffer will have at least `num_bytes` as size.
101    pub async fn alloc_tx_buffer(&self, num_bytes: usize) -> Result<Buffer<Tx>> {
102        self.inner.pool.alloc_tx_buffer(num_bytes).await
103    }
104
105    /// Tries to allocate a [`SinglePartTxBuffer`].
106    ///
107    /// Returns `Ok(None)` if there is no available buffer, or `Err(Error::TxLength)`
108    /// if the requested size cannot meet the device requirement.
109    pub fn try_alloc_single_part_tx_buffer(
110        &self,
111        num_bytes: usize,
112    ) -> Result<Option<SinglePartTxBuffer>> {
113        self.inner.pool.try_alloc_single_part_tx_buffer(num_bytes)
114    }
115
116    /// Waits for at least one TX buffer to be available and returns an iterator
117    /// of buffers with `num_bytes` as capacity.
118    ///
119    /// The returned iterator is guaranteed to yield at least one item (though
120    /// it might be an error if the requested size cannot meet the device
121    /// requirement).
122    ///
123    /// # Note
124    ///
125    /// Given a `Buffer<Tx>` is returned to the pool when it's dropped, the
126    /// returned iterator will seemingly yield infinite items if the yielded
127    /// `Buffer`s are dropped while iterating.
128    pub async fn alloc_tx_buffers(
129        &self,
130        num_bytes: usize,
131    ) -> Result<impl Iterator<Item = Result<Buffer<Tx>>> + '_> {
132        self.inner.pool.alloc_tx_buffers(num_bytes).await
133    }
134
135    /// Attaches [`Session`] to a port.
136    pub async fn attach(&self, port: Port, rx_frames: &[netdev::FrameType]) -> Result<()> {
137        // NB: Need to bind the future returned by `proxy.attach` to a variable
138        // otherwise this function's (`Session::attach`) returned future becomes
139        // not `Send` and we get unexpected compiler errors at a distance.
140        //
141        // The dyn borrow in the signature of `proxy.attach` seems to be the
142        // cause of the compiler's confusion.
143        let fut = self.inner.proxy.attach(&port.into(), rx_frames);
144        let () = fut.await?.map_err(|raw| Error::Attach(port, zx::Status::err_from_raw(raw)))?;
145        Ok(())
146    }
147
148    /// Detaches a port from the [`Session`].
149    pub async fn detach(&self, port: Port) -> Result<()> {
150        let () = self
151            .inner
152            .proxy
153            .detach(&port.into())
154            .await?
155            .map_err(|raw| Error::Detach(port, zx::Status::err_from_raw(raw)))?;
156        Ok(())
157    }
158
159    /// Blocks until there are no more tx buffers in flight to the backing
160    /// device.
161    ///
162    /// Note that this method does not prevent new buffers from being allocated
163    /// and sent, it is up to the caller to prevent any races. This future will
164    /// resolve as soon as it observes a tx idle event. That is, there are no
165    /// frames in flight to the backing device at all and the session currently
166    /// owns all allocated tx buffers.
167    ///
168    /// The synchronization guarantee provided by this method is that any
169    /// buffers previously given to [`Session::send`] will be accounted as
170    /// pending until the device has replied back.
171    pub async fn wait_tx_idle(&self) {
172        self.inner.tx_idle_listeners.wait().await;
173    }
174
175    /// Returns a stream of delegated rx leases from the device.
176    ///
177    /// Leases are yielded from the stream whenever the corresponding receive
178    /// buffer is dropped or reused for tx, which marks the end of processing
179    /// the marked buffer for the delegated lease.
180    ///
181    /// See [`fidl_fuchsia_hardware_network::DelegatedRxLease`] for more
182    /// details.
183    ///
184    /// # Panics
185    ///
186    /// Panics if the session was not created with
187    /// [`fidl_fuchsia_hardware_network::SessionFlags::RECEIVE_RX_POWER_LEASES`]
188    /// or if `watch_rx_leases` has already been called for this session.
189    pub fn watch_rx_leases(&self) -> impl Stream<Item = Result<RxLease>> + Send + Sync + use<> {
190        let inner = Arc::clone(&self.inner);
191        let watcher = RxLeaseWatcher::new(Arc::clone(&inner.pool));
192        futures::stream::try_unfold((inner, watcher), |(inner, mut watcher)| async move {
193            let DelegatedRxLease {
194                hold_until_frame,
195                handle,
196                __source_breaking: fidl::marker::SourceBreaking,
197            } = match inner.proxy.watch_delegated_rx_lease().await {
198                Ok(lease) => lease,
199                Err(e) => {
200                    if e.is_closed() {
201                        return Ok(None);
202                    } else {
203                        return Err(Error::Fidl(e));
204                    }
205                }
206            };
207            let hold_until_frame = hold_until_frame.ok_or(Error::InvalidLease)?;
208            let handle = RxLease::new(handle.ok_or(Error::InvalidLease)?);
209
210            watcher.wait_until(hold_until_frame).await;
211            Ok(Some((handle, (inner, watcher))))
212        })
213    }
214
215    /// Closes the session.
216    pub async fn close(&self) -> Result<()> {
217        self.inner.proxy.close()?;
218        // Synchronize by waiting for the channel to be closed.
219        let mut event_stream = self.inner.proxy.take_event_stream();
220        while let Some(event) = event_stream.next().await {
221            match event {
222                Err(fidl::Error::ClientChannelClosed { .. }) => break,
223                Ok(_) => {} // Ignore other events.
224                Err(e) => return Err(Error::Fidl(e)),
225            }
226        }
227        Ok(())
228    }
229}
230
231struct Inner {
232    pool: Arc<Pool>,
233    proxy: netdev::SessionProxy,
234    name: String,
235    rx: fasync::Fifo<DescId<Rx>>,
236    tx: fasync::Fifo<DescId<Tx>>,
237    num_rx_buffers: usize,
238    tx_ready: Mutex<ReadyStorage<DescId<Tx>>>,
239    tx_idle_listeners: TxIdleListeners,
240    tx_state: Mutex<TxState>,
241}
242
243impl Inner {
244    /// Creates a new session.
245    async fn new(device: &netdev::DeviceProxy, name: &str, config: Config) -> Result<Arc<Self>> {
246        let CreatedPool { pool, descriptors_vmo, data_vmos } = Pool::new(config.clone())?;
247        let tx_vmo_cumulative_buffers = {
248            let mut cumulative = 0u16;
249            config
250                .tx_vmos
251                .iter()
252                .map(|vmo_config| {
253                    cumulative += vmo_config.num_buffers;
254                    cumulative
255                })
256                .collect::<Vec<_>>()
257        };
258        let tx_state = Mutex::new(TxState::new(tx_vmo_cumulative_buffers));
259
260        let session_info = {
261            // The following two constants are not provided by user, panic
262            // instead of returning an error.
263            let descriptor_length =
264                u8::try_from(NETWORK_DEVICE_DESCRIPTOR_LENGTH / std::mem::size_of::<u64>())
265                    .expect("descriptor length in 64-bit words not representable by u8");
266            let data = data_vmos
267                .into_iter()
268                .enumerate()
269                .map(|(idx, vmo)| {
270                    let vmo_id = netdev::VmoId::try_from(idx).expect("invalid vmo id");
271                    let num_rx_buffers = config
272                        .rx_vmos
273                        .iter()
274                        .find(|v| v.vmo_id == vmo_id)
275                        .map(|v| v.num_buffers)
276                        .unwrap_or(0);
277                    fidl_fuchsia_hardware_network::DataVmo {
278                        id: Some(vmo_id),
279                        vmo: Some(vmo),
280                        num_rx_buffers: Some(num_rx_buffers),
281                        __source_breaking: fidl::marker::SourceBreaking,
282                    }
283                })
284                .collect::<Vec<_>>();
285            netdev::SessionInfo {
286                descriptors: Some(descriptors_vmo),
287                data: Some(data),
288                descriptor_version: Some(NETWORK_DEVICE_DESCRIPTOR_VERSION),
289                descriptor_length: Some(descriptor_length),
290                descriptor_count: Some(
291                    config.num_tx_buffers().get() + config.num_rx_buffers().get(),
292                ),
293                options: Some(config.options),
294                ..Default::default()
295            }
296        };
297
298        let (client, netdev::Fifos { rx, tx }) = device
299            .open_session(name, session_info)
300            .await?
301            .map_err(|raw| Error::Open(name.to_owned(), zx::Status::err_from_raw(raw)))?;
302        let proxy = client.into_proxy();
303
304        let rx = fasync::Fifo::from_fifo(rx);
305        let tx = fasync::Fifo::from_fifo(tx);
306
307        let vmos_to_register = if pool.has_decommittable_tx_vmo() {
308            // We keep the smallest VMO always registered for Tx.
309            &pool.decommittable_tx_vmo_ids[0..=0]
310        } else {
311            &[DEFAULT_VMO_ID]
312        };
313        let (s, status) = proxy.register_for_tx(vmos_to_register).await.map_err(Error::Fidl)?;
314        zx::Status::ok(status).map_err(Error::RegisterForTx)?;
315        assert_eq!(s, 1);
316
317        let num_rx_buffers = config.num_rx_buffers().get().into();
318
319        Ok(Arc::new(Self {
320            pool,
321            proxy,
322            name: name.to_owned(),
323            rx,
324            tx,
325            num_rx_buffers,
326            tx_ready: Mutex::new(ReadyStorage::new(config.num_tx_buffers().get().into())),
327            tx_idle_listeners: TxIdleListeners::new(),
328            tx_state,
329        }))
330    }
331
332    /// Polls to submit available rx descriptors from pool to driver.
333    ///
334    /// Returns the number of rx descriptors that are submitted.
335    fn poll_submit_rx(&self, cx: &mut Context<'_>) -> Poll<Result<usize>> {
336        self.pool.rx_pending.lock().poll_submit(&self.rx, cx)
337    }
338
339    /// Polls to submit tx descriptors that are pending to the driver.
340    ///
341    /// Returns the number of tx descriptors that are successfully submitted.
342    fn poll_submit_tx(&self, cx: &mut Context<'_>) -> Poll<Result<usize>> {
343        self.tx_state.lock().poll_submit_tx(&self.proxy, &self.pool, &self.tx, cx)
344    }
345
346    /// Polls completed tx descriptors from the driver then puts them in pool.
347    fn poll_complete_tx(&self, cx: &mut Context<'_>) -> Poll<Result<()>> {
348        let (count, result) = {
349            let mut tx_ready = self.tx_ready.lock();
350            ready!(tx_ready.poll_fifo(cx, &self.tx))
351                .map_err(|status| Error::Fifo("read", "tx", status))?;
352            // TODO(https://github.com/rust-lang/rust/issues/63569): Provide entire
353            // chain of completed descriptors to the pool at once when slice of
354            // MaybeUninit is stabilized.
355            tx_ready
356                .drain()
357                .try_fold(0, |count, desc| match self.pool.tx_completed(desc) {
358                    Ok(()) => Ok(count + 1),
359                    Err(e) => Err((count, e)),
360                })
361                .map_or_else(|(count, e)| (count, Err(e)), |count| (count, Ok(())))
362        };
363        self.tx_idle_listeners.tx_complete(count);
364        result?;
365        Poll::Ready(Ok(()))
366    }
367
368    /// Sends the [`Buffer`] to the driver.
369    ///
370    /// Note: Transmit is completely infallible because the buffer layout and
371    /// zero-padding are already fully resolved and verified upfront during
372    /// buffer allocation (see `AllocGuard::init` in `pool.rs` for details
373    /// and design tradeoffs).
374    fn send(&self, buffer: Buffer<Tx>) {
375        self.tx_idle_listeners.tx_started();
376        let mut state = self.tx_state.lock();
377        state.send(&self.proxy, &self.pool, buffer);
378    }
379
380    /// Receives [`Buffer`]s from the driver into `ready_storage`.
381    ///
382    /// Waits until there are completed rx buffers from the driver.
383    async fn recv<'a>(
384        &'a self,
385        ready_storage: &'a mut RxReadyStorage,
386    ) -> Result<impl ExactSizeIterator<Item = Result<Buffer<Rx>>> + 'a> {
387        poll_fn(|cx| ready_storage.poll_fifo(cx, &self.rx))
388            .await
389            .map_err(|status| Error::Fifo("read", "rx", status))?;
390        Ok(ready_storage.drain().map(move |head| self.pool.rx_completed(head)))
391    }
392}
393
394/// The backing task that drives the session.
395///
396/// A session will stop making progress if this task is not polled continuously.
397#[must_use = "futures do nothing unless you `.await` or poll them"]
398pub struct Task {
399    inner: Arc<Inner>,
400    buffer_usage_sampler: Option<(fasync::Interval, BufferUsageEstimator)>,
401}
402
403impl Future for Task {
404    type Output = Result<()>;
405    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
406        let this = self.as_mut().get_mut();
407        let inner = &*this.inner;
408        loop {
409            let mut all_pending = true;
410
411            // TODO(https://fxbug.dev/42158458): poll once for all completed
412            // descriptors if this becomes a performance bottleneck.
413            while inner.poll_complete_tx(cx)?.is_ready_checked::<()>() {
414                all_pending = false;
415            }
416            if inner.poll_submit_rx(cx)?.is_ready_checked::<usize>() {
417                all_pending = false;
418            }
419            if inner.poll_submit_tx(cx)?.is_ready_checked::<usize>() {
420                all_pending = false;
421            }
422
423            if !all_pending {
424                continue;
425            }
426
427            if let Some((interval, estimator)) = &mut this.buffer_usage_sampler {
428                while interval.poll_next_unpin(cx).is_ready_checked::<Option<()>>() {
429                    let buffer_usage_estimate = {
430                        let mut state = inner.pool.tx_alloc_state.lock();
431                        estimator.update(state.sample_peak_buffer_usage())
432                    };
433                    if inner.tx_state.lock().attempt_unregister(
434                        buffer_usage_estimate,
435                        &inner.proxy,
436                        &inner.pool,
437                    ) {
438                        all_pending = false;
439                    }
440                }
441            }
442
443            if all_pending {
444                return Poll::Pending;
445            }
446        }
447    }
448}
449
450/// Configuration for a single VMO.
451#[derive(Debug, Clone, Copy, PartialEq, Eq)]
452pub(crate) struct VmoConfig {
453    /// The VMO ID.
454    pub(crate) vmo_id: netdev::VmoId,
455    /// Number of buffers to allocate in this VMO.
456    pub(crate) num_buffers: u16,
457}
458
459/// Session configuration.
460#[derive(Debug, Clone)]
461pub struct Config {
462    /// Buffer stride on VMO, in bytes.
463    buffer_stride: NonZeroU64,
464    /// Collection of rx VMO configurations.
465    rx_vmos: Vec<VmoConfig>,
466    /// Collection of tx VMO configurations.
467    tx_vmos: Vec<VmoConfig>,
468    /// Session flags.
469    options: netdev::SessionFlags,
470    /// Buffer layout.
471    buffer_layout: BufferLayout,
472    buffer_usage_sample_interval: std::time::Duration,
473}
474
475impl Config {
476    /// Returns the total number of Rx buffers across all VMOs.
477    pub fn num_rx_buffers(&self) -> NonZeroU16 {
478        let sum: u16 = self.rx_vmos.iter().map(|v| v.num_buffers).sum();
479        NonZeroU16::new(sum).expect("num_rx_buffers is zero")
480    }
481
482    /// Returns the total number of Tx buffers across all VMOs.
483    pub fn num_tx_buffers(&self) -> NonZeroU16 {
484        let sum: u16 = self.tx_vmos.iter().map(|v| v.num_buffers).sum();
485        NonZeroU16::new(sum).expect("num_tx_buffers is zero")
486    }
487}
488
489/// Describes the buffer layout that [`Pool`] needs to know.
490#[derive(Debug, Clone, Copy)]
491struct BufferLayout {
492    /// Minimum tx buffer data length.
493    min_tx_data: usize,
494    /// Minimum tx buffer head length.
495    min_tx_head: u16,
496    /// Minimum tx buffer tail length.
497    min_tx_tail: u16,
498    /// The length of a buffer.
499    length: usize,
500}
501
502/// Network device base info with all required fields.
503#[derive(Debug, Clone, ValidFidlTable)]
504#[fidl_table_src(netdev::DeviceBaseInfo)]
505#[fidl_table_strict]
506pub struct DeviceBaseInfo {
507    /// Maximum number of items in rx FIFO (per session).
508    pub rx_depth: u16,
509    /// Maximum number of items in tx FIFO (per session).
510    pub tx_depth: u16,
511    /// Alignment requirement for buffers in the data VMO.
512    pub buffer_alignment: u32,
513    /// Maximum supported length of buffers in the data VMO, in bytes.
514    #[fidl_field_type(optional)]
515    pub max_buffer_length: Option<NonZeroU32>,
516    /// The minimum rx buffer length required for device.
517    pub min_rx_buffer_length: u32,
518    /// The minimum tx buffer length required for the device.
519    pub min_tx_buffer_length: u32,
520    /// The number of bytes the device requests be free as `head` space in a tx buffer.
521    pub min_tx_buffer_head: u16,
522    /// The amount of bytes the device requests be free as `tail` space in a tx buffer.
523    pub min_tx_buffer_tail: u16,
524    /// Maximum descriptor chain length accepted by the device.
525    pub max_buffer_parts: u8,
526    /// Minimum amount of Rx buffers the client needs to prepare for the
527    /// network device.
528    #[fidl_field_type(optional)]
529    pub min_rx_buffers: Option<NonZeroU16>,
530}
531
532/// Network device information with all required fields.
533#[derive(Debug, Clone, ValidFidlTable)]
534#[fidl_table_src(netdev::DeviceInfo)]
535#[fidl_table_strict]
536pub struct DeviceInfo {
537    /// Minimum descriptor length, in 64-bit words.
538    pub min_descriptor_length: u8,
539    /// Accepted descriptor version.
540    pub descriptor_version: u8,
541    /// Device base info.
542    pub base_info: DeviceBaseInfo,
543}
544
545/// Basic session configuration that can be given to [`DeviceInfo`] to generate
546/// [`Config`]s.
547#[derive(Debug, Copy, Clone)]
548pub struct DerivableConfig {
549    /// The desired default buffer length for the session.
550    pub default_buffer_length: usize,
551    /// Enable rx lease watching.
552    pub watch_rx_leases: bool,
553    /// Enable multi VMO Tx.
554    pub multi_vmo: bool,
555    /// Sampling interval for the buffer usage estimator.
556    pub buffer_usage_sample_interval: std::time::Duration,
557}
558
559impl DerivableConfig {
560    /// A sensibly common default buffer length to be used in
561    /// [`DerivableConfig`]. Provided to ease test writing.
562    ///
563    /// Chosen to be the next power of two after the default Ethernet MTU.
564    ///
565    /// This is the value of the buffer length in the `Default` impl.
566    pub const DEFAULT_BUFFER_LENGTH: usize = 2048;
567    /// The value returned by the `Default` impl.
568    pub const DEFAULT: Self = Self {
569        default_buffer_length: Self::DEFAULT_BUFFER_LENGTH,
570        watch_rx_leases: false,
571        multi_vmo: false,
572        buffer_usage_sample_interval: std::time::Duration::from_secs(1),
573    };
574}
575
576impl Default for DerivableConfig {
577    fn default() -> Self {
578        Self::DEFAULT
579    }
580}
581
582impl DeviceInfo {
583    /// Create a new session config from the device information.
584    ///
585    /// This method also does the boundary checks so that data_length/offset fields read
586    /// from descriptors are safe to convert to [`usize`].
587    pub fn make_config(&self, config: DerivableConfig) -> Result<Config> {
588        let DeviceInfo {
589            min_descriptor_length,
590            descriptor_version,
591            base_info:
592                DeviceBaseInfo {
593                    rx_depth,
594                    tx_depth,
595                    buffer_alignment,
596                    max_buffer_length,
597                    min_rx_buffer_length,
598                    min_tx_buffer_length,
599                    min_tx_buffer_head,
600                    min_tx_buffer_tail,
601                    max_buffer_parts: _,
602                    min_rx_buffers,
603                },
604        } = self;
605        if NETWORK_DEVICE_DESCRIPTOR_VERSION != *descriptor_version {
606            return Err(Error::Config(format!(
607                "descriptor version mismatch: {} != {}",
608                NETWORK_DEVICE_DESCRIPTOR_VERSION, descriptor_version
609            )));
610        }
611        if NETWORK_DEVICE_DESCRIPTOR_LENGTH < usize::from(*min_descriptor_length) {
612            return Err(Error::Config(format!(
613                "descriptor length too small: {} < {}",
614                NETWORK_DEVICE_DESCRIPTOR_LENGTH, min_descriptor_length
615            )));
616        }
617
618        let DerivableConfig {
619            default_buffer_length,
620            watch_rx_leases,
621            multi_vmo,
622            buffer_usage_sample_interval,
623        } = config;
624
625        let num_rx_buffers =
626            NonZeroU16::new(*rx_depth).ok_or_else(|| Error::Config("no RX buffers".to_owned()))?;
627        let num_tx_buffers =
628            NonZeroU16::new(*tx_depth).ok_or_else(|| Error::Config("no TX buffers".to_owned()))?;
629
630        let max_buffer_length = max_buffer_length
631            .and_then(|max| {
632                // The error case is the case where max_buffer_length can't fix in a
633                // usize, but we use it to compare it to usizes, so that's
634                // equivalent to no limit.
635                usize::try_from(max.get()).ok_checked::<TryFromIntError>()
636            })
637            .unwrap_or(usize::MAX);
638        let min_buffer_length = usize::try_from(*min_rx_buffer_length)
639            .ok_checked::<TryFromIntError>()
640            .unwrap_or(usize::MAX);
641
642        let buffer_length =
643            usize::min(max_buffer_length, usize::max(min_buffer_length, default_buffer_length));
644
645        let buffer_alignment = usize::try_from(*buffer_alignment).map_err(
646            |std::num::TryFromIntError { .. }| {
647                Error::Config(format!(
648                    "buffer_alignment not representable within usize: {}",
649                    buffer_alignment,
650                ))
651            },
652        )?;
653
654        let buffer_stride = buffer_length
655            .checked_add(buffer_alignment - 1)
656            .map(|x| x / buffer_alignment * buffer_alignment)
657            .ok_or_else(|| {
658                Error::Config(format!(
659                    "not possible to align {} to {} under usize::MAX",
660                    buffer_length, buffer_alignment,
661                ))
662            })?;
663
664        if buffer_stride < buffer_length {
665            return Err(Error::Config(format!(
666                "buffer stride too small {} < {}",
667                buffer_stride, buffer_length
668            )));
669        }
670
671        if buffer_length < usize::from(*min_tx_buffer_head) + usize::from(*min_tx_buffer_tail) {
672            return Err(Error::Config(format!(
673                "buffer length {} does not meet minimum tx buffer head/tail requirement {}/{}",
674                buffer_length, min_tx_buffer_head, min_tx_buffer_tail,
675            )));
676        }
677
678        let num_buffers = num_rx_buffers
679            .get()
680            .checked_add(num_tx_buffers.get())
681            .filter(|num| *num != u16::MAX)
682            .ok_or_else(|| {
683                Error::Config(format!(
684                    "too many buffers requested: {} + {} > u16::MAX",
685                    num_rx_buffers, num_tx_buffers
686                ))
687            })?;
688
689        let buffer_stride =
690            u64::try_from(buffer_stride).map_err(|std::num::TryFromIntError { .. }| {
691                Error::Config(format!("buffer_stride too big: {} > u64::MAX", buffer_stride))
692            })?;
693
694        // This is following the practice of rust stdlib to ensure allocation
695        // size never reaches isize::MAX.
696        // https://doc.rust-lang.org/std/primitive.pointer.html#method.add-1.
697        match buffer_stride.checked_mul(num_buffers.into()).map(isize::try_from) {
698            None | Some(Err(std::num::TryFromIntError { .. })) => {
699                return Err(Error::Config(format!(
700                    "too much memory required for the buffers: {} * {} > isize::MAX",
701                    buffer_stride, num_buffers
702                )));
703            }
704            Some(Ok(_total)) => (),
705        };
706
707        let buffer_stride = NonZeroU64::new(buffer_stride)
708            .ok_or_else(|| Error::Config("buffer_stride is zero".to_owned()))?;
709
710        let min_tx_data = match usize::try_from(*min_tx_buffer_length)
711            .map(|min_tx| (min_tx <= buffer_length).then_some(min_tx))
712        {
713            Ok(Some(min_tx_buffer_length)) => min_tx_buffer_length,
714            // Either the conversion or the comparison failed.
715            Ok(None) | Err(std::num::TryFromIntError { .. }) => {
716                return Err(Error::Config(format!(
717                    "buffer_length smaller than minimum TX requirement: {} < {}",
718                    buffer_length, *min_tx_buffer_length
719                )));
720            }
721        };
722
723        let mut options = netdev::SessionFlags::empty();
724        options.set(netdev::SessionFlags::RECEIVE_RX_POWER_LEASES, watch_rx_leases);
725
726        let page_size = u64::from(zx::system_get_page_size());
727        let min_buffers_per_vmo = u16::try_from(std::cmp::max(1, page_size / buffer_stride.get()))
728            .map_err(|TryFromIntError { .. }| {
729                Error::Config("Too many buffers per VMO".to_owned())
730            })?;
731
732        let allocate_vmos =
733            |total_depth: u16, min_buffers: u16, start_vmo_id: u8| -> Result<Vec<VmoConfig>> {
734                let mut vmos = Vec::new();
735                let mut current_buffers = min_buffers;
736                let mut total_allocated = 0u16;
737                let mut vmo_id = start_vmo_id;
738                let mut vmos_allocated = 0;
739                while total_allocated < total_depth {
740                    let num_buffers = std::cmp::min(current_buffers, total_depth - total_allocated);
741                    vmos.push(VmoConfig { vmo_id, num_buffers });
742                    total_allocated += num_buffers;
743                    vmo_id = vmo_id
744                        .checked_add(1)
745                        .ok_or_else(|| Error::Config("too many vmos".to_string()))?;
746                    vmos_allocated += 1;
747                    if vmos_allocated > 1 {
748                        current_buffers = current_buffers.saturating_mul(2);
749                    }
750                }
751                Ok(vmos)
752            };
753
754        let (rx_vmos, tx_vmos) = if multi_vmo {
755            let min_rx_buffers = min_rx_buffers.unwrap_or(num_rx_buffers);
756            let rx_vmos =
757                allocate_vmos(num_rx_buffers.get(), min_rx_buffers.get(), DEFAULT_VMO_ID)?;
758            let next_vmo_id = u8::try_from(rx_vmos.len())
759                .map_err(|_| Error::Config("too many rx vmos".to_string()))?;
760            let tx_vmos = allocate_vmos(num_tx_buffers.get(), min_buffers_per_vmo, next_vmo_id)?;
761            (rx_vmos, tx_vmos)
762        } else {
763            (
764                vec![VmoConfig { vmo_id: DEFAULT_VMO_ID, num_buffers: num_rx_buffers.get() }],
765                vec![VmoConfig { vmo_id: DEFAULT_VMO_ID, num_buffers: num_tx_buffers.get() }],
766            )
767        };
768
769        Ok(Config {
770            buffer_stride,
771            rx_vmos,
772            tx_vmos,
773            options,
774            buffer_layout: BufferLayout {
775                length: buffer_length,
776                min_tx_head: *min_tx_buffer_head,
777                min_tx_tail: *min_tx_buffer_tail,
778                min_tx_data,
779            },
780            buffer_usage_sample_interval,
781        })
782    }
783}
784
785/// A port of the device.
786#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
787pub struct Port {
788    pub(crate) base: u8,
789    pub(crate) salt: u8,
790}
791
792impl TryFrom<netdev::PortId> for Port {
793    type Error = Error;
794    fn try_from(netdev::PortId { base, salt }: netdev::PortId) -> Result<Self> {
795        if base <= netdev::MAX_PORTS {
796            Ok(Self { base, salt })
797        } else {
798            Err(Error::InvalidPortId(base))
799        }
800    }
801}
802
803impl From<Port> for netdev::PortId {
804    fn from(Port { base, salt }: Port) -> Self {
805        Self { base, salt }
806    }
807}
808
809/// Pending descriptors to be sent to driver.
810struct Pending<K: AllocKind> {
811    storage: Vec<DescId<K>>,
812    waker: Option<Waker>,
813}
814
815impl<K: AllocKind> Pending<K> {
816    fn new(descs: Vec<DescId<K>>) -> Self {
817        Self { storage: descs, waker: None }
818    }
819
820    /// Extends the pending descriptors buffer.
821    fn extend(&mut self, descs: impl IntoIterator<Item = DescId<K>>) {
822        let Self { storage, waker } = self;
823        storage.extend(descs);
824        if let Some(waker) = waker.take() {
825            waker.wake();
826        }
827    }
828
829    /// Submits the pending buffer to the driver through [`zx::Fifo`].
830    ///
831    /// It will return [`Poll::Pending`] if any of the following happens:
832    ///   - There are no descriptors pending.
833    ///   - The fifo is not ready for write.
834    fn poll_submit(
835        &mut self,
836        fifo: &fasync::Fifo<DescId<K>>,
837        cx: &mut Context<'_>,
838    ) -> Poll<Result<usize>> {
839        let Self { storage, waker } = self;
840        if storage.is_empty() {
841            if waker.as_ref().is_none_or(|waker| !waker.will_wake(cx.waker())) {
842                *waker = Some(cx.waker().clone());
843            }
844            return Poll::Pending;
845        }
846
847        // TODO(https://fxbug.dev/42107145): We're assuming that writing to the
848        // FIFO here is a sufficient memory barrier for the other end to access
849        // the data. That is currently true but not really guaranteed by the
850        // API.
851        let submitted = ready!(fifo.try_write(cx, &storage[..]))
852            .map_err(|status| Error::Fifo("write", K::REFL.as_str(), status))?
853            .get();
854        let _drained = storage.drain(0..submitted);
855        Poll::Ready(Ok(submitted))
856    }
857}
858
859/// Intermediate storage used to batch-receive rx buffers from a session.
860///
861/// Instances are constructed by calling [`Session::new_rx_ready_storage`].
862pub struct RxReadyStorage {
863    inner: ReadyStorage<DescId<Rx>>,
864}
865
866impl RxReadyStorage {
867    /// Creates an [`RxReadyStorage`] that can hold `capacity` rx descriptor
868    /// IDs.
869    pub fn new(capacity: usize) -> Self {
870        Self { inner: ReadyStorage::new(capacity) }
871    }
872
873    fn poll_fifo(
874        &mut self,
875        cx: &mut Context<'_>,
876        fifo: &fasync::Fifo<DescId<Rx>>,
877    ) -> Poll<std::result::Result<(), zx::Status>> {
878        let Self { inner } = self;
879        inner.poll_fifo(cx, fifo)
880    }
881
882    fn drain(&mut self) -> Drain<'_, DescId<Rx>> {
883        let Self { inner } = self;
884        inner.drain()
885    }
886}
887
888/// Intermediate storage used to reduce syscall overhead by acting as a proxy to
889/// read entries from a FIFO.
890///
891/// `ReadyStorage` caches read entries from a FIFO in pre-allocated memory.
892pub(in crate::session) struct ReadyStorage<T> {
893    // NB: A boxed slice of `MaybeUninit` here allows us to give a transparent
894    // memory layout to the FIFO object but still move objects out of storage
895    // without needing a `T: Default` implementation. There's a small added
896    // benefit of not paying for memory initialization on creation as well, but
897    // that's mostly negligible given all allocation is performed upfront.
898    data: Box<[MaybeUninit<T>]>,
899    available: Range<usize>,
900}
901
902impl<T> Drop for ReadyStorage<T> {
903    fn drop(&mut self) {
904        let _ = self.drain();
905    }
906}
907
908struct Drain<'a, T> {
909    ready: &'a mut ReadyStorage<T>,
910}
911
912impl<'a, T> Iterator for Drain<'a, T> {
913    type Item = T;
914
915    fn next(&mut self) -> Option<Self::Item> {
916        let Self { ready: ReadyStorage { data, available: Range { start, end } } } = self;
917        if start != end {
918            let desc = std::mem::replace(&mut data[*start], MaybeUninit::uninit());
919            *start += 1;
920            // SAFETY: Descriptor was in the initialized section, it was
921            // initialized.
922            Some(unsafe { desc.assume_init() })
923        } else {
924            None
925        }
926    }
927
928    fn size_hint(&self) -> (usize, Option<usize>) {
929        let len = self.ready.available.len();
930        (len, Some(len))
931    }
932}
933
934impl<'a, T> ExactSizeIterator for Drain<'a, T> {}
935
936impl<'a, T> Drop for Drain<'a, T> {
937    fn drop(&mut self) {
938        let Self { ready: ReadyStorage { data, available } } = self;
939        let range = available.clone();
940        // TODO(https://github.com/rust-lang/rust/issues/63569): When slice of
941        // MaybeUninit is stabilized we can just drop the whole slice.
942        for initialized in &mut data[range] {
943            // SAFETY: the available range keeps track of initialized buffers,
944            // we must drop them on drop to uphold `MaybeUninit` expectations.
945            unsafe { initialized.assume_init_drop() }
946        }
947        *available = 0..0;
948    }
949}
950
951impl<T> ReadyStorage<T> {
952    pub(crate) fn new(capacity: usize) -> Self {
953        let data = std::iter::from_fn(|| Some(MaybeUninit::uninit())).take(capacity).collect();
954        Self { data, available: 0..0 }
955    }
956
957    fn poll_fifo(
958        &mut self,
959        cx: &mut Context<'_>,
960        fifo: &fasync::Fifo<T>,
961    ) -> Poll<std::result::Result<(), zx::Status>>
962    where
963        T: fasync::FifoEntry,
964    {
965        let Self { data, available: Range { start, end } } = self;
966        if *start == *end {
967            let count: NonZeroUsize = ready!(fifo.try_read(cx, &mut data[..]))?;
968            *start = 0;
969            *end = count.get();
970        }
971        Poll::Ready(Ok(()))
972    }
973
974    /// Returns an iterator that drains all of the elements from the buffer,
975    /// even if dropped.
976    fn drain(&mut self) -> Drain<'_, T> {
977        Drain { ready: self }
978    }
979}
980
981struct TxIdleListeners {
982    event: event_listener::Event,
983    tx_in_flight: AtomicUsize,
984}
985
986impl TxIdleListeners {
987    fn new() -> Self {
988        Self { event: event_listener::Event::new(), tx_in_flight: AtomicUsize::new(0) }
989    }
990
991    /// Decreases the number of outstanding tx buffers by `count`.
992    ///
993    /// Notifies any tx idle listeners if the number reaches 0.
994    fn tx_complete(&self, count: usize) {
995        let Self { event, tx_in_flight } = self;
996        let old_value = tx_in_flight.fetch_sub(count, atomic::Ordering::SeqCst);
997        debug_assert!(old_value >= count);
998        if old_value == count {
999            let _notified: usize = event.notify(usize::MAX);
1000        }
1001    }
1002
1003    /// Increases the number of outstanding tx buffers by 1.
1004    fn tx_started(&self) {
1005        let Self { event: _, tx_in_flight } = self;
1006        let _: usize = tx_in_flight.fetch_add(1, atomic::Ordering::SeqCst);
1007    }
1008
1009    async fn wait(&self) {
1010        let Self { event, tx_in_flight } = self;
1011        // This is _the correct way_ of holding an `event_listener::Listener`.
1012        // We check the condition before installing the listener in the fast
1013        // case, then we must check the condition again after creating the
1014        // listener in case we've raced with the condition updating. Finally we
1015        // must loop and check the condition again because we're not fully
1016        // guaranteed to not have spurious wakeups.
1017        //
1018        // See the event_listener crate documentation for more details.
1019        loop {
1020            if tx_in_flight.load(atomic::Ordering::SeqCst) == 0 {
1021                return;
1022            }
1023
1024            event_listener::listener!(event => listener);
1025
1026            if tx_in_flight.load(atomic::Ordering::SeqCst) == 0 {
1027                return;
1028            }
1029
1030            listener.await;
1031        }
1032    }
1033}
1034
1035/// An RAII lease possibly keeping the system from suspension.
1036///
1037/// Yielded from [`Session::watch_rx_leases`].
1038///
1039/// Dropping an `RxLease` relinquishes it.
1040#[derive(Debug)]
1041pub struct RxLease {
1042    handle: netdev::DelegatedRxLeaseHandle,
1043}
1044
1045impl Drop for RxLease {
1046    fn drop(&mut self) {
1047        let Self { handle } = self;
1048        // Change detector in case we need any evolution on how to relinquish
1049        // leases.
1050        match handle {
1051            netdev::DelegatedRxLeaseHandle::Channel(_channel) => {
1052                // Dropping the channel is enough to relinquish the lease.
1053            }
1054            netdev::DelegatedRxLeaseHandle::Eventpair(_eventpair) => {
1055                // Dropping the eventpair is enough to relinquish the lease.
1056            }
1057            netdev::DelegatedRxLeaseHandle::__SourceBreaking { .. } => {}
1058        }
1059    }
1060}
1061
1062impl RxLease {
1063    /// Creates a lease from a delegated lease handle.
1064    pub fn new(handle: netdev::DelegatedRxLeaseHandle) -> Self {
1065        Self { handle }
1066    }
1067
1068    /// Peeks the internal lease.
1069    pub fn inner(&self) -> &netdev::DelegatedRxLeaseHandle {
1070        &self.handle
1071    }
1072}
1073
1074impl From<netdev::DelegatedRxLeaseHandle> for RxLease {
1075    fn from(handle: netdev::DelegatedRxLeaseHandle) -> Self {
1076        Self::new(handle)
1077    }
1078}
1079
1080#[cfg(test)]
1081mod tests {
1082    use super::*;
1083    use std::num::NonZeroU32;
1084    use std::ops::Deref;
1085    use std::sync::Arc;
1086    use std::task::Poll;
1087
1088    use assert_matches::assert_matches;
1089    use fuchsia_async::Fifo;
1090    use test_case::test_case;
1091    use zerocopy::{FromBytes, Immutable, IntoBytes};
1092
1093    use crate::session::DerivableConfig;
1094
1095    use super::buffer::{
1096        AllocKind, DescId, NETWORK_DEVICE_DESCRIPTOR_LENGTH, NETWORK_DEVICE_DESCRIPTOR_VERSION,
1097    };
1098    use super::{
1099        BufferLayout, BufferUsageEstimator, Config, DeviceBaseInfo, DeviceInfo, Error, Inner,
1100        Mutex, Pool, ReadyStorage, Task, TxIdleListeners, TxState,
1101    };
1102
1103    impl Inner {
1104        pub(super) fn new_test(
1105            pool: Arc<Pool>,
1106            proxy: netdev::SessionProxy,
1107            name: String,
1108            rx: Fifo<DescId<Rx>>,
1109            tx: Fifo<DescId<Tx>>,
1110            num_rx_buffers: usize,
1111            tx_ready: Mutex<ReadyStorage<DescId<Tx>>>,
1112            tx_idle_listeners: TxIdleListeners,
1113            tx_state: Mutex<TxState>,
1114        ) -> Self {
1115            Self {
1116                pool,
1117                proxy,
1118                name,
1119                rx,
1120                tx,
1121                num_rx_buffers,
1122                tx_ready,
1123                tx_idle_listeners,
1124                tx_state,
1125            }
1126        }
1127
1128        pub(super) fn tx_state(&self) -> &Mutex<TxState> {
1129            &self.tx_state
1130        }
1131
1132        pub(super) fn pool(&self) -> &Arc<Pool> {
1133            &self.pool
1134        }
1135    }
1136
1137    impl Task {
1138        pub(super) fn new_test(
1139            inner: Arc<Inner>,
1140            sample_interval: Option<std::time::Duration>,
1141        ) -> Self {
1142            let buffer_usage_sampler = sample_interval
1143                .map(|i| (fasync::Interval::new(i.into()), BufferUsageEstimator::new()));
1144            Self { inner, buffer_usage_sampler }
1145        }
1146    }
1147
1148    pub(super) const DEFAULT_DEVICE_BASE_INFO: DeviceBaseInfo = DeviceBaseInfo {
1149        rx_depth: 1,
1150        tx_depth: 1,
1151        buffer_alignment: 1,
1152        max_buffer_length: None,
1153        min_rx_buffer_length: 0,
1154        min_tx_buffer_head: 0,
1155        min_tx_buffer_length: 0,
1156        min_tx_buffer_tail: 0,
1157        max_buffer_parts: fidl_fuchsia_hardware_network::MAX_DESCRIPTOR_CHAIN,
1158        min_rx_buffers: None,
1159    };
1160
1161    pub(super) const DEFAULT_DEVICE_INFO: DeviceInfo = DeviceInfo {
1162        min_descriptor_length: 0,
1163        descriptor_version: 1,
1164        base_info: DEFAULT_DEVICE_BASE_INFO,
1165    };
1166
1167    const DEFAULT_BUFFER_LENGTH: usize = 2048;
1168
1169    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1170        min_descriptor_length: u8::MAX,
1171        ..DEFAULT_DEVICE_INFO
1172    }, format!("descriptor length too small: {} < {}", NETWORK_DEVICE_DESCRIPTOR_LENGTH, u8::MAX))]
1173    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1174        descriptor_version: 42,
1175        ..DEFAULT_DEVICE_INFO
1176    }, format!("descriptor version mismatch: {} != {}", NETWORK_DEVICE_DESCRIPTOR_VERSION, 42))]
1177    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1178        base_info: DeviceBaseInfo {
1179            tx_depth: 0,
1180            ..DEFAULT_DEVICE_BASE_INFO
1181        },
1182        ..DEFAULT_DEVICE_INFO
1183    }, "no TX buffers")]
1184    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1185        base_info: DeviceBaseInfo {
1186            rx_depth: 0,
1187            ..DEFAULT_DEVICE_BASE_INFO
1188        },
1189        ..DEFAULT_DEVICE_INFO
1190    }, "no RX buffers")]
1191    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1192        base_info: DeviceBaseInfo {
1193            tx_depth: u16::MAX,
1194            rx_depth: u16::MAX,
1195            ..DEFAULT_DEVICE_BASE_INFO
1196        },
1197        ..DEFAULT_DEVICE_INFO
1198    }, format!("too many buffers requested: {} + {} > u16::MAX", u16::MAX, u16::MAX))]
1199    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1200        base_info: DeviceBaseInfo {
1201            min_tx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 + 1,
1202            ..DEFAULT_DEVICE_BASE_INFO
1203        },
1204        ..DEFAULT_DEVICE_INFO
1205    }, format!(
1206        "buffer_length smaller than minimum TX requirement: {} < {}",
1207        DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
1208    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1209        base_info: DeviceBaseInfo {
1210            min_tx_buffer_head: DEFAULT_BUFFER_LENGTH as u16 + 1,
1211            ..DEFAULT_DEVICE_BASE_INFO
1212        },
1213        ..DEFAULT_DEVICE_INFO
1214    }, format!(
1215        "buffer length {} does not meet minimum tx buffer head/tail requirement {}/0",
1216        DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
1217    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
1218        base_info: DeviceBaseInfo {
1219            min_tx_buffer_tail: DEFAULT_BUFFER_LENGTH as u16 + 1,
1220            ..DEFAULT_DEVICE_BASE_INFO
1221        },
1222        ..DEFAULT_DEVICE_INFO
1223    }, format!(
1224        "buffer length {} does not meet minimum tx buffer head/tail requirement 0/{}",
1225        DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
1226    #[test_case(0, DEFAULT_DEVICE_INFO, "buffer_stride is zero")]
1227    #[test_case(usize::MAX, DEFAULT_DEVICE_INFO,
1228    format!(
1229        "too much memory required for the buffers: {} * {} > isize::MAX",
1230        usize::MAX, 2))]
1231    #[test_case(usize::MAX, DeviceInfo {
1232        base_info: DeviceBaseInfo {
1233            buffer_alignment: 2,
1234            ..DEFAULT_DEVICE_BASE_INFO
1235        },
1236        ..DEFAULT_DEVICE_INFO
1237    }, format!(
1238        "not possible to align {} to {} under usize::MAX",
1239        usize::MAX, 2))]
1240    fn configs_from_device_info_err(
1241        buffer_length: usize,
1242        info: DeviceInfo,
1243        expected: impl Deref<Target = str>,
1244    ) {
1245        let config = DerivableConfig { default_buffer_length: buffer_length, ..Default::default() };
1246        assert_matches!(
1247            info.make_config(config),
1248            Err(Error::Config(got)) if got.as_str() == expected.deref()
1249        );
1250    }
1251
1252    #[test_case(DeviceInfo {
1253        base_info: DeviceBaseInfo {
1254            min_rx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 + 1,
1255            ..DEFAULT_DEVICE_BASE_INFO
1256        },
1257        ..DEFAULT_DEVICE_INFO
1258    }, DEFAULT_BUFFER_LENGTH + 1; "default below min")]
1259    #[test_case(DeviceInfo {
1260        base_info: DeviceBaseInfo {
1261            max_buffer_length: NonZeroU32::new(DEFAULT_BUFFER_LENGTH as u32 - 1),
1262            ..DEFAULT_DEVICE_BASE_INFO
1263        },
1264        ..DEFAULT_DEVICE_INFO
1265    }, DEFAULT_BUFFER_LENGTH - 1; "default above max")]
1266    #[test_case(DeviceInfo {
1267        base_info: DeviceBaseInfo {
1268            min_rx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 - 1,
1269            max_buffer_length: NonZeroU32::new(DEFAULT_BUFFER_LENGTH as u32 + 1),
1270            ..DEFAULT_DEVICE_BASE_INFO
1271        },
1272        ..DEFAULT_DEVICE_INFO
1273    }, DEFAULT_BUFFER_LENGTH; "default in bounds")]
1274    fn configs_from_device_buffer_length(info: DeviceInfo, expected_length: usize) {
1275        let config = info
1276            .make_config(DerivableConfig {
1277                default_buffer_length: DEFAULT_BUFFER_LENGTH,
1278                ..Default::default()
1279            })
1280            .expect("is valid");
1281        let Config {
1282            buffer_layout: BufferLayout { length, min_tx_data: _, min_tx_head: _, min_tx_tail: _ },
1283            buffer_stride: _,
1284            rx_vmos: _,
1285            tx_vmos: _,
1286            options: _,
1287            buffer_usage_sample_interval: _,
1288        } = config;
1289        assert_eq!(length, expected_length);
1290    }
1291
1292    #[test]
1293    fn multi_vmo_allocation_scheme() {
1294        let info = DeviceInfo {
1295            base_info: DeviceBaseInfo { rx_depth: 16, tx_depth: 16, ..DEFAULT_DEVICE_BASE_INFO },
1296            ..DEFAULT_DEVICE_INFO
1297        };
1298        let config = info
1299            .make_config(DerivableConfig { multi_vmo: true, ..Default::default() })
1300            .expect("is valid");
1301        let Config { rx_vmos, tx_vmos, .. } = config;
1302        let expected_rx = vec![VmoConfig { vmo_id: 0, num_buffers: 16 }];
1303        let expected_tx = vec![
1304            VmoConfig { vmo_id: 1, num_buffers: 2 },
1305            VmoConfig { vmo_id: 2, num_buffers: 2 },
1306            VmoConfig { vmo_id: 3, num_buffers: 4 },
1307            VmoConfig { vmo_id: 4, num_buffers: 8 },
1308        ];
1309        assert_eq!(rx_vmos, expected_rx);
1310        assert_eq!(tx_vmos, expected_tx);
1311    }
1312
1313    #[test]
1314    fn multi_vmo_allocation_scheme_with_min_rx_buffers() {
1315        let info = DeviceInfo {
1316            base_info: DeviceBaseInfo {
1317                rx_depth: 16,
1318                tx_depth: 16,
1319                min_rx_buffers: NonZeroU16::new(2),
1320                ..DEFAULT_DEVICE_BASE_INFO
1321            },
1322            ..DEFAULT_DEVICE_INFO
1323        };
1324        let config = info
1325            .make_config(DerivableConfig { multi_vmo: true, ..Default::default() })
1326            .expect("is valid");
1327        let Config { rx_vmos, tx_vmos, .. } = config;
1328        let expected_rx = vec![
1329            VmoConfig { vmo_id: 0, num_buffers: 2 },
1330            VmoConfig { vmo_id: 1, num_buffers: 2 },
1331            VmoConfig { vmo_id: 2, num_buffers: 4 },
1332            VmoConfig { vmo_id: 3, num_buffers: 8 },
1333        ];
1334        let expected_tx = vec![
1335            VmoConfig { vmo_id: 4, num_buffers: 2 },
1336            VmoConfig { vmo_id: 5, num_buffers: 2 },
1337            VmoConfig { vmo_id: 6, num_buffers: 4 },
1338            VmoConfig { vmo_id: 7, num_buffers: 8 },
1339        ];
1340        assert_eq!(rx_vmos, expected_rx);
1341        assert_eq!(tx_vmos, expected_tx);
1342    }
1343
1344    pub(super) fn make_fifos<K: AllocKind>() -> (Fifo<DescId<K>>, zx::Fifo<DescId<K>>) {
1345        let (handle, other_end) = zx::Fifo::create(256).unwrap();
1346        (Fifo::from_fifo(handle), other_end)
1347    }
1348
1349    fn remove_rights<T: FromBytes + IntoBytes + Immutable>(
1350        fifo: Fifo<T>,
1351        rights_to_remove: zx::Rights,
1352    ) -> Fifo<T> {
1353        let fifo = zx::Fifo::from(fifo);
1354        let rights = fifo.as_handle_ref().basic_info().expect("can retrieve info").rights;
1355
1356        let fifo = fifo.replace_handle(rights ^ rights_to_remove).expect("can replace");
1357        Fifo::from_fifo(fifo)
1358    }
1359
1360    enum TxOrRx {
1361        Tx,
1362        Rx,
1363    }
1364    #[test_case(TxOrRx::Tx, zx::Rights::READ; "tx read")]
1365    #[test_case(TxOrRx::Tx, zx::Rights::WRITE; "tx write")]
1366    #[test_case(TxOrRx::Rx, zx::Rights::WRITE; "rx read")]
1367    #[fuchsia::test]
1368    async fn task_as_future_poll_error(which_fifo: TxOrRx, right_to_remove: zx::Rights) {
1369        // This is a regression test for https://fxbug.dev/42072513. The flake
1370        // that caused that bug occurred because the Zircon channel was closed
1371        // but the error returned by a failed attempt to write to it wasn't
1372        // being propagated upwards. This test produces a similar situation by
1373        // altering the right on the FIFOs the task uses so as to cause either
1374        // an attempt to write or to read to fail. For completeness, it
1375        // exercises all the FIFO polls that comprise Task::poll.
1376        let config = DEFAULT_DEVICE_INFO
1377            .make_config(DerivableConfig {
1378                default_buffer_length: DEFAULT_BUFFER_LENGTH,
1379                ..Default::default()
1380            })
1381            .expect("is valid");
1382        let CreatedPool { pool, descriptors_vmo: _descriptors_vmo, data_vmos: _data_vmos } =
1383            Pool::new(config).expect("is valid");
1384        let (session_proxy, _session_server) =
1385            fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::SessionMarker>();
1386
1387        let (rx, _rx_sender) = make_fifos();
1388        let (tx, _tx_receiver) = make_fifos();
1389
1390        // Attenuate rights on one of the FIFOs.
1391        let (tx, rx) = match which_fifo {
1392            TxOrRx::Tx => (remove_rights(tx, right_to_remove), rx),
1393            TxOrRx::Rx => (tx, remove_rights(rx, right_to_remove)),
1394        };
1395
1396        let tx_state = Mutex::new(TxState::new(vec![]));
1397
1398        let buf = pool.alloc_tx_buffer(1).await.expect("can allocate");
1399        let inner = Arc::new(Inner {
1400            pool,
1401            proxy: session_proxy,
1402            name: "fake_task".to_string(),
1403            rx,
1404            tx,
1405            num_rx_buffers: 10,
1406            tx_ready: Mutex::new(ReadyStorage::new(10)),
1407            tx_idle_listeners: TxIdleListeners::new(),
1408            tx_state,
1409        });
1410
1411        inner.send(buf);
1412
1413        let task = Task { inner, buffer_usage_sampler: None };
1414        futures::pin_mut!(task);
1415
1416        // The task should not be able to continue because it can't read from or
1417        // write to one of the FIFOs.
1418        assert_matches!(futures::poll!(task.as_mut()), Poll::Ready(Err(Error::Fifo(_, _, _))));
1419    }
1420
1421    #[test_case(1; "drain first")]
1422    #[test_case(2; "drain first two")]
1423    #[test_case(3; "drain all")]
1424    #[fuchsia::test]
1425    async fn ready_storage_batch_iterator(drain_first_n: usize) {
1426        let (handle, fifo_server) = zx::Fifo::<u32>::create(256).unwrap();
1427        let fifo_client = Fifo::from_fifo(handle);
1428        let items = vec![10u32, 20u32, 30u32];
1429        let written = fifo_server.write(&items[..]).expect("write to fifo");
1430        assert_eq!(written.get(), 3);
1431
1432        let mut ready_storage = ReadyStorage::<u32>::new(10);
1433
1434        // First fetch reads all 3 items into ReadyStorage.
1435        poll_fn(|cx| ready_storage.poll_fifo(cx, &fifo_client)).await.expect("fetch from fifo");
1436
1437        let mut drain = ready_storage.drain();
1438        for i in 0..drain_first_n {
1439            assert_eq!(drain.next(), Some(items[i]));
1440        }
1441        if drain_first_n == items.len() {
1442            assert_eq!(drain.next(), None);
1443        }
1444        // Remaining uniterated items should be dropped and `available` reset to
1445        // `0..0`.
1446        std::mem::drop(drain);
1447
1448        // Since `available` is an empty range, `poll_fifo` should block on an
1449        // empty FIFO.
1450        let fetch_fut = poll_fn(|cx| ready_storage.poll_fifo(cx, &fifo_client));
1451        futures::pin_mut!(fetch_fut);
1452        assert_matches!(futures::poll!(fetch_fut), Poll::Pending);
1453    }
1454}