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