//! Owned, read-only block requests. No allocation or DMA access occurs here. //! The driver retains the physical buffer until completion or an observed reset. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { InvalidQueue, InvalidHead, InvalidBuffer, InvalidDeadline, StaleDevice, Busy, IdExhausted, UnknownRequest, WrongState, WrongCompletion, DeviceFailure, NotReset, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum State { Prepared, Published } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Token { pub head: u16, pub id: u64, pub device_generation: u64 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Request { pub token: Token, pub buffer: u64, pub lba: u64, pub deadline: u64, pub state: State, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Snapshot { pub live: u16, pub device_generation: u64, pub next_id: u64, slots: [Option; N], } pub struct Requests { size: u16, slots: [Option; N], live: u16, device_generation: u64, next_id: u64, } impl Requests { pub fn new(size: u16, device_generation: u64) -> Result { if size == 0 || !size.is_power_of_two() || size > 1024 || usize::from(size) > N || device_generation == 0 { return Err(Error::InvalidQueue); } Ok(Self {size, slots: [None; N], live: 0, device_generation, next_id: 1}) } pub fn snapshot(&self) -> Snapshot { Snapshot {live: self.live, device_generation: self.device_generation, next_id: self.next_id, slots: self.slots} } pub fn prepare(&mut self, head: u16, buffer: u64, lba: u64, deadline: u64, now: u64, device_generation: u64) -> Result { if device_generation != self.device_generation { return Err(Error::StaleDevice); } if head >= self.size { return Err(Error::InvalidHead); } if buffer == 0 || buffer & 511 != 0 || buffer.checked_add(512).is_none() { return Err(Error::InvalidBuffer); } if deadline <= now { return Err(Error::InvalidDeadline); } if self.slots[usize::from(head)].is_some() { return Err(Error::Busy); } let next = self.next_id.checked_add(1).ok_or(Error::IdExhausted)?; let token = Token {head, id: self.next_id, device_generation}; self.slots[usize::from(head)] = Some(Request { token, buffer, lba, deadline, state: State::Prepared, }); self.next_id = next; self.live += 1; Ok(token) } fn slot(&self, token: Token) -> Result { if token.device_generation != self.device_generation { return Err(Error::StaleDevice); } let request = self.slots.get(usize::from(token.head)) .and_then(|slot| *slot).ok_or(Error::UnknownRequest)?; if request.token != token { return Err(Error::WrongCompletion); } Ok(request) } pub fn request(&self, token: Token) -> Result { self.slot(token) } /// Only a prepared request can be rolled back without a device reset. pub fn abort_prepared(&mut self, token: Token) -> Result { let request = self.slot(token)?; if request.state != State::Prepared { return Err(Error::WrongState); } self.slots[usize::from(token.head)] = None; self.live -= 1; Ok(request) } pub fn publish(&mut self, token: Token, now: u64) -> Result<(), Error> { let request = self.slot(token)?; if request.state != State::Prepared { return Err(Error::WrongState); } if now >= request.deadline { return Err(Error::InvalidDeadline); } self.slots[usize::from(token.head)].as_mut().unwrap().state = State::Published; Ok(()) } /// A timed-out published request still owns its buffer until reset. pub fn expired(&self, token: Token, now: u64) -> Result { let request = self.slot(token)?; if request.state != State::Published { return Err(Error::WrongState); } Ok(now >= request.deadline) } /// The caller has already observed a used element and validated its DMA /// length/status. An unrelated token cannot consume this request's buffer. pub fn complete(&mut self, token: Token, used_head: u32, used_length: u32, status: u8) -> Result { let request = self.slot(token)?; if request.state != State::Published { return Err(Error::WrongState); } if used_head != u32::from(token.head) || used_length != 513 { return Err(Error::WrongCompletion); } if status != 0 { return Err(Error::DeviceFailure); } self.slots[usize::from(token.head)] = None; self.live -= 1; Ok(request) } /// Called only after device status and QueueReady have both been read as 0. /// Incrementing the generation invalidates all old completion tokens. pub fn observe_reset(&mut self, status: u8, queue_ready: u16, new_generation: u64) -> Result<(), Error> { if status != 0 || queue_ready != 0 || new_generation <= self.device_generation { return Err(Error::NotReset); } self.slots = [None; N]; self.live = 0; self.device_generation = new_generation; Ok(()) } }