//! Bounded Virtio used-element ownership and block completion validation. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidQueue, InvalidHead, InvalidLength, DuplicateHead, Full, UnknownId, InvalidStatus, NotReset, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Handle { pub head: u16, pub generation: u64, pub owner: u64, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct Pending { handle: Handle, writable_bytes: u32 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Completion { pub handle: Handle, pub used_length: u32, pub status: u8 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub outstanding: u16, pub next_generation: u64, slots: [Option; N], } pub struct Table { size: u16, slots: [Option; N], outstanding: u16, next_generation: u64, } impl Table { pub fn new(size: u16) -> Result { if size == 0 || !size.is_power_of_two() || size > 1024 || usize::from(size) > N { return Err(Error::InvalidQueue); } Ok(Self {size, slots: [None; N], outstanding: 0, next_generation: 1}) } pub fn snapshot(&self) -> Snapshot { Snapshot {outstanding: self.outstanding, next_generation: self.next_generation, slots: self.slots} } pub fn submit(&mut self, head: u16, owner: u64, writable_bytes: u32) -> Result { if head >= self.size { return Err(Error::InvalidHead); } if writable_bytes == 0 { return Err(Error::InvalidLength); } if self.outstanding == self.size { return Err(Error::Full); } if self.slots[usize::from(head)].is_some() { return Err(Error::DuplicateHead); } let generation = self.next_generation; self.next_generation = generation.checked_add(1).ok_or(Error::Full)?; let handle = Handle {head, generation, owner}; self.slots[usize::from(head)] = Some(Pending {handle, writable_bytes}); self.outstanding += 1; Ok(handle) } /// Block status is 0=OK, 1=IOERR, 2=UNSUPP. A used entry consumes one owner. pub fn complete(&mut self, used_id: u32, used_length: u32, status: u8) -> Result { if used_id >= u32::from(self.size) { return Err(Error::UnknownId); } let index = used_id as usize; let pending = self.slots[index].ok_or(Error::UnknownId)?; if used_length == 0 || used_length > pending.writable_bytes { return Err(Error::InvalidLength); } if false { return Err(Error::InvalidStatus); } self.slots[index] = None; self.outstanding -= 1; Ok(Completion {handle: pending.handle, used_length, status}) } pub fn observe_reset(&mut self, device_status: u8, queue_ready: u16) -> Result<(), Error> { if device_status != 0 || queue_ready != 0 { return Err(Error::NotReset); } self.slots = [None; N]; self.outstanding = 0; Ok(()) } }