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;
8
9use std::fmt::Debug;
10use std::mem::MaybeUninit;
11use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, TryFromIntError};
12use std::ops::Range;
13use std::pin::Pin;
14use std::sync::Arc;
15use std::sync::atomic::{self, AtomicUsize};
16use std::task::Waker;
17
18use explicit::{PollExt as _, ResultExt as _};
19use fidl_fuchsia_hardware_network as netdev;
20use fidl_fuchsia_hardware_network::DelegatedRxLease;
21use fidl_table_validation::ValidFidlTable;
22use fuchsia_async as fasync;
23use fuchsia_sync::Mutex;
24use futures::future::{Future, poll_fn};
25use futures::task::{Context, Poll};
26use futures::{Stream, StreamExt as _, ready};
27
28use crate::error::{Error, Result};
29use buffer::pool::{Pool, RxLeaseWatcher};
30use buffer::{
31    AllocKind, DescId, NETWORK_DEVICE_DESCRIPTOR_LENGTH, NETWORK_DEVICE_DESCRIPTOR_VERSION,
32};
33pub use buffer::{Buffer, ChecksumRxOffloading, Rx, SinglePartTxBuffer, Tx};
34
35// TODO(https://fxbug.dev/438527741): This is the VMO ID used for single VMO
36// clients (Rx + Tx in the same VMO). When VMO split is applied everywhere,
37// remove this constant.
38const DEFAULT_VMO_ID: u8 = 0;
39
40/// A session between network device client and driver.
41#[derive(Clone)]
42pub struct Session {
43    inner: Arc<Inner>,
44}
45
46impl Debug for Session {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        let Self { inner } = self;
49        let Inner {
50            name,
51            pool: _,
52            proxy: _,
53            rx: _,
54            tx: _,
55            tx_pending: _,
56            rx_ready: _,
57            tx_ready: _,
58            tx_idle_listeners: _,
59        } = &**inner;
60        f.debug_struct("Session").field("name", &name).finish_non_exhaustive()
61    }
62}
63
64impl Session {
65    /// Creates a new session with the given `name` and `config`.
66    pub async fn new(
67        device: &netdev::DeviceProxy,
68        name: &str,
69        config: Config,
70    ) -> Result<(Self, Task)> {
71        let inner = Inner::new(device, name, config).await?;
72        Ok((Session { inner: Arc::clone(&inner) }, Task { inner }))
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    // Pending tx descriptors to be sent.
225    tx_pending: Pending<Tx>,
226    rx_ready: Mutex<ReadyBuffer<DescId<Rx>>>,
227    tx_ready: Mutex<ReadyBuffer<DescId<Tx>>>,
228    tx_idle_listeners: TxIdleListeners,
229}
230
231impl Inner {
232    /// Creates a new session.
233    async fn new(device: &netdev::DeviceProxy, name: &str, config: Config) -> Result<Arc<Self>> {
234        let (pool, descriptors, data) = Pool::new(config)?;
235
236        let session_info = {
237            // The following two constants are not provided by user, panic
238            // instead of returning an error.
239            let descriptor_length =
240                u8::try_from(NETWORK_DEVICE_DESCRIPTOR_LENGTH / std::mem::size_of::<u64>())
241                    .expect("descriptor length in 64-bit words not representable by u8");
242            let data = vec![fidl_fuchsia_hardware_network::DataVmo {
243                id: Some(DEFAULT_VMO_ID),
244                vmo: Some(data),
245                num_rx_buffers: Some(config.num_rx_buffers.get()),
246                __source_breaking: fidl::marker::SourceBreaking,
247            }];
248            netdev::SessionInfo {
249                descriptors: Some(descriptors),
250                data: Some(data),
251                descriptor_version: Some(NETWORK_DEVICE_DESCRIPTOR_VERSION),
252                descriptor_length: Some(descriptor_length),
253                descriptor_count: Some(config.num_tx_buffers.get() + config.num_rx_buffers.get()),
254                options: Some(config.options),
255                ..Default::default()
256            }
257        };
258
259        let (client, netdev::Fifos { rx, tx }) = device
260            .open_session(name, session_info)
261            .await?
262            .map_err(|raw| Error::Open(name.to_owned(), zx::Status::from_raw(raw)))?;
263        let proxy = client.into_proxy();
264
265        if config.num_tx_buffers.get() > 0 {
266            let (_successful, status) =
267                proxy.register_for_tx(&[DEFAULT_VMO_ID]).await.map_err(Error::Fidl)?;
268            zx::Status::ok(status).map_err(Error::RegisterForTx)?;
269        }
270
271        let rx = fasync::Fifo::from_fifo(rx);
272        let tx = fasync::Fifo::from_fifo(tx);
273
274        Ok(Arc::new(Self {
275            pool,
276            proxy,
277            name: name.to_owned(),
278            rx,
279            tx,
280            tx_pending: Pending::new(Vec::new()),
281            rx_ready: Mutex::new(ReadyBuffer::new(config.num_rx_buffers.get().into())),
282            tx_ready: Mutex::new(ReadyBuffer::new(config.num_tx_buffers.get().into())),
283            tx_idle_listeners: TxIdleListeners::new(),
284        }))
285    }
286
287    /// Polls to submit available rx descriptors from pool to driver.
288    ///
289    /// Returns the number of rx descriptors that are submitted.
290    fn poll_submit_rx(&self, cx: &mut Context<'_>) -> Poll<Result<usize>> {
291        self.pool.rx_pending.poll_submit(&self.rx, cx)
292    }
293
294    /// Polls completed rx descriptors from the driver.
295    ///
296    /// Returns the the head of a completed rx descriptor chain.
297    fn poll_complete_rx(&self, cx: &mut Context<'_>) -> Poll<Result<DescId<Rx>>> {
298        let mut rx_ready = self.rx_ready.lock();
299        rx_ready.poll_with_fifo(cx, &self.rx).map_err(|status| Error::Fifo("read", "rx", status))
300    }
301
302    /// Polls to submit tx descriptors that are pending to the driver.
303    ///
304    /// Returns the number of tx descriptors that are successfully submitted.
305    fn poll_submit_tx(&self, cx: &mut Context<'_>) -> Poll<Result<usize>> {
306        self.tx_pending.poll_submit(&self.tx, cx)
307    }
308
309    /// Polls completed tx descriptors from the driver then puts them in pool.
310    fn poll_complete_tx(&self, cx: &mut Context<'_>) -> Poll<Result<()>> {
311        let result = {
312            let mut tx_ready = self.tx_ready.lock();
313            // TODO(https://github.com/rust-lang/rust/issues/63569): Provide entire
314            // chain of completed descriptors to the pool at once when slice of
315            // MaybeUninit is stabilized.
316            tx_ready.poll_with_fifo(cx, &self.tx).map(|r| match r {
317                Ok(desc) => self.pool.tx_completed(desc),
318                Err(status) => Err(Error::Fifo("read", "tx", status)),
319            })
320        };
321
322        match &result {
323            Poll::Ready(Ok(())) => self.tx_idle_listeners.tx_complete(),
324            Poll::Pending | Poll::Ready(Err(_)) => {}
325        }
326        result
327    }
328
329    /// Sends the [`Buffer`] to the driver.
330    ///
331    /// Note: Transmit is completely infallible because the buffer layout and
332    /// zero-padding are already fully resolved and verified upfront during
333    /// buffer allocation (see `AllocGuard::init` in `pool.rs` for details
334    /// and design tradeoffs).
335    fn send(&self, buffer: Buffer<Tx>) {
336        self.tx_idle_listeners.tx_started();
337        self.tx_pending.extend(std::iter::once(buffer.leak()));
338    }
339
340    /// Receives a [`Buffer`] from the driver.
341    ///
342    /// Waits until there is completed rx buffers from the driver.
343    async fn recv(&self) -> Result<Buffer<Rx>> {
344        poll_fn(|cx| -> Poll<Result<Buffer<Rx>>> {
345            let head = ready!(self.poll_complete_rx(cx))?;
346            Poll::Ready(self.pool.rx_completed(head))
347        })
348        .await
349    }
350}
351
352/// The backing task that drives the session.
353///
354/// A session will stop making progress if this task is not polled continuously.
355#[must_use = "futures do nothing unless you `.await` or poll them"]
356pub struct Task {
357    inner: Arc<Inner>,
358}
359
360impl Future for Task {
361    type Output = Result<()>;
362    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
363        let inner = &Pin::into_inner(self).inner;
364        loop {
365            let mut all_pending = true;
366            // TODO(https://fxbug.dev/42158458): poll once for all completed
367            // descriptors if this becomes a performance bottleneck.
368            while inner.poll_complete_tx(cx)?.is_ready_checked::<()>() {
369                all_pending = false;
370            }
371            if inner.poll_submit_rx(cx)?.is_ready_checked::<usize>() {
372                all_pending = false;
373            }
374            if inner.poll_submit_tx(cx)?.is_ready_checked::<usize>() {
375                all_pending = false;
376            }
377            if all_pending {
378                return Poll::Pending;
379            }
380        }
381    }
382}
383
384/// Session configuration.
385#[derive(Debug, Clone, Copy)]
386pub struct Config {
387    /// Buffer stride on VMO, in bytes.
388    buffer_stride: NonZeroU64,
389    /// Number of rx descriptors to allocate.
390    num_rx_buffers: NonZeroU16,
391    /// Number of tx descriptors to allocate.
392    num_tx_buffers: NonZeroU16,
393    /// Session flags.
394    options: netdev::SessionFlags,
395    /// Buffer layout.
396    buffer_layout: BufferLayout,
397}
398
399/// Describes the buffer layout that [`Pool`] needs to know.
400#[derive(Debug, Clone, Copy)]
401struct BufferLayout {
402    /// Minimum tx buffer data length.
403    min_tx_data: usize,
404    /// Minimum tx buffer head length.
405    min_tx_head: u16,
406    /// Minimum tx buffer tail length.
407    min_tx_tail: u16,
408    /// The length of a buffer.
409    length: usize,
410}
411
412/// Network device base info with all required fields.
413#[derive(Debug, Clone, ValidFidlTable)]
414#[fidl_table_src(netdev::DeviceBaseInfo)]
415#[fidl_table_strict]
416pub struct DeviceBaseInfo {
417    /// Maximum number of items in rx FIFO (per session).
418    pub rx_depth: u16,
419    /// Maximum number of items in tx FIFO (per session).
420    pub tx_depth: u16,
421    /// Alignment requirement for buffers in the data VMO.
422    pub buffer_alignment: u32,
423    /// Maximum supported length of buffers in the data VMO, in bytes.
424    #[fidl_field_type(optional)]
425    pub max_buffer_length: Option<NonZeroU32>,
426    /// The minimum rx buffer length required for device.
427    pub min_rx_buffer_length: u32,
428    /// The minimum tx buffer length required for the device.
429    pub min_tx_buffer_length: u32,
430    /// The number of bytes the device requests be free as `head` space in a tx buffer.
431    pub min_tx_buffer_head: u16,
432    /// The amount of bytes the device requests be free as `tail` space in a tx buffer.
433    pub min_tx_buffer_tail: u16,
434    /// Maximum descriptor chain length accepted by the device.
435    pub max_buffer_parts: u8,
436    /// Minimum amount of Rx buffers the client needs to prepare for the
437    /// network device.
438    #[fidl_field_type(optional)]
439    pub min_rx_buffers: Option<NonZeroU16>,
440}
441
442/// Network device information with all required fields.
443#[derive(Debug, Clone, ValidFidlTable)]
444#[fidl_table_src(netdev::DeviceInfo)]
445#[fidl_table_strict]
446pub struct DeviceInfo {
447    /// Minimum descriptor length, in 64-bit words.
448    pub min_descriptor_length: u8,
449    /// Accepted descriptor version.
450    pub descriptor_version: u8,
451    /// Device base info.
452    pub base_info: DeviceBaseInfo,
453}
454
455/// Basic session configuration that can be given to [`DeviceInfo`] to generate
456/// [`Config`]s.
457#[derive(Debug, Copy, Clone)]
458pub struct DerivableConfig {
459    /// The desired default buffer length for the session.
460    pub default_buffer_length: usize,
461    /// Enable rx lease watching.
462    pub watch_rx_leases: bool,
463}
464
465impl DerivableConfig {
466    /// A sensibly common default buffer length to be used in
467    /// [`DerivableConfig`]. Provided to ease test writing.
468    ///
469    /// Chosen to be the next power of two after the default Ethernet MTU.
470    ///
471    /// This is the value of the buffer length in the `Default` impl.
472    pub const DEFAULT_BUFFER_LENGTH: usize = 2048;
473    /// The value returned by the `Default` impl.
474    pub const DEFAULT: Self =
475        Self { default_buffer_length: Self::DEFAULT_BUFFER_LENGTH, watch_rx_leases: false };
476}
477
478impl Default for DerivableConfig {
479    fn default() -> Self {
480        Self::DEFAULT
481    }
482}
483
484impl DeviceInfo {
485    /// Create a new session config from the device information.
486    ///
487    /// This method also does the boundary checks so that data_length/offset fields read
488    /// from descriptors are safe to convert to [`usize`].
489    pub fn make_config(&self, config: DerivableConfig) -> Result<Config> {
490        let DeviceInfo {
491            min_descriptor_length,
492            descriptor_version,
493            base_info:
494                DeviceBaseInfo {
495                    rx_depth,
496                    tx_depth,
497                    buffer_alignment,
498                    max_buffer_length,
499                    min_rx_buffer_length,
500                    min_tx_buffer_length,
501                    min_tx_buffer_head,
502                    min_tx_buffer_tail,
503                    max_buffer_parts: _,
504                    min_rx_buffers: _,
505                },
506        } = self;
507        if NETWORK_DEVICE_DESCRIPTOR_VERSION != *descriptor_version {
508            return Err(Error::Config(format!(
509                "descriptor version mismatch: {} != {}",
510                NETWORK_DEVICE_DESCRIPTOR_VERSION, descriptor_version
511            )));
512        }
513        if NETWORK_DEVICE_DESCRIPTOR_LENGTH < usize::from(*min_descriptor_length) {
514            return Err(Error::Config(format!(
515                "descriptor length too small: {} < {}",
516                NETWORK_DEVICE_DESCRIPTOR_LENGTH, min_descriptor_length
517            )));
518        }
519
520        let DerivableConfig { default_buffer_length, watch_rx_leases } = config;
521
522        let num_rx_buffers =
523            NonZeroU16::new(*rx_depth).ok_or_else(|| Error::Config("no RX buffers".to_owned()))?;
524        let num_tx_buffers =
525            NonZeroU16::new(*tx_depth).ok_or_else(|| Error::Config("no TX buffers".to_owned()))?;
526
527        let max_buffer_length = max_buffer_length
528            .and_then(|max| {
529                // The error case is the case where max_buffer_length can't fix in a
530                // usize, but we use it to compare it to usizes, so that's
531                // equivalent to no limit.
532                usize::try_from(max.get()).ok_checked::<TryFromIntError>()
533            })
534            .unwrap_or(usize::MAX);
535        let min_buffer_length = usize::try_from(*min_rx_buffer_length)
536            .ok_checked::<TryFromIntError>()
537            .unwrap_or(usize::MAX);
538
539        let buffer_length =
540            usize::min(max_buffer_length, usize::max(min_buffer_length, default_buffer_length));
541
542        let buffer_alignment = usize::try_from(*buffer_alignment).map_err(
543            |std::num::TryFromIntError { .. }| {
544                Error::Config(format!(
545                    "buffer_alignment not representable within usize: {}",
546                    buffer_alignment,
547                ))
548            },
549        )?;
550
551        let buffer_stride = buffer_length
552            .checked_add(buffer_alignment - 1)
553            .map(|x| x / buffer_alignment * buffer_alignment)
554            .ok_or_else(|| {
555                Error::Config(format!(
556                    "not possible to align {} to {} under usize::MAX",
557                    buffer_length, buffer_alignment,
558                ))
559            })?;
560
561        if buffer_stride < buffer_length {
562            return Err(Error::Config(format!(
563                "buffer stride too small {} < {}",
564                buffer_stride, buffer_length
565            )));
566        }
567
568        if buffer_length < usize::from(*min_tx_buffer_head) + usize::from(*min_tx_buffer_tail) {
569            return Err(Error::Config(format!(
570                "buffer length {} does not meet minimum tx buffer head/tail requirement {}/{}",
571                buffer_length, min_tx_buffer_head, min_tx_buffer_tail,
572            )));
573        }
574
575        let num_buffers =
576            rx_depth.checked_add(*tx_depth).filter(|num| *num != u16::MAX).ok_or_else(|| {
577                Error::Config(format!(
578                    "too many buffers requested: {} + {} > u16::MAX",
579                    rx_depth, tx_depth
580                ))
581            })?;
582
583        let buffer_stride =
584            u64::try_from(buffer_stride).map_err(|std::num::TryFromIntError { .. }| {
585                Error::Config(format!("buffer_stride too big: {} > u64::MAX", buffer_stride))
586            })?;
587
588        // This is following the practice of rust stdlib to ensure allocation
589        // size never reaches isize::MAX.
590        // https://doc.rust-lang.org/std/primitive.pointer.html#method.add-1.
591        match buffer_stride.checked_mul(num_buffers.into()).map(isize::try_from) {
592            None | Some(Err(std::num::TryFromIntError { .. })) => {
593                return Err(Error::Config(format!(
594                    "too much memory required for the buffers: {} * {} > isize::MAX",
595                    buffer_stride, num_buffers
596                )));
597            }
598            Some(Ok(_total)) => (),
599        };
600
601        let buffer_stride = NonZeroU64::new(buffer_stride)
602            .ok_or_else(|| Error::Config("buffer_stride is zero".to_owned()))?;
603
604        let min_tx_data = match usize::try_from(*min_tx_buffer_length)
605            .map(|min_tx| (min_tx <= buffer_length).then_some(min_tx))
606        {
607            Ok(Some(min_tx_buffer_length)) => min_tx_buffer_length,
608            // Either the conversion or the comparison failed.
609            Ok(None) | Err(std::num::TryFromIntError { .. }) => {
610                return Err(Error::Config(format!(
611                    "buffer_length smaller than minimum TX requirement: {} < {}",
612                    buffer_length, *min_tx_buffer_length
613                )));
614            }
615        };
616
617        let mut options = netdev::SessionFlags::empty();
618        options.set(netdev::SessionFlags::RECEIVE_RX_POWER_LEASES, watch_rx_leases);
619
620        Ok(Config {
621            buffer_stride,
622            num_rx_buffers,
623            num_tx_buffers,
624            options,
625            buffer_layout: BufferLayout {
626                length: buffer_length,
627                min_tx_head: *min_tx_buffer_head,
628                min_tx_tail: *min_tx_buffer_tail,
629                min_tx_data,
630            },
631        })
632    }
633}
634
635/// A port of the device.
636#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
637pub struct Port {
638    pub(crate) base: u8,
639    pub(crate) salt: u8,
640}
641
642impl TryFrom<netdev::PortId> for Port {
643    type Error = Error;
644    fn try_from(netdev::PortId { base, salt }: netdev::PortId) -> Result<Self> {
645        if base <= netdev::MAX_PORTS {
646            Ok(Self { base, salt })
647        } else {
648            Err(Error::InvalidPortId(base))
649        }
650    }
651}
652
653impl From<Port> for netdev::PortId {
654    fn from(Port { base, salt }: Port) -> Self {
655        Self { base, salt }
656    }
657}
658
659/// Pending descriptors to be sent to driver.
660struct Pending<K: AllocKind> {
661    inner: Mutex<(Vec<DescId<K>>, Option<Waker>)>,
662}
663
664impl<K: AllocKind> Pending<K> {
665    fn new(descs: Vec<DescId<K>>) -> Self {
666        Self { inner: Mutex::new((descs, None)) }
667    }
668
669    /// Extends the pending descriptors buffer.
670    fn extend(&self, descs: impl IntoIterator<Item = DescId<K>>) {
671        let mut guard = self.inner.lock();
672        let (storage, waker) = &mut *guard;
673        storage.extend(descs);
674        if let Some(waker) = waker.take() {
675            waker.wake();
676        }
677    }
678
679    /// Submits the pending buffer to the driver through [`zx::Fifo`].
680    ///
681    /// It will return [`Poll::Pending`] if any of the following happens:
682    ///   - There are no descriptors pending.
683    ///   - The fifo is not ready for write.
684    fn poll_submit(
685        &self,
686        fifo: &fasync::Fifo<DescId<K>>,
687        cx: &mut Context<'_>,
688    ) -> Poll<Result<usize>> {
689        let mut guard = self.inner.lock();
690        let (storage, waker) = &mut *guard;
691        if storage.is_empty() {
692            *waker = Some(cx.waker().clone());
693            return Poll::Pending;
694        }
695
696        // TODO(https://fxbug.dev/42107145): We're assuming that writing to the
697        // FIFO here is a sufficient memory barrier for the other end to access
698        // the data. That is currently true but not really guaranteed by the
699        // API.
700        let submitted = ready!(fifo.try_write(cx, &storage[..]))
701            .map_err(|status| Error::Fifo("write", K::REFL.as_str(), status))?;
702        let _drained = storage.drain(0..submitted);
703        Poll::Ready(Ok(submitted))
704    }
705}
706
707/// An intermediary buffer used to reduce syscall overhead by acting as a proxy
708/// to read entries from a FIFO.
709///
710/// `ReadyBuffer` caches read entries from a FIFO in pre-allocated memory,
711/// allowing different batch sizes between what is acquired from the FIFO and
712/// what's processed by the caller.
713struct ReadyBuffer<T> {
714    // NB: A vector of `MaybeUninit` here allows us to give a transparent memory
715    // layout to the FIFO object but still move objects out of our buffer
716    // without needing a `T: Default` implementation. There's a small added
717    // benefit of not paying for memory initialization on creation as well, but
718    // that's mostly negligible given all allocation is performed upfront.
719    data: Vec<MaybeUninit<T>>,
720    available: Range<usize>,
721}
722
723impl<T> Drop for ReadyBuffer<T> {
724    fn drop(&mut self) {
725        let Self { data, available } = self;
726        for initialized in &mut data[available.clone()] {
727            // SAFETY: the available range keeps track of initialized buffers,
728            // we must drop them on drop to uphold `MaybeUninit` expectations.
729            unsafe { initialized.assume_init_drop() }
730        }
731        *available = 0..0;
732    }
733}
734
735impl<T> ReadyBuffer<T> {
736    fn new(capacity: usize) -> Self {
737        let data = std::iter::from_fn(|| Some(MaybeUninit::uninit())).take(capacity).collect();
738        Self { data, available: 0..0 }
739    }
740
741    fn poll_with_fifo(
742        &mut self,
743        cx: &mut Context<'_>,
744        fifo: &fuchsia_async::Fifo<T>,
745    ) -> Poll<std::result::Result<T, zx::Status>>
746    where
747        T: fasync::FifoEntry,
748    {
749        let Self { data, available: Range { start, end } } = self;
750
751        loop {
752            // Always pop from available data first.
753            if *start != *end {
754                let desc = std::mem::replace(&mut data[*start], MaybeUninit::uninit());
755                *start += 1;
756                // SAFETY: Descriptor was in the initialized section, it was
757                // initialized.
758                let desc = unsafe { desc.assume_init() };
759                return Poll::Ready(Ok(desc));
760            }
761            // Fetch more from the FIFO.
762            let count = ready!(fifo.try_read(cx, &mut data[..]))?;
763            *start = 0;
764            *end = count;
765        }
766    }
767}
768
769struct TxIdleListeners {
770    event: event_listener::Event,
771    tx_in_flight: AtomicUsize,
772}
773
774impl TxIdleListeners {
775    fn new() -> Self {
776        Self { event: event_listener::Event::new(), tx_in_flight: AtomicUsize::new(0) }
777    }
778
779    /// Decreases the number of outstanding tx buffers by 1.
780    ///
781    /// Notifies any tx idle listeners if the number reaches 0.
782    fn tx_complete(&self) {
783        let Self { event, tx_in_flight } = self;
784        let old_value = tx_in_flight.fetch_sub(1, atomic::Ordering::SeqCst);
785        debug_assert_ne!(old_value, 0);
786        if old_value == 1 {
787            let _notified: usize = event.notify(usize::MAX);
788        }
789    }
790
791    /// Increases the number of outstanding tx buffers by 1.
792    fn tx_started(&self) {
793        let Self { event: _, tx_in_flight } = self;
794        let _: usize = tx_in_flight.fetch_add(1, atomic::Ordering::SeqCst);
795    }
796
797    async fn wait(&self) {
798        let Self { event, tx_in_flight } = self;
799        // This is _the correct way_ of holding an `event_listener::Listener`.
800        // We check the condition before installing the listener in the fast
801        // case, then we must check the condition again after creating the
802        // listener in case we've raced with the condition updating. Finally we
803        // must loop and check the condition again because we're not fully
804        // guaranteed to not have spurious wakeups.
805        //
806        // See the event_listener crate documentation for more details.
807        loop {
808            if tx_in_flight.load(atomic::Ordering::SeqCst) == 0 {
809                return;
810            }
811
812            event_listener::listener!(event => listener);
813
814            if tx_in_flight.load(atomic::Ordering::SeqCst) == 0 {
815                return;
816            }
817
818            listener.await;
819        }
820    }
821}
822
823/// An RAII lease possibly keeping the system from suspension.
824///
825/// Yielded from [`Session::watch_rx_leases`].
826///
827/// Dropping an `RxLease` relinquishes it.
828#[derive(Debug)]
829pub struct RxLease {
830    handle: netdev::DelegatedRxLeaseHandle,
831}
832
833impl Drop for RxLease {
834    fn drop(&mut self) {
835        let Self { handle } = self;
836        // Change detector in case we need any evolution on how to relinquish
837        // leases.
838        match handle {
839            netdev::DelegatedRxLeaseHandle::Channel(_channel) => {
840                // Dropping the channel is enough to relinquish the lease.
841            }
842            netdev::DelegatedRxLeaseHandle::Eventpair(_eventpair) => {
843                // Dropping the eventpair is enough to relinquish the lease.
844            }
845            netdev::DelegatedRxLeaseHandle::__SourceBreaking { .. } => {}
846        }
847    }
848}
849
850impl RxLease {
851    /// Peeks the internal lease.
852    pub fn inner(&self) -> &netdev::DelegatedRxLeaseHandle {
853        &self.handle
854    }
855}
856
857#[cfg(test)]
858mod tests {
859    use std::num::NonZeroU32;
860    use std::ops::Deref;
861    use std::sync::Arc;
862    use std::task::Poll;
863
864    use assert_matches::assert_matches;
865    use fuchsia_async::Fifo;
866    use test_case::test_case;
867    use zerocopy::{FromBytes, Immutable, IntoBytes};
868
869    use crate::session::DerivableConfig;
870
871    use super::buffer::{
872        AllocKind, DescId, NETWORK_DEVICE_DESCRIPTOR_LENGTH, NETWORK_DEVICE_DESCRIPTOR_VERSION,
873    };
874    use super::{
875        BufferLayout, Config, DeviceBaseInfo, DeviceInfo, Error, Inner, Mutex, Pending, Pool,
876        ReadyBuffer, Task, TxIdleListeners,
877    };
878
879    const DEFAULT_DEVICE_BASE_INFO: DeviceBaseInfo = DeviceBaseInfo {
880        rx_depth: 1,
881        tx_depth: 1,
882        buffer_alignment: 1,
883        max_buffer_length: None,
884        min_rx_buffer_length: 0,
885        min_tx_buffer_head: 0,
886        min_tx_buffer_length: 0,
887        min_tx_buffer_tail: 0,
888        max_buffer_parts: fidl_fuchsia_hardware_network::MAX_DESCRIPTOR_CHAIN,
889        min_rx_buffers: None,
890    };
891
892    const DEFAULT_DEVICE_INFO: DeviceInfo = DeviceInfo {
893        min_descriptor_length: 0,
894        descriptor_version: 1,
895        base_info: DEFAULT_DEVICE_BASE_INFO,
896    };
897
898    const DEFAULT_BUFFER_LENGTH: usize = 2048;
899
900    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
901        min_descriptor_length: u8::MAX,
902        ..DEFAULT_DEVICE_INFO
903    }, format!("descriptor length too small: {} < {}", NETWORK_DEVICE_DESCRIPTOR_LENGTH, u8::MAX))]
904    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
905        descriptor_version: 42,
906        ..DEFAULT_DEVICE_INFO
907    }, format!("descriptor version mismatch: {} != {}", NETWORK_DEVICE_DESCRIPTOR_VERSION, 42))]
908    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
909        base_info: DeviceBaseInfo {
910            tx_depth: 0,
911            ..DEFAULT_DEVICE_BASE_INFO
912        },
913        ..DEFAULT_DEVICE_INFO
914    }, "no TX buffers")]
915    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
916        base_info: DeviceBaseInfo {
917            rx_depth: 0,
918            ..DEFAULT_DEVICE_BASE_INFO
919        },
920        ..DEFAULT_DEVICE_INFO
921    }, "no RX buffers")]
922    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
923        base_info: DeviceBaseInfo {
924            tx_depth: u16::MAX,
925            rx_depth: u16::MAX,
926            ..DEFAULT_DEVICE_BASE_INFO
927        },
928        ..DEFAULT_DEVICE_INFO
929    }, format!("too many buffers requested: {} + {} > u16::MAX", u16::MAX, u16::MAX))]
930    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
931        base_info: DeviceBaseInfo {
932            min_tx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 + 1,
933            ..DEFAULT_DEVICE_BASE_INFO
934        },
935        ..DEFAULT_DEVICE_INFO
936    }, format!(
937        "buffer_length smaller than minimum TX requirement: {} < {}",
938        DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
939    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
940        base_info: DeviceBaseInfo {
941            min_tx_buffer_head: DEFAULT_BUFFER_LENGTH as u16 + 1,
942            ..DEFAULT_DEVICE_BASE_INFO
943        },
944        ..DEFAULT_DEVICE_INFO
945    }, format!(
946        "buffer length {} does not meet minimum tx buffer head/tail requirement {}/0",
947        DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
948    #[test_case(DEFAULT_BUFFER_LENGTH, DeviceInfo {
949        base_info: DeviceBaseInfo {
950            min_tx_buffer_tail: DEFAULT_BUFFER_LENGTH as u16 + 1,
951            ..DEFAULT_DEVICE_BASE_INFO
952        },
953        ..DEFAULT_DEVICE_INFO
954    }, format!(
955        "buffer length {} does not meet minimum tx buffer head/tail requirement 0/{}",
956        DEFAULT_BUFFER_LENGTH, DEFAULT_BUFFER_LENGTH + 1))]
957    #[test_case(0, DEFAULT_DEVICE_INFO, "buffer_stride is zero")]
958    #[test_case(usize::MAX, DEFAULT_DEVICE_INFO,
959    format!(
960        "too much memory required for the buffers: {} * {} > isize::MAX",
961        usize::MAX, 2))]
962    #[test_case(usize::MAX, DeviceInfo {
963        base_info: DeviceBaseInfo {
964            buffer_alignment: 2,
965            ..DEFAULT_DEVICE_BASE_INFO
966        },
967        ..DEFAULT_DEVICE_INFO
968    }, format!(
969        "not possible to align {} to {} under usize::MAX",
970        usize::MAX, 2))]
971    fn configs_from_device_info_err(
972        buffer_length: usize,
973        info: DeviceInfo,
974        expected: impl Deref<Target = str>,
975    ) {
976        let config = DerivableConfig { default_buffer_length: buffer_length, ..Default::default() };
977        assert_matches!(
978            info.make_config(config),
979            Err(Error::Config(got)) if got.as_str() == expected.deref()
980        );
981    }
982
983    #[test_case(DeviceInfo {
984        base_info: DeviceBaseInfo {
985            min_rx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 + 1,
986            ..DEFAULT_DEVICE_BASE_INFO
987        },
988        ..DEFAULT_DEVICE_INFO
989    }, DEFAULT_BUFFER_LENGTH + 1; "default below min")]
990    #[test_case(DeviceInfo {
991        base_info: DeviceBaseInfo {
992            max_buffer_length: NonZeroU32::new(DEFAULT_BUFFER_LENGTH as u32 - 1),
993            ..DEFAULT_DEVICE_BASE_INFO
994        },
995        ..DEFAULT_DEVICE_INFO
996    }, DEFAULT_BUFFER_LENGTH - 1; "default above max")]
997    #[test_case(DeviceInfo {
998        base_info: DeviceBaseInfo {
999            min_rx_buffer_length: DEFAULT_BUFFER_LENGTH as u32 - 1,
1000            max_buffer_length: NonZeroU32::new(DEFAULT_BUFFER_LENGTH as u32 + 1),
1001            ..DEFAULT_DEVICE_BASE_INFO
1002        },
1003        ..DEFAULT_DEVICE_INFO
1004    }, DEFAULT_BUFFER_LENGTH; "default in bounds")]
1005    fn configs_from_device_buffer_length(info: DeviceInfo, expected_length: usize) {
1006        let config = info
1007            .make_config(DerivableConfig {
1008                default_buffer_length: DEFAULT_BUFFER_LENGTH,
1009                ..Default::default()
1010            })
1011            .expect("is valid");
1012        let Config {
1013            buffer_layout: BufferLayout { length, min_tx_data: _, min_tx_head: _, min_tx_tail: _ },
1014            buffer_stride: _,
1015            num_rx_buffers: _,
1016            num_tx_buffers: _,
1017            options: _,
1018        } = config;
1019        assert_eq!(length, expected_length);
1020    }
1021
1022    fn make_fifos<K: AllocKind>() -> (Fifo<DescId<K>>, zx::Fifo<DescId<K>>) {
1023        let (handle, other_end) = zx::Fifo::create(1).unwrap();
1024        (Fifo::from_fifo(handle), other_end)
1025    }
1026
1027    fn remove_rights<T: FromBytes + IntoBytes + Immutable>(
1028        fifo: Fifo<T>,
1029        rights_to_remove: zx::Rights,
1030    ) -> Fifo<T> {
1031        let fifo = zx::Fifo::from(fifo);
1032        let rights = fifo.as_handle_ref().basic_info().expect("can retrieve info").rights;
1033
1034        let fifo = fifo.replace_handle(rights ^ rights_to_remove).expect("can replace");
1035        Fifo::from_fifo(fifo)
1036    }
1037
1038    enum TxOrRx {
1039        Tx,
1040        Rx,
1041    }
1042    #[test_case(TxOrRx::Tx, zx::Rights::READ; "tx read")]
1043    #[test_case(TxOrRx::Tx, zx::Rights::WRITE; "tx write")]
1044    #[test_case(TxOrRx::Rx, zx::Rights::WRITE; "rx read")]
1045    #[fuchsia_async::run_singlethreaded(test)]
1046    async fn task_as_future_poll_error(which_fifo: TxOrRx, right_to_remove: zx::Rights) {
1047        // This is a regression test for https://fxbug.dev/42072513. The flake
1048        // that caused that bug occurred because the Zircon channel was closed
1049        // but the error returned by a failed attempt to write to it wasn't
1050        // being propagated upwards. This test produces a similar situation by
1051        // altering the right on the FIFOs the task uses so as to cause either
1052        // an attempt to write or to read to fail. For completeness, it
1053        // exercises all the FIFO polls that comprise Task::poll.
1054        let (pool, _descriptors, _data) = Pool::new(
1055            DEFAULT_DEVICE_INFO
1056                .make_config(DerivableConfig {
1057                    default_buffer_length: DEFAULT_BUFFER_LENGTH,
1058                    ..Default::default()
1059                })
1060                .expect("is valid"),
1061        )
1062        .expect("is valid");
1063        let (session_proxy, _session_server) =
1064            fidl::endpoints::create_proxy::<fidl_fuchsia_hardware_network::SessionMarker>();
1065
1066        let (rx, _rx_sender) = make_fifos();
1067        let (tx, _tx_receiver) = make_fifos();
1068
1069        // Attenuate rights on one of the FIFOs.
1070        let (tx, rx) = match which_fifo {
1071            TxOrRx::Tx => (remove_rights(tx, right_to_remove), rx),
1072            TxOrRx::Rx => (tx, remove_rights(rx, right_to_remove)),
1073        };
1074
1075        let buf = pool.alloc_tx_buffer(1).await.expect("can allocate");
1076        let inner = Arc::new(Inner {
1077            pool,
1078            proxy: session_proxy,
1079            name: "fake_task".to_string(),
1080            rx,
1081            tx,
1082            tx_pending: Pending::new(vec![]),
1083            rx_ready: Mutex::new(ReadyBuffer::new(10)),
1084            tx_ready: Mutex::new(ReadyBuffer::new(10)),
1085            tx_idle_listeners: TxIdleListeners::new(),
1086        });
1087
1088        inner.send(buf);
1089
1090        let mut task = Task { inner };
1091
1092        // The task should not be able to continue because it can't read from or
1093        // write to one of the FIFOs.
1094        assert_matches!(futures::poll!(&mut task), Poll::Ready(Err(Error::Fifo(_, _, _))));
1095    }
1096}