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 | 18 +++++---- rust/kernel/block/mq/request/command.rs | 65 +++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs index 491979daa50e..5ec17a2674b7 100644 --- a/drivers/block/rnull/rnull.rs +++ b/drivers/block/rnull/rnull.rs @@ -547,10 +547,10 @@ fn transfer( let length_sectors_allowed = segment_length_sectors.min(max_remaining_sectors); segment.truncate(length_sectors_allowed << 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)? } _ => (), @@ -743,7 +743,7 @@ fn queue_rq( if this.memory_backed { memalloc_scope!(let _noio: NoIo); - 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 503623267b19..5bf2cf2736a5 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -132,6 +132,7 @@ pub use operations::Operations; pub use request::{ + Command, IdleRequest, Request, RequestTimerHandle, // diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs index a05df2351c2c..63e248970ab1 100644 --- a/rust/kernel/block/mq/request.rs +++ b/rust/kernel/block/mq/request.rs @@ -45,6 +45,9 @@ 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`]. @@ -111,11 +114,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 { @@ -242,13 +251,6 @@ pub(crate) unsafe fn aref_from_raw(ptr: *mut bindings::request) -> ARef { unsafe { ARef::from_raw(NonNull::new_unchecked(ptr.cast())) } } - /// Get the command identifier for the request - pub fn command(&self) -> u32 { - use core::ops::BitAnd; - // SAFETY: By C API contract and type invariant, `cmd_flags` is valid for read - unsafe { (*self.0 .0.get()).cmd_flags }.bitand((1u32 << bindings::REQ_OP_BITS) - 1) - } - /// Complete the request by scheduling `Operations::complete` for /// execution. /// diff --git a/rust/kernel/block/mq/request/command.rs b/rust/kernel/block/mq/request/command.rs new file mode 100644 index 000000000000..70a8d67fa35c --- /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