use crate::error::{KernelError, Result};
pub const BLK_MQ_MAX_DEPTH: u16 = 32768;
pub const BLK_MQ_MAX_HW_QUEUES: u16 = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum RequestOp {
Read = 0,
Write = 1,
Flush = 2,
Discard = 3,
WriteZeroes = 4,
SecureErase = 5,
ZoneReset = 6,
ZoneOpen = 7,
ZoneClose = 8,
ZoneFinish = 9,
}
impl RequestOp {
#[must_use]
pub const fn is_read(&self) -> bool {
matches!(self, Self::Read)
}
#[must_use]
pub const fn is_write(&self) -> bool {
matches!(self, Self::Write)
}
#[must_use]
pub const fn has_data(&self) -> bool {
matches!(self, Self::Read | Self::Write)
}
#[must_use]
pub const fn is_zone_op(&self) -> bool {
matches!(
self,
Self::ZoneReset | Self::ZoneOpen | Self::ZoneClose | Self::ZoneFinish
)
}
#[must_use]
pub const fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Read),
1 => Some(Self::Write),
2 => Some(Self::Flush),
3 => Some(Self::Discard),
4 => Some(Self::WriteZeroes),
5 => Some(Self::SecureErase),
6 => Some(Self::ZoneReset),
7 => Some(Self::ZoneOpen),
8 => Some(Self::ZoneClose),
9 => Some(Self::ZoneFinish),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct RequestFlags(u32);
impl RequestFlags {
pub const NONE: Self = Self(0);
pub const FUA: Self = Self(1 << 0);
pub const META: Self = Self(1 << 1);
pub const SYNC: Self = Self(1 << 2);
pub const NOWAIT: Self = Self(1 << 3);
#[must_use]
pub const fn new() -> Self {
Self::NONE
}
#[must_use]
pub const fn with_fua(self) -> Self {
Self(self.0 | Self::FUA.0)
}
#[must_use]
pub const fn with_sync(self) -> Self {
Self(self.0 | Self::SYNC.0)
}
#[must_use]
pub const fn with_nowait(self) -> Self {
Self(self.0 | Self::NOWAIT.0)
}
#[must_use]
pub const fn is_fua(&self) -> bool {
(self.0 & Self::FUA.0) != 0
}
#[must_use]
pub const fn is_sync(&self) -> bool {
(self.0 & Self::SYNC.0) != 0
}
#[must_use]
pub const fn is_nowait(&self) -> bool {
(self.0 & Self::NOWAIT.0) != 0
}
#[must_use]
pub const fn bits(&self) -> u32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BioVec {
pub addr: u64,
pub len: u32,
pub offset: u32,
}
impl BioVec {
#[must_use]
pub const fn new(addr: u64, len: u32) -> Self {
Self {
addr,
len,
offset: 0,
}
}
#[must_use]
pub const fn with_offset(addr: u64, len: u32, offset: u32) -> Self {
Self { addr, len, offset }
}
#[must_use]
pub const fn effective_addr(&self) -> u64 {
self.addr + self.offset as u64
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
}
impl Default for BioVec {
fn default() -> Self {
Self::new(0, 0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Request {
tag: u16,
queue_id: u16,
op: RequestOp,
flags: RequestFlags,
sector: u64,
nr_sectors: u32,
bio_vec: BioVec,
}
impl Request {
#[must_use]
pub const fn new(tag: u16, queue_id: u16, op: RequestOp) -> Self {
Self {
tag,
queue_id,
op,
flags: RequestFlags::NONE,
sector: 0,
nr_sectors: 0,
bio_vec: BioVec::new(0, 0),
}
}
#[must_use]
pub const fn read(tag: u16, queue_id: u16, sector: u64, nr_sectors: u32) -> Self {
Self {
tag,
queue_id,
op: RequestOp::Read,
flags: RequestFlags::NONE,
sector,
nr_sectors,
bio_vec: BioVec::new(0, 0),
}
}
#[must_use]
pub const fn write(tag: u16, queue_id: u16, sector: u64, nr_sectors: u32) -> Self {
Self {
tag,
queue_id,
op: RequestOp::Write,
flags: RequestFlags::NONE,
sector,
nr_sectors,
bio_vec: BioVec::new(0, 0),
}
}
#[must_use]
pub const fn tag(&self) -> u16 {
self.tag
}
#[must_use]
pub const fn queue_id(&self) -> u16 {
self.queue_id
}
#[must_use]
pub const fn op(&self) -> RequestOp {
self.op
}
#[must_use]
pub const fn flags(&self) -> RequestFlags {
self.flags
}
#[must_use]
pub const fn sector(&self) -> u64 {
self.sector
}
#[must_use]
pub const fn nr_sectors(&self) -> u32 {
self.nr_sectors
}
#[must_use]
pub const fn bio_vec(&self) -> &BioVec {
&self.bio_vec
}
pub fn set_sector_range(&mut self, sector: u64, nr_sectors: u32) {
self.sector = sector;
self.nr_sectors = nr_sectors;
}
pub fn set_bio_vec(&mut self, bio_vec: BioVec) {
self.bio_vec = bio_vec;
}
pub fn set_flags(&mut self, flags: RequestFlags) {
self.flags = flags;
}
#[must_use]
pub const fn byte_offset(&self) -> u64 {
self.sector * 512
}
#[must_use]
pub const fn byte_len(&self) -> u64 {
self.nr_sectors as u64 * 512
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TagSetConfig {
pub nr_hw_queues: u16,
pub queue_depth: u16,
pub numa_node: i32,
pub flags: u32,
}
impl TagSetConfig {
#[must_use]
pub const fn new(nr_hw_queues: u16, queue_depth: u16) -> Self {
Self {
nr_hw_queues,
queue_depth,
numa_node: -1,
flags: 0,
}
}
#[must_use]
pub const fn with_numa_node(mut self, node: i32) -> Self {
self.numa_node = node;
self
}
pub fn validate(&self) -> Result<()> {
if self.nr_hw_queues == 0 {
return Err(KernelError::InvalidArgument);
}
if self.nr_hw_queues > BLK_MQ_MAX_HW_QUEUES {
return Err(KernelError::InvalidArgument);
}
if self.queue_depth == 0 {
return Err(KernelError::InvalidArgument);
}
if self.queue_depth > BLK_MQ_MAX_DEPTH {
return Err(KernelError::InvalidArgument);
}
Ok(())
}
#[must_use]
pub const fn total_tags(&self) -> u32 {
self.nr_hw_queues as u32 * self.queue_depth as u32
}
}
impl Default for TagSetConfig {
fn default() -> Self {
Self::new(1, 128)
}
}
pub trait BlockOps: Send + Sync {
type QueueData: Send + Sync;
fn queue_rq(
queue_data: &Self::QueueData,
request: &Request,
is_last: bool,
) -> Result<()>;
fn commit_rqs(queue_data: &Self::QueueData);
fn complete(request: &Request, result: i32);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_request_op_is_read() {
assert!(RequestOp::Read.is_read());
assert!(!RequestOp::Write.is_read());
assert!(!RequestOp::Flush.is_read());
}
#[test]
fn test_request_op_is_write() {
assert!(RequestOp::Write.is_write());
assert!(!RequestOp::Read.is_write());
assert!(!RequestOp::Discard.is_write());
}
#[test]
fn test_request_op_has_data() {
assert!(RequestOp::Read.has_data());
assert!(RequestOp::Write.has_data());
assert!(!RequestOp::Flush.has_data());
assert!(!RequestOp::Discard.has_data());
}
#[test]
fn test_request_op_is_zone_op() {
assert!(RequestOp::ZoneReset.is_zone_op());
assert!(RequestOp::ZoneOpen.is_zone_op());
assert!(!RequestOp::Read.is_zone_op());
assert!(!RequestOp::Write.is_zone_op());
}
#[test]
fn test_request_op_from_u8() {
assert_eq!(RequestOp::from_u8(0), Some(RequestOp::Read));
assert_eq!(RequestOp::from_u8(1), Some(RequestOp::Write));
assert_eq!(RequestOp::from_u8(2), Some(RequestOp::Flush));
assert_eq!(RequestOp::from_u8(100), None);
}
#[test]
fn test_request_op_repr() {
assert_eq!(RequestOp::Read as u8, 0);
assert_eq!(RequestOp::Write as u8, 1);
assert_eq!(RequestOp::Flush as u8, 2);
}
#[test]
fn test_request_flags_default() {
let flags = RequestFlags::default();
assert!(!flags.is_fua());
assert!(!flags.is_sync());
assert!(!flags.is_nowait());
}
#[test]
fn test_request_flags_builders() {
let flags = RequestFlags::new().with_fua().with_sync();
assert!(flags.is_fua());
assert!(flags.is_sync());
assert!(!flags.is_nowait());
}
#[test]
fn test_request_flags_bits() {
assert_eq!(RequestFlags::NONE.bits(), 0);
assert_ne!(RequestFlags::FUA.bits(), 0);
assert_ne!(RequestFlags::SYNC.bits(), 0);
}
#[test]
fn test_bio_vec_new() {
let bv = BioVec::new(0x1000, 4096);
assert_eq!(bv.addr, 0x1000);
assert_eq!(bv.len, 4096);
assert_eq!(bv.offset, 0);
}
#[test]
fn test_bio_vec_with_offset() {
let bv = BioVec::with_offset(0x1000, 4096, 512);
assert_eq!(bv.effective_addr(), 0x1000 + 512);
}
#[test]
fn test_bio_vec_is_empty() {
assert!(BioVec::new(0, 0).is_empty());
assert!(!BioVec::new(0x1000, 1).is_empty());
}
#[test]
fn test_request_new() {
let req = Request::new(10, 0, RequestOp::Read);
assert_eq!(req.tag(), 10);
assert_eq!(req.queue_id(), 0);
assert_eq!(req.op(), RequestOp::Read);
}
#[test]
fn test_request_read() {
let req = Request::read(5, 1, 1000, 8);
assert_eq!(req.tag(), 5);
assert_eq!(req.queue_id(), 1);
assert_eq!(req.op(), RequestOp::Read);
assert_eq!(req.sector(), 1000);
assert_eq!(req.nr_sectors(), 8);
}
#[test]
fn test_request_write() {
let req = Request::write(6, 2, 2000, 16);
assert_eq!(req.op(), RequestOp::Write);
assert_eq!(req.sector(), 2000);
assert_eq!(req.nr_sectors(), 16);
}
#[test]
fn test_request_byte_calculations() {
let req = Request::read(0, 0, 100, 8);
assert_eq!(req.byte_offset(), 100 * 512);
assert_eq!(req.byte_len(), 8 * 512);
}
#[test]
fn test_request_setters() {
let mut req = Request::new(0, 0, RequestOp::Read);
req.set_sector_range(500, 32);
assert_eq!(req.sector(), 500);
assert_eq!(req.nr_sectors(), 32);
req.set_bio_vec(BioVec::new(0x2000, 16384));
assert_eq!(req.bio_vec().addr, 0x2000);
req.set_flags(RequestFlags::FUA);
assert!(req.flags().is_fua());
}
#[test]
fn test_tag_set_config_new() {
let config = TagSetConfig::new(4, 128);
assert_eq!(config.nr_hw_queues, 4);
assert_eq!(config.queue_depth, 128);
assert_eq!(config.numa_node, -1);
}
#[test]
fn test_tag_set_config_with_numa() {
let config = TagSetConfig::new(2, 64).with_numa_node(0);
assert_eq!(config.numa_node, 0);
}
#[test]
fn test_tag_set_config_validate() {
assert!(TagSetConfig::new(1, 1).validate().is_ok());
assert!(TagSetConfig::new(128, 32768).validate().is_ok());
assert!(TagSetConfig::new(0, 128).validate().is_err());
assert!(TagSetConfig::new(129, 128).validate().is_err()); assert!(TagSetConfig::new(1, 0).validate().is_err());
assert!(TagSetConfig::new(1, 32769).validate().is_err()); }
#[test]
fn test_tag_set_config_total_tags() {
let config = TagSetConfig::new(4, 128);
assert_eq!(config.total_tags(), 4 * 128);
}
#[test]
fn test_tag_set_config_default() {
let config = TagSetConfig::default();
assert_eq!(config.nr_hw_queues, 1);
assert_eq!(config.queue_depth, 128);
assert!(config.validate().is_ok());
}
#[test]
fn abi_max_queue_depth() {
assert_eq!(BLK_MQ_MAX_DEPTH, 32768, "max depth must be 32768");
}
#[test]
fn abi_tag_width() {
let max_tag: u16 = u16::MAX;
assert!(max_tag >= BLK_MQ_MAX_DEPTH);
}
#[test]
fn abi_queue_id_width() {
let max_queue: u16 = u16::MAX;
assert!(max_queue >= BLK_MQ_MAX_HW_QUEUES);
}
}