Add the `RequestList` type as a safe wrapper around the C `struct rq_list`. This type provides methods to iterate over and manipulate lists of block requests, which is needed for implementing the `queue_rqs` callback. The abstraction includes methods for popping requests from the list, checking if the list is empty, and peeking at the head request. Signed-off-by: Andreas Hindborg --- rust/helpers/blk.c | 26 ++++++++ rust/kernel/block/mq.rs | 2 + rust/kernel/block/mq/request_list.rs | 113 +++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) diff --git a/rust/helpers/blk.c b/rust/helpers/blk.c index 91b72131a04cd..0db567dfc5d53 100644 --- a/rust/helpers/blk.c +++ b/rust/helpers/blk.c @@ -27,3 +27,29 @@ bool rust_helper_blk_mq_add_to_batch(struct request *req, { return blk_mq_add_to_batch(req, iob, is_error, complete); } + +__rust_helper struct request *rust_helper_rq_list_pop(struct rq_list *rl) +{ + return rq_list_pop(rl); +} + +__rust_helper int rust_helper_rq_list_empty(const struct rq_list *rl) +{ + return rq_list_empty(rl); +} + +__rust_helper void rust_helper_rq_list_add_tail(struct rq_list *rl, + struct request *rq) +{ + rq_list_add_tail(rl, rq); +} + +__rust_helper void rust_helper_rq_list_init(struct rq_list *rl) +{ + rq_list_init(rl); +} + +__rust_helper struct request *rust_helper_rq_list_peek(struct rq_list *rl) +{ + return rq_list_peek(rl); +} diff --git a/rust/kernel/block/mq.rs b/rust/kernel/block/mq.rs index b36112f7b22b9..786c32f2cb56c 100644 --- a/rust/kernel/block/mq.rs +++ b/rust/kernel/block/mq.rs @@ -129,6 +129,7 @@ pub mod gen_disk; mod operations; mod request; +mod request_list; mod request_queue; pub mod tag_set; @@ -141,6 +142,7 @@ pub use request::IdleRequest; pub use request::Request; pub use request::RequestTimerHandle; +pub use request_list::RequestList; pub use request_queue::RequestQueue; pub use tag_set::QueueType; pub use tag_set::TagSet; diff --git a/rust/kernel/block/mq/request_list.rs b/rust/kernel/block/mq/request_list.rs new file mode 100644 index 0000000000000..60284d9ef6c9c --- /dev/null +++ b/rust/kernel/block/mq/request_list.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 + +use core::marker::PhantomData; + +use crate::{owned::Owned, types::Opaque}; + +use super::{IdleRequest, Operations}; + +/// A list of [`Request`]. +/// +/// # INVARIANTS +/// +/// - `self.inner` is always a valid list, meaning the `next` and `prev` +/// pointers point to valid requests, or are both null. +/// - All requests in the list are valid for use as `IdleRequest`. +#[repr(transparent)] +pub struct RequestList { + inner: Opaque, + _p: PhantomData, +} + +impl RequestList { + /// Create a new [`RequestList`]. + pub fn new() -> Self { + let this = Self { + inner: Opaque::zeroed(), + _p: PhantomData, + }; + + // NOTE: We are actually good to go, but we call the C initializer for forward + // compatibility. + // SAFETY: `this.inner` is a valid allocation for use as `bindings::rq_list!. + unsafe { bindings::rq_list_init(this.inner.get()) } + + //INVARIANT: `self.inner` was initialized above and is empty. + this + } + + /// Create a mutable reference to a [`RequestList`] from a raw pointer. + /// + /// # SAFETY + /// - The list pointed to by `ptr` must satisfy the invariants of `Self`. + /// - The list pointed to by `ptr` must remain valid for use as a mutable reference for the + /// duration of `'a`. + pub unsafe fn from_raw<'a>(ptr: *mut bindings::rq_list) -> &'a mut Self { + // SAFETY: + // - RequestList is transparent. + // - By function safety requirements, `ptr` is valid for us as a mutable reference. + unsafe { &mut (*ptr.cast()) } + } + + /// Check if the list is empty. + pub fn empty(&self) -> bool { + // SAFETY: By type invariant, self.inner is valid. + let ret = unsafe { bindings::rq_list_empty(self.inner.get()) }; + ret != 0 + } + + /// Pop a request from the list. + /// + /// Returns [`None`] if the list is empty. + pub fn pop(&mut self) -> Option>> { + // SAFETY: By type invariant `self.inner` is a valid list. + let ptr = unsafe { bindings::rq_list_pop(self.inner.get()) }; + + if !ptr.is_null() { + // SAFETY: If `rq_list_pop` returns a non-null pointer, it points to a valid request. By + // type invariant all requests in this list are valid for use as `IdleRequest`. + Some(unsafe { IdleRequest::from_raw(ptr) }) + } else { + None + } + } + + /// Push a request on the tail of the list. + pub fn push_tail(&mut self, rq: Owned>) { + let ptr = rq.as_raw(); + core::mem::forget(rq); + // INVARIANT: rq is an `IdleRequest`. + // SAFETY: By type invariant, `self.inner` is a valid list. + unsafe { bindings::rq_list_add_tail(self.inner.get(), ptr) }; + } + + /// Peek at the head of the list. + /// + /// Returns a null pointer if the list is empty. + pub fn peek_raw(&self) -> *mut bindings::request { + // SAFETY: By type invariant, `self.inner` is a valid list. + unsafe { bindings::rq_list_peek(self.inner.get()) } + } +} + +impl Default for RequestList { + fn default() -> Self { + Self::new() + } +} + +impl Drop for RequestList { + fn drop(&mut self) { + while let Some(rq) = self.pop() { + drop(rq) + } + } +} + +impl Iterator for &mut RequestList { + type Item = Owned>; + + fn next(&mut self) -> Option { + self.pop() + } +} -- 2.51.2