//! A page-resident, single-producer/single-consumer ring for two EL0 mappings. //! The kernel must validate ownership and map this object only into its two //! authorized processes. Neither endpoint is a scheduler or a syscall adapter. use core::sync::atomic::{AtomicU64, Ordering}; pub const CAPACITY: u64 = 16; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { Full, Empty, InvalidHeader, Exhausted } #[repr(C, align(64))] pub struct SharedRing { pub producer: AtomicU64, pub consumer: AtomicU64, pub slots: [AtomicU64; CAPACITY as usize], } impl SharedRing { pub const fn new() -> Self { Self { producer: AtomicU64::new(0), consumer: AtomicU64::new(0), slots: [const { AtomicU64::new(0) }; CAPACITY as usize], } } pub fn endpoints(&self) -> (Producer<'_>, Consumer<'_>) { (Producer { page: self, next: 0 }, Consumer { page: self, next: 0 }) } } pub struct Producer<'a> { page: &'a SharedRing, next: u64 } pub struct Consumer<'a> { page: &'a SharedRing, next: u64 } impl Producer<'_> { pub fn try_push(&mut self, value: u64) -> Result<(), Error> { let published = self.page.producer.load(Ordering::Relaxed); if published != self.next { return Err(Error::InvalidHeader); } let consumed = self.page.consumer.load(Ordering::Acquire); let distance = published.checked_sub(consumed).ok_or(Error::InvalidHeader)?; if distance > CAPACITY { return Err(Error::InvalidHeader); } if distance == CAPACITY { return Err(Error::Full); } let next = published.checked_add(1).ok_or(Error::Exhausted)?; self.page.slots[(published % CAPACITY) as usize] .store(value, Ordering::Relaxed); self.page.producer.store(next, Ordering::Release); self.next = next; Ok(()) } pub fn published(&self) -> u64 { self.next } } impl Consumer<'_> { pub fn try_pop(&mut self) -> Result { let consumed = self.page.consumer.load(Ordering::Relaxed); if consumed != self.next { return Err(Error::InvalidHeader); } let published = self.page.producer.load(Ordering::Acquire); let distance = published.checked_sub(consumed).ok_or(Error::InvalidHeader)?; if distance > CAPACITY { return Err(Error::InvalidHeader); } if distance == 0 { return Err(Error::Empty); } let next = consumed.checked_add(1).ok_or(Error::Exhausted)?; let value = self.page.slots[(consumed % CAPACITY) as usize] .load(Ordering::Relaxed); self.page.consumer.store(next, Ordering::Release); self.next = next; Ok(value) } pub fn consumed(&self) -> u64 { self.next } }