Add the `Command` enum as a Rust abstraction for block request operation codes. The enum variants correspond to the C `REQ_OP_*` defines and include read, write, flush, discard, and zone management operations. Also add a `command()` method to `Request` to retrieve the operation code. Signed-off-by: Andreas Hindborg --- drivers/block/rnull/rnull.rs | 6 +-- rust/kernel/block/mq.rs | 1 + rust/kernel/block/mq/request.rs | 11 +++++- rust/kernel/block/mq/request/command.rs | 65 +++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 3fb19836ce6ff..9383b82f9a736 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -508,10 +508,10 @@ fn transfer( .len() .min((end_sector - sector) as u32 >> SECTOR_SHIFT); match command { - bindings::req_op_REQ_OP_WRITE => { + mq::Command::Write => { self.write(&mut tree_guard, &mut hw_data_guard, sector, segment)? } - bindings::req_op_REQ_OP_READ => { + mq::Command::Read => { self.read(&mut tree_guard, &mut hw_data_guard, sector, segment)? } _ => (), @@ -703,7 +703,7 @@ fn queue_rq( Self::handle_bad_blocks(this.deref(), &mut rq, &mut sectors)?; if this.memory_backed { - if rq.command() == bindings::req_op_REQ_OP_DISCARD { + if rq.command() == mq::Command::Discard { this.discard(&hw_data, rq.sector(), sectors)?; } else { this.transfer(&hw_data, &mut rq, sectors)?; diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index 02738f52389ba..057a5f366be3a 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -131,6 +131,7 @@ pub mod tag_set; pub use operations::Operations; +pub use request::Command; pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs index d6f3ffde1c5c4..4e47419776e0f 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -23,6 +23,9 @@ use crate::block::bio::Bio; use crate::block::bio::BioIterator; +mod command; +pub use command::Command; + /// A [`Request`] that a driver has not yet begun to process. /// /// A driver can convert an `IdleRequest` to a [`Request`] by calling [`IdleRequest::start`]. @@ -89,11 +92,17 @@ fn deref(&self) -> &Self::Target { impl RequestInner { /// Get the command identifier for the request - pub fn command(&self) -> u32 { + fn command_raw(&self) -> u32 { // SAFETY: By C API contract and type invariant, `cmd_flags` is valid for read unsafe { (*self.0.get()).cmd_flags & ((1 << bindings::REQ_OP_BITS) - 1) } } + /// Get the command of this request. + pub fn command(&self) -> Command { + // SAFETY: By type invariant of `Self`, `self.0` is valid and live. + unsafe { Command::from_raw(self.command_raw()) } + } + /// Get the target sector for the request. #[inline(always)] pub fn sector(&self) -> u64 { diff --git a/rust/kernel/block/mq/request/command.rs b/rust/kernel/block/mq/request/command.rs new file mode 100644 index 0000000000000..70a8d67fa35c0 --- /dev/null +++ b/rust/kernel/block/mq/request/command.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 + +/// Block I/O operation codes. +/// +/// This is the Rust abstraction for the C [`enum req_op`]. +/// +/// Operations common to the bio and request structures. The kernel uses 8 bits +/// for encoding the operation, and the remaining 24 bits for flags. +/// +/// The least significant bit of the operation number indicates the data +/// transfer direction: +/// +/// - If the least significant bit is set, transfers are TO the device. +/// - If the least significant bit is not set, transfers are FROM the device. +/// +/// If an operation does not transfer data, the least significant bit has no +/// meaning. +/// +/// [`enum req_op`]: srctree/include/linux/blk_types.h +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[repr(u32)] +pub enum Command { + /// Read sectors from the device. + Read = bindings::req_op_REQ_OP_READ, + /// Write sectors to the device. + Write = bindings::req_op_REQ_OP_WRITE, + /// Flush the volatile write cache. + Flush = bindings::req_op_REQ_OP_FLUSH, + /// Discard sectors. + Discard = bindings::req_op_REQ_OP_DISCARD, + /// Securely erase sectors. + SecureErase = bindings::req_op_REQ_OP_SECURE_ERASE, + /// Write data at the current zone write pointer. + ZoneAppend = bindings::req_op_REQ_OP_ZONE_APPEND, + /// Write zeroes. This allows to implement zeroing for devices that don't use either discard + /// with a predictable zero pattern or WRITE SAME of zeroes. + WriteZeroes = bindings::req_op_REQ_OP_WRITE_ZEROES, + /// Open a zone. + ZoneOpen = bindings::req_op_REQ_OP_ZONE_OPEN, + /// Close a zone. + ZoneClose = bindings::req_op_REQ_OP_ZONE_CLOSE, + /// Transition a zone to full. + ZoneFinish = bindings::req_op_REQ_OP_ZONE_FINISH, + /// Reset a zone write pointer. + ZoneReset = bindings::req_op_REQ_OP_ZONE_RESET, + /// Reset all the zones present on the device. + ZoneResetAll = bindings::req_op_REQ_OP_ZONE_RESET_ALL, + /// Driver private request for data transfer to the driver. + DriverIn = bindings::req_op_REQ_OP_DRV_IN, + /// Driver private request for data transfer from the driver. + DriverOut = bindings::req_op_REQ_OP_DRV_OUT, +} + +impl Command { + /// Creates a [`Command`] from a raw `u32` value. + /// + /// # Safety + /// + /// The value must be a valid `req_op` operation code. + pub unsafe fn from_raw(value: u32) -> Self { + // SAFETY: The caller guarantees that the value is a valid operation + // code. + unsafe { core::mem::transmute(value) } + } +} -- 2.51.2