1use core::convert::Infallible as Never;
8
9use alloc::vec::Vec;
10
11use derivative::Derivative;
12use log::trace;
13use netstack3_base::sync::Mutex;
14use netstack3_base::{
15 ChecksumOffloadResult, ChecksumOffloadSpec, Device, DeviceIdContext, ErrorAndSerializer,
16 NetworkSerializationContext, NetworkSerializer, TxMetadata,
17};
18use packet::{Buf, FragmentedBuffer as _, NoReuseBufferProvider, ReusableBuffer, new_buf_vec};
19
20use crate::internal::base::{DeviceBufferBindingsTypes, DeviceSendFrameError};
21use crate::internal::queue::{
22 DequeueState, DeviceBufferSpec, EnqueueResult, TransmitQueueFrameError, fifo,
23};
24use crate::internal::socket::{DeviceSocketHandler, ParseSentFrameError, SentFrame};
25
26#[derive(Derivative)]
28#[derivative(Default(bound = "Allocator: Default"))]
29pub struct TransmitQueueState<Meta, Buffer, Allocator> {
30 pub(super) allocator: ByteSliceAllocator<Allocator, Buffer>,
31 pub(super) queue: Option<fifo::Queue<Meta, Buffer>>,
32 pub(super) checksum_offload_spec: ChecksumOffloadSpec,
33}
34
35pub struct TransmitQueue<Meta, Buffer, Allocator> {
37 pub(crate) deque: Mutex<DequeueState<Meta, Buffer>>,
41 pub(crate) queue: Mutex<TransmitQueueState<Meta, Buffer, Allocator>>,
46}
47
48impl<Meta, Buffer, Allocator> TransmitQueue<Meta, Buffer, Allocator> {
49 pub(crate) fn new(allocator: Allocator, checksum_offload_spec: ChecksumOffloadSpec) -> Self {
50 Self {
51 deque: Mutex::new(DequeueState::default()),
52 queue: Mutex::new(TransmitQueueState {
53 allocator: ByteSliceAllocator::new(allocator),
54 queue: None,
55 checksum_offload_spec,
56 }),
57 }
58 }
59
60 pub(crate) fn tx_offload_spec(&self) -> ChecksumOffloadSpec {
61 self.queue.lock().checksum_offload_spec.clone()
62 }
63}
64
65pub trait TransmitQueueBindingsContext<DeviceId>: DeviceBufferBindingsTypes {
67 fn wake_tx_task(&mut self, device_id: &DeviceId);
74}
75
76pub trait TxQueuePacketMetadataCommon {
78 fn set_checksum_offload_result(&mut self, result: Option<ChecksumOffloadResult>);
80}
81
82impl<T: TxMetadata> TxQueuePacketMetadataCommon for T {
83 fn set_checksum_offload_result(&mut self, result: Option<ChecksumOffloadResult>) {
84 TxMetadata::set_checksum_offload_result(self, result);
85 }
86}
87
88pub trait TransmitQueueCommon<D: Device, C>: DeviceIdContext<D> {
90 type Meta: TxQueuePacketMetadataCommon;
92
93 type DequeueContext;
95
96 fn parse_outgoing_frame<'a, 'b>(
98 buf: &'a [u8],
99 meta: &'a Self::Meta,
100 ) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError>;
101}
102
103pub trait TransmitQueueContext<D: DeviceBufferSpec<BC>, BC>: TransmitQueueCommon<D, BC> {
105 fn with_transmit_queue_mut<
107 O,
108 F: FnOnce(&mut TransmitQueueState<Self::Meta, D::TxBuffer, D::TxAllocator>) -> O,
109 >(
110 &mut self,
111 device_id: &Self::DeviceId,
112 cb: F,
113 ) -> O;
114
115 fn with_transmit_queue<
117 O,
118 F: FnOnce(&TransmitQueueState<Self::Meta, D::TxBuffer, D::TxAllocator>) -> O,
119 >(
120 &mut self,
121 device_id: &Self::DeviceId,
122 cb: F,
123 ) -> O;
124
125 fn send_frame(
130 &mut self,
131 bindings_ctx: &mut BC,
132 device_id: &Self::DeviceId,
133 dequeue_context: Option<&mut Self::DequeueContext>,
134 meta: Self::Meta,
135 buf: D::TxBuffer,
136 ) -> Result<(), DeviceSendFrameError>;
137}
138
139pub trait TransmitDequeueContext<D: DeviceBufferSpec<BC>, BC>: TransmitQueueContext<D, BC> {
141 type TransmitQueueCtx<'a>: TransmitQueueContext<
143 D,
144 BC,
145 Meta = Self::Meta,
146 DequeueContext = Self::DequeueContext,
147 DeviceId = Self::DeviceId,
148 > + DeviceSocketHandler<D, BC>;
149
150 fn with_dequed_packets_and_tx_queue_ctx<
152 O,
153 F: FnOnce(&mut DequeueState<Self::Meta, D::TxBuffer>, &mut Self::TransmitQueueCtx<'_>) -> O,
154 >(
155 &mut self,
156 device_id: &Self::DeviceId,
157 cb: F,
158 ) -> O;
159}
160
161pub enum TransmitQueueConfiguration {
163 None,
165 Fifo,
167}
168
169pub trait TransmitQueueHandler<D: Device, BC>: TransmitQueueCommon<D, BC> {
171 fn queue_tx_frame<S>(
173 &mut self,
174 bindings_ctx: &mut BC,
175 device_id: &Self::DeviceId,
176 meta: Self::Meta,
177 body: S,
178 ) -> Result<usize, TransmitQueueFrameError<S>>
179 where
180 S: NetworkSerializer,
181 S::Buffer: ReusableBuffer;
182}
183
184pub(super) fn deliver_to_device_sockets<
185 D: DeviceBufferSpec<BC>,
186 BC: TransmitQueueBindingsContext<CC::DeviceId>,
187 CC: TransmitQueueCommon<D, BC> + DeviceSocketHandler<D, BC>,
188>(
189 core_ctx: &mut CC,
190 bindings_ctx: &mut BC,
191 device_id: &CC::DeviceId,
192 buffer: &D::TxBuffer,
193 meta: &CC::Meta,
194) {
195 buffer.with_bytes(|b| {
196 let mut handle_parsed = |bytes: &[u8]| match CC::parse_outgoing_frame(bytes, meta) {
197 Ok(sent_frame) => DeviceSocketHandler::handle_frame(
198 core_ctx,
199 bindings_ctx,
200 device_id,
201 sent_frame.into(),
202 bytes,
203 ),
204 Err(ParseSentFrameError) => {
205 trace!("failed to parse outgoing frame on {:?} ({} bytes)", device_id, bytes.len())
206 }
207 };
208
209 if let Some(bytes) = b.try_get_contiguous() {
210 handle_parsed(bytes)
211 } else {
212 handle_parsed(&b.to_flattened_vec())
213 }
214 })
215}
216
217impl EnqueueResult {
218 fn maybe_wake_tx<D, BC: TransmitQueueBindingsContext<D>>(
219 self,
220 bindings_ctx: &mut BC,
221 device_id: &D,
222 ) {
223 match self {
224 Self::QueuePreviouslyWasOccupied => (),
225 Self::QueueWasPreviouslyEmpty => bindings_ctx.wake_tx_task(device_id),
226 }
227 }
228}
229
230enum EnqueueStatus<Meta, Buffer> {
231 NotAttempted(Meta, Buffer),
232 Attempted,
233}
234
235fn insert_and_notify<
238 D: DeviceBufferSpec<BC>,
239 BC: TransmitQueueBindingsContext<CC::DeviceId>,
240 CC: TransmitQueueContext<D, BC> + DeviceSocketHandler<D, BC>,
241>(
242 bindings_ctx: &mut BC,
243 device_id: &CC::DeviceId,
244 inserter: Option<fifo::QueueTxInserter<'_, CC::Meta, D::TxBuffer>>,
245 meta: CC::Meta,
246 body: D::TxBuffer,
247) -> EnqueueStatus<CC::Meta, D::TxBuffer> {
248 match inserter {
249 None => EnqueueStatus::NotAttempted(meta, body),
251 Some(inserter) => {
252 inserter.insert(meta, body).maybe_wake_tx(bindings_ctx, device_id);
253 EnqueueStatus::Attempted
254 }
255 }
256}
257
258fn handle_post_enqueue<
261 D: DeviceBufferSpec<BC>,
262 BC: TransmitQueueBindingsContext<CC::DeviceId>,
263 CC: TransmitQueueContext<D, BC> + DeviceSocketHandler<D, BC>,
264>(
265 core_ctx: &mut CC,
266 bindings_ctx: &mut BC,
267 device_id: &CC::DeviceId,
268 status: EnqueueStatus<CC::Meta, D::TxBuffer>,
269) -> Result<(), DeviceSendFrameError> {
270 match status {
271 EnqueueStatus::NotAttempted(meta, body) => {
272 deliver_to_device_sockets(core_ctx, bindings_ctx, device_id, &body, &meta);
275 core_ctx.send_frame(bindings_ctx, device_id, None, meta, body)
278 }
279 EnqueueStatus::Attempted => Ok(()),
280 }
281}
282
283impl<
284 D: DeviceBufferSpec<BC>,
285 BC: TransmitQueueBindingsContext<CC::DeviceId>,
286 CC: TransmitQueueContext<D, BC> + DeviceSocketHandler<D, BC>,
287> TransmitQueueHandler<D, BC> for CC
288{
289 fn queue_tx_frame<S>(
290 &mut self,
291 bindings_ctx: &mut BC,
292 device_id: &CC::DeviceId,
293 mut meta: CC::Meta,
294 body: S,
295 ) -> Result<usize, TransmitQueueFrameError<S>>
296 where
297 S: NetworkSerializer,
298 S::Buffer: ReusableBuffer,
299 {
300 let (len, result) = self.with_transmit_queue_mut(
301 device_id,
302 |TransmitQueueState { allocator, queue, checksum_offload_spec }| {
303 let queue_len = queue.as_ref().map_or(0, |q| q.len());
304 let inserter = match queue {
305 None => None,
306 Some(q) => match q.tx_inserter() {
307 Some(i) => Some(i),
308 None => return Err(TransmitQueueFrameError::QueueFull(body)),
309 },
310 };
311 let mut context = NetworkSerializationContext::new(checksum_offload_spec.clone());
312 let body = body
313 .serialize_outer(
314 &mut context,
315 NoReuseBufferProvider(BufferAllocAdaptor {
316 allocator: &mut *allocator,
317 queue_len,
318 }),
319 )
320 .map_err(|(e, serializer)| {
321 TransmitQueueFrameError::SerializeError(ErrorAndSerializer {
322 serializer,
323 error: e.map_alloc(
324 |_: <D::TxAllocator as TxBufferAllocator<D::TxBuffer>>::Error| (),
325 ),
326 })
327 })?;
328 meta.set_checksum_offload_result(context.csum_offload_result());
329 let len = body.len();
330 let result = insert_and_notify::<_, _, CC>(
331 bindings_ctx,
332 device_id,
333 inserter,
334 meta,
335 allocator.get_buffer(),
336 );
337 Ok((len, result))
338 },
339 )?;
340
341 handle_post_enqueue(self, bindings_ctx, device_id, result)
342 .map(|()| len)
343 .map_err(TransmitQueueFrameError::NoQueue)
344 }
345}
346
347pub trait TxBufferAllocator<Buffer> {
349 type Error;
351
352 fn alloc(&mut self, len: usize, queue_len: usize) -> Result<Buffer, Self::Error>;
355}
356
357#[derive(Derivative)]
362#[derivative(Default(bound = "A: Default"))]
363pub(super) struct ByteSliceAllocator<A, B> {
364 allocator: A,
365 buffer: Option<B>,
366}
367
368impl<A, B> ByteSliceAllocator<A, B> {
369 fn new(allocator: A) -> Self {
370 Self { allocator, buffer: None }
371 }
372}
373
374impl<A: TxBufferAllocator<B>, B: AsMut<[u8]>> ByteSliceAllocator<A, B> {
375 fn alloc_buf_slice(
377 &mut self,
378 len: usize,
379 queue_len: usize,
380 ) -> Result<Buf<&mut [u8]>, A::Error> {
381 let old = self.buffer.replace(self.allocator.alloc(len, queue_len)?);
382 debug_assert!(old.is_none());
383 Ok(Buf::new(&mut self.buffer.as_mut().expect("must be set").as_mut()[..len], ..))
386 }
387
388 fn get_buffer(&mut self) -> B {
391 self.buffer.take().expect("must be allocated")
392 }
393}
394
395struct BufferAllocAdaptor<'a, A, B> {
396 allocator: &'a mut ByteSliceAllocator<A, B>,
397 queue_len: usize,
398}
399
400impl<'a, A, B> packet::BufferAlloc<Buf<&'a mut [u8]>> for BufferAllocAdaptor<'a, A, B>
401where
402 B: AsMut<[u8]>,
403 A: TxBufferAllocator<B>,
404{
405 type Error = A::Error;
406
407 fn alloc(self, len: usize) -> Result<Buf<&'a mut [u8]>, Self::Error> {
408 self.allocator.alloc_buf_slice(len, self.queue_len)
409 }
410}
411
412#[derive(Default)]
414pub struct BufVecU8Allocator;
415
416impl TxBufferAllocator<Buf<Vec<u8>>> for BufVecU8Allocator {
417 type Error = Never;
418
419 fn alloc(&mut self, len: usize, _qlen: usize) -> Result<Buf<Vec<u8>>, Self::Error> {
420 new_buf_vec(len)
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 use alloc::vec;
429
430 use assert_matches::assert_matches;
431 use net_declare::net_mac;
432 use net_types::ethernet::Mac;
433 use netstack3_base::testutil::{
434 FakeBindingsCtx, FakeCoreCtx, FakeLinkDevice, FakeLinkDeviceId, FakeTxMetadata,
435 };
436
437 impl<BT> DeviceBufferSpec<BT> for FakeLinkDevice {
438 type TxBuffer = packet::Buf<Vec<u8>>;
439 type TxAllocator = BufVecU8Allocator;
440 }
441 use netstack3_base::{
442 ContextPair, CounterContext, CtxPair, ResourceCounterContext, WorkQueueReport,
443 };
444 use test_case::test_case;
445
446 use crate::DeviceCounters;
447 use crate::internal::queue::api::TransmitQueueApi;
448 use crate::internal::queue::{BatchSize, MAX_TX_QUEUED_LEN};
449 use crate::internal::socket::{EthernetFrame, Frame};
450
451 #[derive(Default)]
452 struct FakeTxQueueState {
453 queue: TransmitQueueState<FakeTxMetadata, Buf<Vec<u8>>, BufVecU8Allocator>,
454 transmitted_packets: Vec<(Buf<Vec<u8>>, Option<DequeueContext>)>,
455 no_buffers: bool,
456 stack_wide_device_counters: DeviceCounters,
457 per_device_counters: DeviceCounters,
458 }
459
460 #[derive(Default)]
461 struct FakeTxQueueBindingsCtxState {
462 woken_tx_tasks: Vec<FakeLinkDeviceId>,
463 delivered_to_sockets: Vec<Frame<Vec<u8>>>,
464 }
465
466 type FakeCoreCtxImpl = FakeCoreCtx<FakeTxQueueState, FakeTxMetadata, FakeLinkDeviceId>;
467 type FakeBindingsCtxImpl = FakeBindingsCtx<(), (), FakeTxQueueBindingsCtxState, ()>;
468
469 impl TransmitQueueBindingsContext<FakeLinkDeviceId> for FakeBindingsCtxImpl {
470 fn wake_tx_task(&mut self, device_id: &FakeLinkDeviceId) {
471 self.state.woken_tx_tasks.push(device_id.clone())
472 }
473 }
474
475 const SRC_MAC: Mac = net_mac!("AA:BB:CC:DD:EE:FF");
476 const DEST_MAC: Mac = net_mac!("FF:EE:DD:CC:BB:AA");
477
478 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
479 struct DequeueContext;
480
481 impl TransmitQueueCommon<FakeLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
482 type DequeueContext = DequeueContext;
483 type Meta = FakeTxMetadata;
484
485 fn parse_outgoing_frame<'a, 'b>(
486 buf: &'a [u8],
487 _meta: &'b Self::Meta,
488 ) -> Result<SentFrame<&'a [u8]>, ParseSentFrameError> {
489 Ok(fake_sent_ethernet_with_body(buf))
490 }
491 }
492
493 fn fake_sent_ethernet_with_body<B>(body: B) -> SentFrame<B> {
494 SentFrame::Ethernet(EthernetFrame {
495 src_mac: SRC_MAC,
496 dst_mac: DEST_MAC,
497 ethertype: None,
498 body_offset: 0,
499 body,
500 })
501 }
502
503 trait TransmitQueueApiExt: ContextPair + Sized {
505 fn transmit_queue_api<D>(&mut self) -> TransmitQueueApi<D, &mut Self> {
506 TransmitQueueApi::new(self)
507 }
508 }
509
510 impl<O> TransmitQueueApiExt for O where O: ContextPair + Sized {}
511
512 impl TransmitQueueContext<FakeLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
513 fn with_transmit_queue<
514 O,
515 F: FnOnce(&TransmitQueueState<FakeTxMetadata, Buf<Vec<u8>>, BufVecU8Allocator>) -> O,
516 >(
517 &mut self,
518 &FakeLinkDeviceId: &FakeLinkDeviceId,
519 cb: F,
520 ) -> O {
521 cb(&self.state.queue)
522 }
523
524 fn with_transmit_queue_mut<
525 O,
526 F: FnOnce(&mut TransmitQueueState<FakeTxMetadata, Buf<Vec<u8>>, BufVecU8Allocator>) -> O,
527 >(
528 &mut self,
529 &FakeLinkDeviceId: &FakeLinkDeviceId,
530 cb: F,
531 ) -> O {
532 cb(&mut self.state.queue)
533 }
534
535 fn send_frame(
536 &mut self,
537 _bindings_ctx: &mut FakeBindingsCtxImpl,
538 &FakeLinkDeviceId: &FakeLinkDeviceId,
539 dequeue_context: Option<&mut DequeueContext>,
540 _meta: FakeTxMetadata,
541 buf: Buf<Vec<u8>>,
542 ) -> Result<(), DeviceSendFrameError> {
543 let FakeTxQueueState { transmitted_packets, no_buffers, .. } = &mut self.state;
544 if *no_buffers {
545 Err(DeviceSendFrameError::NoBuffers)
546 } else {
547 Ok(transmitted_packets.push((buf, dequeue_context.map(|c| *c))))
548 }
549 }
550 }
551
552 impl ResourceCounterContext<FakeLinkDeviceId, DeviceCounters> for FakeCoreCtxImpl {
553 fn per_resource_counters<'a>(
554 &'a self,
555 _resource: &'a FakeLinkDeviceId,
556 ) -> &'a DeviceCounters {
557 &self.state.per_device_counters
558 }
559 }
560
561 impl CounterContext<DeviceCounters> for FakeCoreCtxImpl {
562 fn counters(&self) -> &DeviceCounters {
563 &self.state.stack_wide_device_counters
564 }
565 }
566
567 impl TransmitDequeueContext<FakeLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
568 type TransmitQueueCtx<'a> = Self;
569
570 fn with_dequed_packets_and_tx_queue_ctx<
571 O,
572 F: FnOnce(
573 &mut DequeueState<FakeTxMetadata, Buf<Vec<u8>>>,
574 &mut Self::TransmitQueueCtx<'_>,
575 ) -> O,
576 >(
577 &mut self,
578 &FakeLinkDeviceId: &FakeLinkDeviceId,
579 cb: F,
580 ) -> O {
581 cb(&mut DequeueState::default(), self)
582 }
583 }
584
585 impl DeviceSocketHandler<FakeLinkDevice, FakeBindingsCtxImpl> for FakeCoreCtxImpl {
586 fn handle_frame(
587 &mut self,
588 bindings_ctx: &mut FakeBindingsCtxImpl,
589 _device: &Self::DeviceId,
590 frame: Frame<&[u8]>,
591 _whole_frame: &[u8],
592 ) {
593 bindings_ctx.state.delivered_to_sockets.push(frame.cloned())
594 }
595 }
596
597 #[test]
598 fn noqueue() {
599 let mut ctx = CtxPair::with_core_ctx(FakeCoreCtxImpl::default());
600
601 let body = Buf::new(vec![0], ..);
602
603 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
604 assert_eq!(
605 TransmitQueueHandler::queue_tx_frame(
606 core_ctx,
607 bindings_ctx,
608 &FakeLinkDeviceId,
609 FakeTxMetadata,
610 body.clone(),
611 ),
612 Ok(body.len())
613 );
614 let FakeTxQueueBindingsCtxState { woken_tx_tasks, delivered_to_sockets } =
615 &bindings_ctx.state;
616 assert_matches!(&woken_tx_tasks[..], &[]);
617 assert_eq!(
618 delivered_to_sockets,
619 &[Frame::Sent(fake_sent_ethernet_with_body(body.as_ref().into()))]
620 );
621 assert_eq!(core::mem::take(&mut core_ctx.state.transmitted_packets), [(body, None)]);
622
623 assert_eq!(
626 ctx.transmit_queue_api().transmit_queued_frames(
627 &FakeLinkDeviceId,
628 BatchSize::default(),
629 &mut DequeueContext,
630 ),
631 Ok(WorkQueueReport::AllDone),
632 );
633
634 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
635 assert_matches!(&bindings_ctx.state.woken_tx_tasks[..], &[]);
636 assert_eq!(core::mem::take(&mut core_ctx.state.transmitted_packets), []);
637 }
638
639 #[test_case(BatchSize::MAX)]
640 #[test_case(BatchSize::MAX/2)]
641 fn fifo_queue_and_dequeue(batch_size: usize) {
642 let mut ctx = CtxPair::with_core_ctx(FakeCoreCtxImpl::default());
643
644 ctx.transmit_queue_api()
645 .set_configuration(&FakeLinkDeviceId, TransmitQueueConfiguration::Fifo);
646
647 for _ in 0..2 {
648 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
649 for i in 0..MAX_TX_QUEUED_LEN {
650 let body = Buf::new(vec![i as u8], ..);
651 assert_eq!(
652 TransmitQueueHandler::queue_tx_frame(
653 core_ctx,
654 bindings_ctx,
655 &FakeLinkDeviceId,
656 FakeTxMetadata,
657 body
658 ),
659 Ok(1)
660 );
661 assert_eq!(bindings_ctx.state.woken_tx_tasks, [FakeLinkDeviceId]);
664 }
665
666 let body = Buf::new(vec![131], ..);
667 assert_eq!(
668 TransmitQueueHandler::queue_tx_frame(
669 core_ctx,
670 bindings_ctx,
671 &FakeLinkDeviceId,
672 FakeTxMetadata,
673 body.clone(),
674 ),
675 Err(TransmitQueueFrameError::QueueFull(body))
676 );
677
678 let FakeTxQueueBindingsCtxState { woken_tx_tasks, delivered_to_sockets } =
679 &mut bindings_ctx.state;
680 assert_eq!(core::mem::take(woken_tx_tasks), [FakeLinkDeviceId]);
683 assert_eq!(core::mem::take(delivered_to_sockets), &[]);
685
686 assert!(MAX_TX_QUEUED_LEN > batch_size);
687 for i in (0..(MAX_TX_QUEUED_LEN - batch_size)).step_by(batch_size) {
688 assert_eq!(
689 ctx.transmit_queue_api().transmit_queued_frames(
690 &FakeLinkDeviceId,
691 BatchSize::new_saturating(batch_size),
692 &mut DequeueContext
693 ),
694 Ok(WorkQueueReport::Pending),
695 );
696 assert_eq!(
697 core::mem::take(&mut ctx.core_ctx.state.transmitted_packets),
698 (i..i + batch_size)
699 .map(|i| (Buf::new(vec![i as u8], ..), Some(DequeueContext)))
700 .collect::<Vec<_>>()
701 );
702 }
703
704 assert_eq!(
705 ctx.transmit_queue_api().transmit_queued_frames(
706 &FakeLinkDeviceId,
707 BatchSize::new_saturating(batch_size),
708 &mut DequeueContext
709 ),
710 Ok(WorkQueueReport::AllDone),
711 );
712
713 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
714 assert_eq!(
715 core::mem::take(&mut core_ctx.state.transmitted_packets),
716 (batch_size * (MAX_TX_QUEUED_LEN / batch_size - 1)..MAX_TX_QUEUED_LEN)
717 .map(|i| (Buf::new(vec![i as u8], ..), Some(DequeueContext)))
718 .collect::<Vec<_>>()
719 );
720 let FakeTxQueueBindingsCtxState { woken_tx_tasks, delivered_to_sockets } =
723 &mut bindings_ctx.state;
724 assert_matches!(&core::mem::take(woken_tx_tasks)[..], &[]);
725
726 assert_eq!(
729 core::mem::take(delivered_to_sockets),
730 (0..MAX_TX_QUEUED_LEN)
731 .map(|i| Frame::Sent(fake_sent_ethernet_with_body(vec![i as u8])))
732 .collect::<Vec<_>>()
733 );
734 }
735 }
736
737 #[test]
738 fn dequeue_error() {
739 let mut ctx = CtxPair::with_core_ctx(FakeCoreCtxImpl::default());
740
741 ctx.transmit_queue_api()
742 .set_configuration(&FakeLinkDeviceId, TransmitQueueConfiguration::Fifo);
743
744 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
745 let body = Buf::new(vec![0], ..);
746 assert_eq!(
747 TransmitQueueHandler::queue_tx_frame(
748 core_ctx,
749 bindings_ctx,
750 &FakeLinkDeviceId,
751 FakeTxMetadata,
752 body.clone(),
753 ),
754 Ok(body.len())
755 );
756 assert_eq!(core::mem::take(&mut bindings_ctx.state.woken_tx_tasks), [FakeLinkDeviceId]);
757 assert_eq!(core_ctx.state.transmitted_packets, []);
758
759 core_ctx.state.no_buffers = true;
760 assert_eq!(
761 ctx.transmit_queue_api().transmit_queued_frames(
762 &FakeLinkDeviceId,
763 BatchSize::default(),
764 &mut DequeueContext
765 ),
766 Err(DeviceSendFrameError::NoBuffers),
767 );
768 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
769 assert_eq!(core_ctx.state.transmitted_packets, []);
770 let FakeTxQueueBindingsCtxState { woken_tx_tasks, delivered_to_sockets } =
771 &bindings_ctx.state;
772 assert_matches!(&woken_tx_tasks[..], &[]);
773 assert_eq!(
776 delivered_to_sockets,
777 &[Frame::Sent(fake_sent_ethernet_with_body(body.as_ref().into()))]
778 );
779
780 core_ctx.state.no_buffers = false;
781 assert_eq!(
782 ctx.transmit_queue_api().transmit_queued_frames(
783 &FakeLinkDeviceId,
784 BatchSize::default(),
785 &mut DequeueContext
786 ),
787 Ok(WorkQueueReport::AllDone),
788 );
789 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
790 assert_matches!(&bindings_ctx.state.woken_tx_tasks[..], &[]);
791 assert_eq!(core::mem::take(&mut core_ctx.state.transmitted_packets), []);
793 }
794
795 #[test_case(true; "device no buffers")]
796 #[test_case(false; "device has buffers")]
797 fn drain_before_noqueue(no_buffers: bool) {
798 let mut ctx = CtxPair::with_core_ctx(FakeCoreCtxImpl::default());
799
800 ctx.transmit_queue_api()
801 .set_configuration(&FakeLinkDeviceId, TransmitQueueConfiguration::Fifo);
802
803 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
804 let body = Buf::new(vec![0], ..);
805 assert_eq!(
806 TransmitQueueHandler::queue_tx_frame(
807 core_ctx,
808 bindings_ctx,
809 &FakeLinkDeviceId,
810 FakeTxMetadata,
811 body.clone(),
812 ),
813 Ok(body.len())
814 );
815 assert_eq!(core::mem::take(&mut bindings_ctx.state.woken_tx_tasks), [FakeLinkDeviceId]);
816 assert_eq!(core_ctx.state.transmitted_packets, []);
817
818 core_ctx.state.no_buffers = no_buffers;
819 ctx.transmit_queue_api()
820 .set_configuration(&FakeLinkDeviceId, TransmitQueueConfiguration::None);
821
822 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
823 let FakeTxQueueBindingsCtxState { woken_tx_tasks, delivered_to_sockets } =
824 &bindings_ctx.state;
825 assert_matches!(&woken_tx_tasks[..], &[]);
826 assert_eq!(
827 delivered_to_sockets,
828 &[Frame::Sent(fake_sent_ethernet_with_body(body.as_ref().into()))]
829 );
830 if no_buffers {
831 assert_eq!(core_ctx.state.transmitted_packets, []);
832 } else {
833 assert_eq!(core::mem::take(&mut core_ctx.state.transmitted_packets), [(body, None)]);
834 }
835 }
836
837 #[test]
838 fn count() {
839 let mut ctx = CtxPair::with_core_ctx(FakeCoreCtxImpl::default());
840 assert_eq!(ctx.transmit_queue_api().count(&FakeLinkDeviceId), None);
841
842 ctx.transmit_queue_api()
843 .set_configuration(&FakeLinkDeviceId, TransmitQueueConfiguration::Fifo);
844
845 assert_eq!(ctx.transmit_queue_api().count(&FakeLinkDeviceId), Some(0));
846
847 let CtxPair { core_ctx, bindings_ctx } = &mut ctx;
848 let body = Buf::new(vec![0], ..);
849 assert_eq!(
850 TransmitQueueHandler::queue_tx_frame(
851 core_ctx,
852 bindings_ctx,
853 &FakeLinkDeviceId,
854 FakeTxMetadata,
855 body,
856 ),
857 Ok(1)
858 );
859
860 assert_eq!(ctx.transmit_queue_api().count(&FakeLinkDeviceId), Some(1));
861 }
862}