1use core::num::{NonZeroU16, NonZeroU64};
4
5use crate::{ChainLayoutError, ChainRegion};
6
7pub const MAX_SPLIT_QUEUE_SIZE: u16 = 32_768;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(transparent)]
13pub struct QueueSize(NonZeroU16);
14
15impl QueueSize {
16 pub const fn new(value: u16) -> Option<Self> {
18 if value.is_power_of_two() && value <= MAX_SPLIT_QUEUE_SIZE {
19 match NonZeroU16::new(value) {
20 Some(value) => Some(Self(value)),
21 None => None,
22 }
23 } else {
24 None
25 }
26 }
27
28 pub const fn get(self) -> u16 {
30 self.0.get()
31 }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
36#[repr(transparent)]
37pub struct QueueEpoch(NonZeroU64);
38
39impl QueueEpoch {
40 pub const INITIAL: Self = match NonZeroU64::new(1) {
42 Some(value) => Self(value),
43 None => panic!("one is nonzero"),
44 };
45
46 pub const fn new(value: u64) -> Option<Self> {
48 match NonZeroU64::new(value) {
49 Some(value) => Some(Self(value)),
50 None => None,
51 }
52 }
53
54 pub const fn get(self) -> u64 {
56 self.0.get()
57 }
58
59 pub const fn checked_next(self) -> Option<Self> {
61 match self.get().checked_add(1) {
62 Some(value) => Self::new(value),
63 None => None,
64 }
65 }
66}
67
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
70pub struct ChainId {
71 epoch: QueueEpoch,
72 token: u64,
73}
74
75impl ChainId {
76 pub const fn new(epoch: QueueEpoch, token: u64) -> Self {
78 Self { epoch, token }
79 }
80
81 pub const fn epoch(self) -> QueueEpoch {
83 self.epoch
84 }
85
86 pub const fn token(self) -> u64 {
88 self.token
89 }
90}
91
92#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
94#[repr(transparent)]
95pub struct UsedLength(u32);
96
97impl UsedLength {
98 pub const fn new(bytes: u32) -> Self {
100 Self(bytes)
101 }
102
103 pub const fn get(self) -> u32 {
105 self.0
106 }
107}
108
109#[derive(Clone, Copy, Debug, PartialEq, Eq)]
111pub struct QueueState {
112 max_size: QueueSize,
113 size: Option<QueueSize>,
114 ready: bool,
115 epoch: QueueEpoch,
116}
117
118impl QueueState {
119 pub const fn unconfigured(max_size: QueueSize, epoch: QueueEpoch) -> Self {
121 Self {
122 max_size,
123 size: None,
124 ready: false,
125 epoch,
126 }
127 }
128
129 pub const fn new(
131 max_size: QueueSize,
132 size: Option<QueueSize>,
133 ready: bool,
134 epoch: QueueEpoch,
135 ) -> Result<Self, QueueConfigError> {
136 if let Some(size) = size {
137 if size.get() > max_size.get() {
138 return Err(QueueConfigError::SizeExceedsMaximum);
139 }
140 }
141 if ready && size.is_none() {
142 return Err(QueueConfigError::ReadyWithoutSize);
143 }
144 Ok(Self {
145 max_size,
146 size,
147 ready,
148 epoch,
149 })
150 }
151
152 pub const fn max_size(self) -> QueueSize {
154 self.max_size
155 }
156
157 pub const fn size(self) -> Option<QueueSize> {
159 self.size
160 }
161
162 pub const fn ready(self) -> bool {
164 self.ready
165 }
166
167 pub const fn epoch(self) -> QueueEpoch {
169 self.epoch
170 }
171}
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq)]
175pub enum QueueConfigError {
176 SizeExceedsMaximum,
178 ReadyWithoutSize,
180 NonIncreasingEpoch,
182}
183
184#[derive(Clone, Copy, Debug, PartialEq, Eq)]
186pub enum QueueError<E> {
187 NotReady,
189 InvalidConfiguration(QueueConfigError),
191 ResetRace {
193 operation: QueueEpoch,
195 current: QueueEpoch,
197 },
198 UsedLengthExceeded {
200 used: UsedLength,
202 capacity: u64,
204 },
205 Transport(E),
207}
208
209#[derive(Clone, Copy, Debug, PartialEq, Eq)]
211pub enum MalformedChain {
212 DescriptorLoop,
214 DescriptorIndex,
216 DescriptorFlags,
218 DescriptorCount,
220 ZeroLength,
222 Direction,
224 LengthOverflow,
226 PortLengthMismatch,
228 IndirectUnsupported,
230 IndirectMalformed,
232 Address,
234}
235
236impl From<ChainLayoutError> for MalformedChain {
237 fn from(error: ChainLayoutError) -> Self {
238 match error {
239 ChainLayoutError::DescriptorCount => Self::DescriptorCount,
240 ChainLayoutError::ZeroLength => Self::ZeroLength,
241 ChainLayoutError::Direction => Self::Direction,
242 ChainLayoutError::LengthOverflow => Self::LengthOverflow,
243 ChainLayoutError::PortLengthMismatch => Self::PortLengthMismatch,
244 }
245 }
246}
247
248#[derive(Clone, Copy, Debug, PartialEq, Eq)]
250pub enum ChainError<E> {
251 Malformed(MalformedChain),
253 ResetRace {
255 chain: QueueEpoch,
257 current: QueueEpoch,
259 },
260 Transport(E),
262}
263
264#[derive(Debug)]
269pub struct ChainIo<'a, R: ?Sized, W: ?Sized> {
270 regions: &'a [ChainRegion],
271 request: &'a R,
272 response: &'a mut W,
273}
274
275impl<'a, R: ?Sized, W: ?Sized> ChainIo<'a, R, W> {
276 pub fn new(regions: &'a [ChainRegion], request: &'a R, response: &'a mut W) -> Self {
278 Self {
279 regions,
280 request,
281 response,
282 }
283 }
284
285 pub fn into_parts(self) -> (&'a [ChainRegion], &'a R, &'a mut W) {
287 (self.regions, self.request, self.response)
288 }
289}
290
291pub type ChainIoResult<'a, R, W, E> = Result<ChainIo<'a, R, W>, ChainError<E>>;
293
294pub trait DeviceChain {
300 type Request: ?Sized;
302 type Response: ?Sized;
304 type Error;
306
307 fn id(&self) -> ChainId;
311
312 fn io(&mut self) -> ChainIoResult<'_, Self::Request, Self::Response, Self::Error>;
316}
317
318#[derive(Clone, Copy, Debug, PartialEq, Eq)]
320pub enum NotificationHint {
321 Suppressed,
323 Notify,
325}
326
327#[derive(Clone, Copy, Debug, PartialEq, Eq)]
329pub enum NotificationRecheck {
330 Idle,
332 WorkPending,
334}
335
336pub trait QueuePort {
338 fn state(&self) -> QueueState;
343}
344
345pub trait QueueControl: QueuePort {
347 type Error;
349
350 fn configure(&mut self, size: QueueSize) -> Result<(), QueueError<Self::Error>>;
355
356 fn set_ready(&mut self, ready: bool) -> Result<(), QueueError<Self::Error>>;
361}
362
363#[derive(Clone, Copy, Debug, PartialEq, Eq)]
365pub struct PublishedChain {
366 id: ChainId,
367 notification: NotificationHint,
368}
369
370impl PublishedChain {
371 pub const fn new(id: ChainId, notification: NotificationHint) -> Self {
373 Self { id, notification }
374 }
375
376 pub const fn id(self) -> ChainId {
378 self.id
379 }
380
381 pub const fn notification(self) -> NotificationHint {
383 self.notification
384 }
385}
386
387#[derive(Clone, Copy, Debug, PartialEq, Eq)]
389pub enum PublishErrorKind<E> {
390 NotReady,
392 QueueFull,
394 InsufficientDescriptors,
396 ResetRace {
398 operation: QueueEpoch,
400 current: QueueEpoch,
402 },
403 Transport(E),
405}
406
407#[derive(Debug, PartialEq, Eq)]
409pub struct PublishError<C, E> {
410 chain: C,
411 kind: PublishErrorKind<E>,
412}
413
414impl<C, E> PublishError<C, E> {
415 pub const fn new(chain: C, kind: PublishErrorKind<E>) -> Self {
417 Self { chain, kind }
418 }
419
420 pub const fn chain(&self) -> &C {
422 &self.chain
423 }
424
425 pub const fn kind(&self) -> &PublishErrorKind<E> {
427 &self.kind
428 }
429
430 pub fn into_parts(self) -> (C, PublishErrorKind<E>) {
432 (self.chain, self.kind)
433 }
434}
435
436#[derive(Debug, PartialEq, Eq)]
438pub struct UsedChain<C> {
439 id: ChainId,
440 used: UsedLength,
441 chain: C,
442}
443
444impl<C> UsedChain<C> {
445 pub const fn new(id: ChainId, used: UsedLength, chain: C) -> Self {
447 Self { id, used, chain }
448 }
449
450 pub const fn id(&self) -> ChainId {
452 self.id
453 }
454
455 pub const fn used(&self) -> UsedLength {
457 self.used
458 }
459
460 pub const fn chain(&self) -> &C {
462 &self.chain
463 }
464
465 pub fn into_parts(self) -> (ChainId, UsedLength, C) {
467 (self.id, self.used, self.chain)
468 }
469}
470
471#[derive(Debug, PartialEq, Eq)]
473pub struct ReclaimedChain<C> {
474 id: ChainId,
475 chain: C,
476}
477
478impl<C> ReclaimedChain<C> {
479 pub const fn new(id: ChainId, chain: C) -> Self {
481 Self { id, chain }
482 }
483
484 pub const fn id(&self) -> ChainId {
486 self.id
487 }
488
489 pub fn into_parts(self) -> (ChainId, C) {
491 (self.id, self.chain)
492 }
493}
494
495pub trait DriverQueue: QueuePort {
502 type Chain;
504 type Reclaimed: IntoIterator<Item = ReclaimedChain<Self::Chain>>;
506 type Error;
508
509 fn publish(
514 &mut self,
515 chain: Self::Chain,
516 ) -> Result<PublishedChain, PublishError<Self::Chain, Self::Error>>;
517
518 fn pop_used(&mut self) -> Result<Option<UsedChain<Self::Chain>>, QueueError<Self::Error>>;
523
524 fn disable_used_notifications(&mut self) -> Result<(), QueueError<Self::Error>>;
528
529 fn enable_used_notifications(&mut self)
534 -> Result<NotificationRecheck, QueueError<Self::Error>>;
535
536 fn reset(&mut self, next_epoch: QueueEpoch)
542 -> Result<Self::Reclaimed, QueueError<Self::Error>>;
543}
544
545pub trait DeviceQueue: QueuePort {
551 type Chain: DeviceChain;
553 type Error;
555
556 fn pop_available(&mut self) -> Result<Option<Self::Chain>, QueueError<Self::Error>>;
560
561 fn complete(
566 &mut self,
567 chain: Self::Chain,
568 used: UsedLength,
569 ) -> Result<NotificationHint, QueueError<Self::Error>>;
570
571 fn disable_available_notifications(&mut self) -> Result<(), QueueError<Self::Error>>;
575
576 fn enable_available_notifications(
581 &mut self,
582 ) -> Result<NotificationRecheck, QueueError<Self::Error>>;
583
584 fn reset(&mut self, next_epoch: QueueEpoch) -> Result<(), QueueError<Self::Error>>;
589}
590
591#[cfg(test)]
592mod tests {
593 extern crate std;
594
595 use std::collections::VecDeque;
596 use std::sync::Arc;
597 use std::sync::atomic::{AtomicU64, Ordering};
598 use std::vec::Vec;
599
600 use super::*;
601
602 #[derive(Debug, PartialEq, Eq)]
603 struct TestPayload {
604 request: [u8; 4],
605 response: [u8; 4],
606 }
607
608 #[derive(Debug)]
609 struct TestDeviceChain {
610 id: ChainId,
611 current_epoch: Arc<AtomicU64>,
612 payload: TestPayload,
613 }
614
615 impl DeviceChain for TestDeviceChain {
616 type Request = [u8; 4];
617 type Response = [u8; 4];
618 type Error = ();
619
620 fn id(&self) -> ChainId {
621 self.id
622 }
623
624 fn io(
625 &mut self,
626 ) -> Result<ChainIo<'_, Self::Request, Self::Response>, ChainError<Self::Error>> {
627 let current = QueueEpoch::new(self.current_epoch.load(Ordering::Acquire)).unwrap();
628 if current != self.id.epoch() {
629 return Err(ChainError::ResetRace {
630 chain: self.id.epoch(),
631 current,
632 });
633 }
634 static REGIONS: [ChainRegion; 2] = [ChainRegion::readable(4), ChainRegion::writable(4)];
635 Ok(ChainIo::new(
636 ®IONS,
637 &self.payload.request,
638 &mut self.payload.response,
639 ))
640 }
641 }
642
643 struct TestQueue {
644 state: QueueState,
645 current_epoch: Arc<AtomicU64>,
646 next_token: u64,
647 available: VecDeque<(ChainId, TestPayload)>,
648 used: VecDeque<UsedChain<TestPayload>>,
649 }
650
651 impl TestQueue {
652 fn new() -> Self {
653 let epoch = QueueEpoch::INITIAL;
654 Self {
655 state: QueueState::unconfigured(QueueSize::new(8).unwrap(), epoch),
656 current_epoch: Arc::new(AtomicU64::new(epoch.get())),
657 next_token: 0,
658 available: VecDeque::new(),
659 used: VecDeque::new(),
660 }
661 }
662
663 fn check_ready(&self) -> Result<(), QueueError<()>> {
664 if self.state.ready() {
665 Ok(())
666 } else {
667 Err(QueueError::NotReady)
668 }
669 }
670
671 fn advance_epoch(&mut self, next_epoch: QueueEpoch) -> Result<(), QueueError<()>> {
672 if next_epoch <= self.state.epoch() {
673 return Err(QueueError::InvalidConfiguration(
674 QueueConfigError::NonIncreasingEpoch,
675 ));
676 }
677 self.state = QueueState::unconfigured(self.state.max_size(), next_epoch);
678 self.current_epoch
679 .store(next_epoch.get(), Ordering::Release);
680 Ok(())
681 }
682 }
683
684 impl QueuePort for TestQueue {
685 fn state(&self) -> QueueState {
686 self.state
687 }
688 }
689
690 impl QueueControl for TestQueue {
691 type Error = ();
692
693 fn configure(&mut self, size: QueueSize) -> Result<(), QueueError<Self::Error>> {
694 if self.state.ready() || size > self.state.max_size() {
695 return Err(QueueError::InvalidConfiguration(
696 QueueConfigError::SizeExceedsMaximum,
697 ));
698 }
699 self.state =
700 QueueState::new(self.state.max_size(), Some(size), false, self.state.epoch())
701 .unwrap();
702 Ok(())
703 }
704
705 fn set_ready(&mut self, ready: bool) -> Result<(), QueueError<Self::Error>> {
706 self.state = QueueState::new(
707 self.state.max_size(),
708 self.state.size(),
709 ready,
710 self.state.epoch(),
711 )
712 .map_err(QueueError::InvalidConfiguration)?;
713 Ok(())
714 }
715 }
716
717 impl DriverQueue for TestQueue {
718 type Chain = TestPayload;
719 type Reclaimed = Vec<ReclaimedChain<Self::Chain>>;
720 type Error = ();
721
722 fn publish(
723 &mut self,
724 chain: Self::Chain,
725 ) -> Result<PublishedChain, PublishError<Self::Chain, Self::Error>> {
726 if !self.state.ready() {
727 return Err(PublishError::new(chain, PublishErrorKind::NotReady));
728 }
729 if self.available.len() >= usize::from(self.state.size().unwrap().get()) {
730 return Err(PublishError::new(chain, PublishErrorKind::QueueFull));
731 }
732 let id = ChainId::new(self.state.epoch(), self.next_token);
733 self.next_token += 1;
734 self.available.push_back((id, chain));
735 Ok(PublishedChain::new(id, NotificationHint::Notify))
736 }
737
738 fn pop_used(&mut self) -> Result<Option<UsedChain<Self::Chain>>, QueueError<Self::Error>> {
739 self.check_ready()?;
740 Ok(self.used.pop_front())
741 }
742
743 fn disable_used_notifications(&mut self) -> Result<(), QueueError<Self::Error>> {
744 self.check_ready()
745 }
746
747 fn enable_used_notifications(
748 &mut self,
749 ) -> Result<NotificationRecheck, QueueError<Self::Error>> {
750 self.check_ready()?;
751 Ok(if self.used.is_empty() {
752 NotificationRecheck::Idle
753 } else {
754 NotificationRecheck::WorkPending
755 })
756 }
757
758 fn reset(
759 &mut self,
760 next_epoch: QueueEpoch,
761 ) -> Result<Self::Reclaimed, QueueError<Self::Error>> {
762 self.advance_epoch(next_epoch)?;
763 let reclaimed = self
764 .available
765 .drain(..)
766 .map(|(id, chain)| ReclaimedChain::new(id, chain))
767 .collect();
768 self.used.clear();
769 Ok(reclaimed)
770 }
771 }
772
773 impl DeviceQueue for TestQueue {
774 type Chain = TestDeviceChain;
775 type Error = ();
776
777 fn pop_available(&mut self) -> Result<Option<Self::Chain>, QueueError<Self::Error>> {
778 self.check_ready()?;
779 Ok(self
780 .available
781 .pop_front()
782 .map(|(id, payload)| TestDeviceChain {
783 id,
784 current_epoch: Arc::clone(&self.current_epoch),
785 payload,
786 }))
787 }
788
789 fn complete(
790 &mut self,
791 chain: Self::Chain,
792 used: UsedLength,
793 ) -> Result<NotificationHint, QueueError<Self::Error>> {
794 if chain.id.epoch() != self.state.epoch() {
795 return Err(QueueError::ResetRace {
796 operation: chain.id.epoch(),
797 current: self.state.epoch(),
798 });
799 }
800 if u64::from(used.get()) > chain.payload.response.len() as u64 {
801 return Err(QueueError::UsedLengthExceeded {
802 used,
803 capacity: chain.payload.response.len() as u64,
804 });
805 }
806 self.used
807 .push_back(UsedChain::new(chain.id, used, chain.payload));
808 Ok(NotificationHint::Notify)
809 }
810
811 fn disable_available_notifications(&mut self) -> Result<(), QueueError<Self::Error>> {
812 self.check_ready()
813 }
814
815 fn enable_available_notifications(
816 &mut self,
817 ) -> Result<NotificationRecheck, QueueError<Self::Error>> {
818 self.check_ready()?;
819 Ok(if self.available.is_empty() {
820 NotificationRecheck::Idle
821 } else {
822 NotificationRecheck::WorkPending
823 })
824 }
825
826 fn reset(&mut self, next_epoch: QueueEpoch) -> Result<(), QueueError<Self::Error>> {
827 self.advance_epoch(next_epoch)?;
828 self.available.clear();
829 self.used.clear();
830 Ok(())
831 }
832 }
833
834 #[test]
835 fn queue_sizes_and_states_are_validated() {
836 assert_eq!(QueueSize::new(0), None);
837 assert_eq!(QueueSize::new(3), None);
838 assert_eq!(QueueSize::new(MAX_SPLIT_QUEUE_SIZE + 1), None);
839
840 let max = QueueSize::new(8).unwrap();
841 let too_large = QueueSize::new(16).unwrap();
842 assert_eq!(
843 QueueState::new(max, Some(too_large), false, QueueEpoch::INITIAL),
844 Err(QueueConfigError::SizeExceedsMaximum)
845 );
846 assert_eq!(
847 QueueState::new(max, None, true, QueueEpoch::INITIAL),
848 Err(QueueConfigError::ReadyWithoutSize)
849 );
850 }
851
852 #[test]
853 fn publication_transfers_ownership_until_used() {
854 let mut queue = TestQueue::new();
855 QueueControl::configure(&mut queue, QueueSize::new(2).unwrap()).unwrap();
856 QueueControl::set_ready(&mut queue, true).unwrap();
857
858 let published = DriverQueue::publish(
859 &mut queue,
860 TestPayload {
861 request: *b"ping",
862 response: [0; 4],
863 },
864 )
865 .unwrap();
866 assert_eq!(published.notification(), NotificationHint::Notify);
867
868 let mut chain = DeviceQueue::pop_available(&mut queue).unwrap().unwrap();
869 assert_eq!(chain.id(), published.id());
870 let (regions, request, response) = chain.io().unwrap().into_parts();
871 assert_eq!(regions.len(), 2);
872 assert_eq!(request, b"ping");
873 response.copy_from_slice(b"pong");
874 DeviceQueue::complete(&mut queue, chain, UsedLength::new(4)).unwrap();
875
876 let used = DriverQueue::pop_used(&mut queue).unwrap().unwrap();
877 assert_eq!(used.id(), published.id());
878 assert_eq!(used.used(), UsedLength::new(4));
879 assert_eq!(used.chain().response, *b"pong");
880 }
881
882 #[test]
883 fn publication_backpressure_returns_the_chain() {
884 let mut queue = TestQueue::new();
885 let payload = TestPayload {
886 request: *b"ping",
887 response: [0; 4],
888 };
889 let error = DriverQueue::publish(&mut queue, payload).unwrap_err();
890 let (payload, kind) = error.into_parts();
891 assert_eq!(kind, PublishErrorKind::NotReady);
892 assert_eq!(payload.request, *b"ping");
893 }
894
895 #[test]
896 fn reset_epoch_rejects_late_completion() {
897 let mut queue = TestQueue::new();
898 QueueControl::configure(&mut queue, QueueSize::new(2).unwrap()).unwrap();
899 QueueControl::set_ready(&mut queue, true).unwrap();
900 DriverQueue::publish(
901 &mut queue,
902 TestPayload {
903 request: *b"ping",
904 response: [0; 4],
905 },
906 )
907 .unwrap();
908 let mut chain = DeviceQueue::pop_available(&mut queue).unwrap().unwrap();
909 let old_epoch = chain.id().epoch();
910 let next_epoch = old_epoch.checked_next().unwrap();
911
912 DeviceQueue::reset(&mut queue, next_epoch).unwrap();
913 assert!(matches!(
914 chain.io(),
915 Err(ChainError::ResetRace { chain, current })
916 if chain == old_epoch && current == next_epoch
917 ));
918 assert_eq!(
919 DeviceQueue::complete(&mut queue, chain, UsedLength::new(0)),
920 Err(QueueError::ResetRace {
921 operation: old_epoch,
922 current: next_epoch,
923 })
924 );
925 assert!(queue.used.is_empty());
926 }
927}