[PATCH 44/79] block: rust: add an abstraction for `bindings::req_op`

Andreas Hindborg
2026-02-15T23:46:56Z

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 <a.hindborg@kernel.org>
---
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<T: Operations> RequestInner<T> {
/// 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