mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Andreas Hindborg <nmi@metaspace.dk>
To: Jens Axboe <axboe@kernel.dk>, Christoph Hellwig <hch@lst.de>,
	Keith Busch <kbusch@kernel.org>,
	Damien Le Moal <Damien.LeMoal@wdc.com>,
	Hannes Reinecke <hare@suse.de>,
	lsf-pc@lists.linux-foundation.org,
	rust-for-linux@vger.kernel.org, linux-block@vger.kernel.org
Cc: "Andreas Hindborg" <a.hindborg@samsung.com>,
	"Matthew Wilcox" <willy@infradead.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Wedson Almeida Filho" <wedsonaf@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <benno.lossin@proton.me>,
	"Andreas Hindborg" <nmi@metaspace.dk>,
	linux-kernel@vger.kernel.org (open list),
	gost.dev@samsung.com
Subject: [RFC PATCH 04/11] rust: block: introduce `kernel::block::bio` module
Date: Wed,  3 May 2023 11:07:01 +0200	[thread overview]
Message-ID: <20230503090708.2524310-5-nmi@metaspace.dk> (raw)
In-Reply-To: <20230503090708.2524310-1-nmi@metaspace.dk>

From: Andreas Hindborg <a.hindborg@samsung.com>

Add abstractions for working with `struct bio`.

Signed-off-by: Andreas Hindborg <a.hindborg@samsung.com>
---
 rust/kernel/block.rs            |   1 +
 rust/kernel/block/bio.rs        |  93 ++++++++++++++++
 rust/kernel/block/bio/vec.rs    | 181 ++++++++++++++++++++++++++++++++
 rust/kernel/block/mq/request.rs |  16 +++
 4 files changed, 291 insertions(+)
 create mode 100644 rust/kernel/block/bio.rs
 create mode 100644 rust/kernel/block/bio/vec.rs

diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs
index 4c93317a568a..1797859551fd 100644
--- a/rust/kernel/block.rs
+++ b/rust/kernel/block.rs
@@ -2,4 +2,5 @@
 
 //! Types for working with the block layer
 
+pub mod bio;
 pub mod mq;
diff --git a/rust/kernel/block/bio.rs b/rust/kernel/block/bio.rs
new file mode 100644
index 000000000000..6e93e4420105
--- /dev/null
+++ b/rust/kernel/block/bio.rs
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Types for working with the bio layer.
+//!
+//! C header: [`include/linux/blk_types.h`](../../include/linux/blk_types.h)
+
+use core::fmt;
+use core::ptr::NonNull;
+
+mod vec;
+
+pub use vec::BioSegmentIterator;
+pub use vec::Segment;
+
+/// A wrapper around a `struct bio` pointer
+///
+/// # Invariants
+///
+/// First field must alwyas be a valid pointer to a valid `struct bio`.
+pub struct Bio<'a>(
+    NonNull<crate::bindings::bio>,
+    core::marker::PhantomData<&'a ()>,
+);
+
+impl<'a> Bio<'a> {
+    /// Returns an iterator over segments in this `Bio`. Does not consider
+    /// segments of other bios in this bio chain.
+    #[inline(always)]
+    pub fn segment_iter(&'a self) -> BioSegmentIterator<'a> {
+        BioSegmentIterator::new(self)
+    }
+
+    /// Get a pointer to the `bio_vec` off this bio
+    #[inline(always)]
+    fn io_vec(&self) -> *const bindings::bio_vec {
+        // SAFETY: By type invariant, get_raw() returns a valid pointer to a
+        // valid `struct bio`
+        unsafe { (*self.get_raw()).bi_io_vec }
+    }
+
+    /// Return a copy of the `bvec_iter` for this `Bio`
+    #[inline(always)]
+    fn iter(&self) -> bindings::bvec_iter {
+        // SAFETY: self.0 is always a valid pointer
+        unsafe { (*self.get_raw()).bi_iter }
+    }
+
+    /// Get the next `Bio` in the chain
+    #[inline(always)]
+    fn next(&self) -> Option<Bio<'a>> {
+        // SAFETY: self.0 is always a valid pointer
+        let next = unsafe { (*self.get_raw()).bi_next };
+        Some(Self(NonNull::new(next)?, core::marker::PhantomData))
+    }
+
+    /// Return the raw pointer of the wrapped `struct bio`
+    #[inline(always)]
+    fn get_raw(&self) -> *const bindings::bio {
+        self.0.as_ptr()
+    }
+
+    /// Create an instance of `Bio` from a raw pointer. Does check that the
+    /// pointer is not null.
+    #[inline(always)]
+    pub(crate) unsafe fn from_raw(ptr: *mut bindings::bio) -> Option<Bio<'a>> {
+        Some(Self(NonNull::new(ptr)?, core::marker::PhantomData))
+    }
+}
+
+impl core::fmt::Display for Bio<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Bio {:?}", self.0.as_ptr())
+    }
+}
+
+/// An iterator over `Bio`
+pub struct BioIterator<'a> {
+    pub(crate) bio: Option<Bio<'a>>,
+}
+
+impl<'a> core::iter::Iterator for BioIterator<'a> {
+    type Item = Bio<'a>;
+
+    #[inline(always)]
+    fn next(&mut self) -> Option<Bio<'a>> {
+        if let Some(current) = self.bio.take() {
+            self.bio = current.next();
+            Some(current)
+        } else {
+            None
+        }
+    }
+}
diff --git a/rust/kernel/block/bio/vec.rs b/rust/kernel/block/bio/vec.rs
new file mode 100644
index 000000000000..acd328a6fe54
--- /dev/null
+++ b/rust/kernel/block/bio/vec.rs
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Types for working with `struct bio_vec` IO vectors
+//!
+//! C header: [`include/linux/bvec.h`](../../include/linux/bvec.h)
+
+use super::Bio;
+use crate::error::Result;
+use crate::pages::Pages;
+use core::fmt;
+use core::mem::ManuallyDrop;
+
+#[inline(always)]
+fn mp_bvec_iter_offset(bvec: *const bindings::bio_vec, iter: &bindings::bvec_iter) -> u32 {
+    (unsafe { (*bvec_iter_bvec(bvec, iter)).bv_offset }) + iter.bi_bvec_done
+}
+
+#[inline(always)]
+fn mp_bvec_iter_page(
+    bvec: *const bindings::bio_vec,
+    iter: &bindings::bvec_iter,
+) -> *mut bindings::page {
+    unsafe { (*bvec_iter_bvec(bvec, iter)).bv_page }
+}
+
+#[inline(always)]
+fn mp_bvec_iter_page_idx(bvec: *const bindings::bio_vec, iter: &bindings::bvec_iter) -> usize {
+    (mp_bvec_iter_offset(bvec, iter) / crate::PAGE_SIZE) as usize
+}
+
+#[inline(always)]
+fn mp_bvec_iter_len(bvec: *const bindings::bio_vec, iter: &bindings::bvec_iter) -> u32 {
+    iter.bi_size
+        .min(unsafe { (*bvec_iter_bvec(bvec, iter)).bv_len } - iter.bi_bvec_done)
+}
+
+#[inline(always)]
+fn bvec_iter_bvec(
+    bvec: *const bindings::bio_vec,
+    iter: &bindings::bvec_iter,
+) -> *const bindings::bio_vec {
+    unsafe { bvec.add(iter.bi_idx as usize) }
+}
+
+#[inline(always)]
+fn bvec_iter_page(
+    bvec: *const bindings::bio_vec,
+    iter: &bindings::bvec_iter,
+) -> *mut bindings::page {
+    unsafe { mp_bvec_iter_page(bvec, iter).add(mp_bvec_iter_page_idx(bvec, iter)) }
+}
+
+#[inline(always)]
+fn bvec_iter_len(bvec: *const bindings::bio_vec, iter: &bindings::bvec_iter) -> u32 {
+    mp_bvec_iter_len(bvec, iter).min(crate::PAGE_SIZE - bvec_iter_offset(bvec, iter))
+}
+
+#[inline(always)]
+fn bvec_iter_offset(bvec: *const bindings::bio_vec, iter: &bindings::bvec_iter) -> u32 {
+    mp_bvec_iter_offset(bvec, iter) % crate::PAGE_SIZE
+}
+
+/// A wrapper around a `strutct bio_vec` - a contiguous range of physical memory addresses
+///
+/// # Invariants
+///
+/// `bio_vec` must always be initialized and valid
+pub struct Segment<'a> {
+    bio_vec: bindings::bio_vec,
+    _marker: core::marker::PhantomData<&'a ()>,
+}
+
+impl Segment<'_> {
+    /// Get he lenght of the segment in bytes
+    #[inline(always)]
+    pub fn len(&self) -> usize {
+        self.bio_vec.bv_len as usize
+    }
+
+    /// Returns true if the length of the segment is 0
+    #[inline(always)]
+    pub fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    /// Get the offset field of the `bio_vec`
+    #[inline(always)]
+    pub fn offset(&self) -> usize {
+        self.bio_vec.bv_offset as usize
+    }
+
+    /// Copy data of this segment into `page`.
+    #[inline(always)]
+    pub fn copy_to_page_atomic(&self, page: &mut Pages<0>) -> Result {
+        // SAFETY: self.bio_vec is valid and thus bv_page must be a valid
+        // pointer to a `struct page`. We do not own the page, but we prevent
+        // drop by wrapping the `Pages` in `ManuallyDrop`.
+        let our_page = ManuallyDrop::new(unsafe { Pages::<0>::from_raw(self.bio_vec.bv_page) });
+        let our_map = our_page.kmap_atomic();
+
+        // TODO: Checck offset is within page - what guarantees does `bio_vec` provide?
+        let ptr = unsafe { (our_map.get_ptr() as *const u8).add(self.offset()) };
+
+        unsafe { page.write_atomic(ptr, self.offset(), self.len()) }
+    }
+
+    /// Copy data from `page` into this segment
+    #[inline(always)]
+    pub fn copy_from_page_atomic(&mut self, page: &Pages<0>) -> Result {
+        // SAFETY: self.bio_vec is valid and thus bv_page must be a valid
+        // pointer to a `struct page`. We do not own the page, but we prevent
+        // drop by wrapping the `Pages` in `ManuallyDrop`.
+        let our_page = ManuallyDrop::new(unsafe { Pages::<0>::from_raw(self.bio_vec.bv_page) });
+        let our_map = our_page.kmap_atomic();
+
+        // TODO: Checck offset is within page
+        let ptr = unsafe { (our_map.get_ptr() as *mut u8).add(self.offset()) };
+
+        unsafe { page.read_atomic(ptr, self.offset(), self.len()) }
+    }
+}
+
+impl core::fmt::Display for Segment<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "Segment {:?} len: {}",
+            self.bio_vec.bv_page, self.bio_vec.bv_len
+        )
+    }
+}
+
+/// An iterator over `Segment`
+pub struct BioSegmentIterator<'a> {
+    bio: &'a Bio<'a>,
+    iter: bindings::bvec_iter,
+}
+
+impl<'a> BioSegmentIterator<'a> {
+    #[inline(always)]
+    pub(crate) fn new(bio: &'a Bio<'a>) -> BioSegmentIterator<'_> {
+        Self {
+            bio,
+            iter: bio.iter(),
+        }
+    }
+}
+
+impl<'a> core::iter::Iterator for BioSegmentIterator<'a> {
+    type Item = Segment<'a>;
+
+    #[inline(always)]
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.iter.bi_size == 0 {
+            return None;
+        }
+
+        // Macro
+        // bio_vec = bio_iter_iovec(bio, self.iter)
+        // bio_vec = bvec_iter_bvec(bio.bi_io_vec, self.iter);
+        let bio_vec_ret = bindings::bio_vec {
+            bv_page: bvec_iter_page(self.bio.io_vec(), &self.iter),
+            bv_len: bvec_iter_len(self.bio.io_vec(), &self.iter),
+            bv_offset: bvec_iter_offset(self.bio.io_vec(), &self.iter),
+        };
+
+        // Static C function
+        unsafe {
+            bindings::bio_advance_iter_single(
+                self.bio.get_raw(),
+                &mut self.iter as *mut bindings::bvec_iter,
+                bio_vec_ret.bv_len,
+            )
+        };
+
+        Some(Segment {
+            bio_vec: bio_vec_ret,
+            _marker: core::marker::PhantomData,
+        })
+    }
+}
diff --git a/rust/kernel/block/mq/request.rs b/rust/kernel/block/mq/request.rs
index e95ae3fd71ad..ccb1033b64b6 100644
--- a/rust/kernel/block/mq/request.rs
+++ b/rust/kernel/block/mq/request.rs
@@ -11,6 +11,9 @@ use crate::{
 };
 use core::marker::PhantomData;
 
+use crate::block::bio::Bio;
+use crate::block::bio::BioIterator;
+
 /// A wrapper around a blk-mq `struct request`. This represents an IO request.
 pub struct Request<T: Operations> {
     ptr: *mut bindings::request,
@@ -63,6 +66,19 @@ impl<T: Operations> Request<T> {
         }
     }
 
+    /// Get a wrapper for the first Bio in this request
+    #[inline(always)]
+    pub fn bio(&self) -> Option<Bio<'_>> {
+        let ptr = unsafe { (*self.ptr).bio };
+        unsafe { Bio::from_raw(ptr) }
+    }
+
+    /// Get an iterator over all bio structurs in this request
+    #[inline(always)]
+    pub fn bio_iter(&self) -> BioIterator<'_> {
+        BioIterator { bio: self.bio() }
+    }
+
     /// Get the target sector for the request
     #[inline(always)]
     pub fn sector(&self) -> usize {
-- 
2.40.0


  parent reply	other threads:[~2023-05-03  9:07 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-05-03  9:06 [RFC PATCH 00/11] Rust null block driver Andreas Hindborg
2023-05-03  9:06 ` [RFC PATCH 01/11] rust: add radix tree abstraction Andreas Hindborg
2023-05-03 10:34   ` Benno Lossin
2023-05-05  4:04   ` Matthew Wilcox
2023-05-05  4:49     ` Andreas Hindborg
2023-05-05  5:28       ` Matthew Wilcox
2023-05-05  6:09         ` Christoph Hellwig
2023-05-05  8:33           ` Chaitanya Kulkarni
2023-05-03  9:06 ` [RFC PATCH 02/11] rust: add `pages` module for handling page allocation Andreas Hindborg
2023-05-03 12:31   ` Benno Lossin
2023-05-03 12:38     ` Benno Lossin
2023-05-05  4:09   ` Matthew Wilcox
2023-05-05  4:42     ` Andreas Hindborg
2023-05-05  5:29       ` Matthew Wilcox
2023-05-03  9:07 ` [RFC PATCH 03/11] rust: block: introduce `kernel::block::mq` module Andreas Hindborg
2023-05-08 12:29   ` Benno Lossin
2023-05-11  6:52     ` Sergio González Collado
2024-01-23 14:03       ` Andreas Hindborg (Samsung)
2024-01-12  9:18     ` Andreas Hindborg (Samsung)
2024-01-23 16:14       ` Benno Lossin
2024-01-23 18:39         ` Andreas Hindborg (Samsung)
2024-01-25  9:26           ` Benno Lossin
2024-01-29 14:14             ` Andreas Hindborg (Samsung)
2023-05-03  9:07 ` Andreas Hindborg [this message]
2023-05-08 12:58   ` [RFC PATCH 04/11] rust: block: introduce `kernel::block::bio` module Benno Lossin
2024-01-11 12:49     ` Andreas Hindborg (Samsung)
2024-02-28 14:31       ` Andreas Hindborg
2024-03-09 12:30         ` Benno Lossin
2023-05-03  9:07 ` [RFC PATCH 05/11] RUST: add `module_params` macro Andreas Hindborg
2023-05-03  9:07 ` [RFC PATCH 06/11] rust: apply cache line padding for `SpinLock` Andreas Hindborg
2023-05-03 12:03   ` Alice Ryhl
2024-02-23 11:29     ` Andreas Hindborg (Samsung)
2024-02-26  9:15       ` Alice Ryhl
2023-05-03  9:07 ` [RFC PATCH 07/11] rust: lock: add support for `Lock::lock_irqsave` Andreas Hindborg
2023-05-03  9:07 ` [RFC PATCH 08/11] rust: lock: implement `IrqSaveBackend` for `SpinLock` Andreas Hindborg
2023-05-03  9:07 ` [RFC PATCH 09/11] RUST: implement `ForeignOwnable` for `Pin` Andreas Hindborg
2023-05-03  9:07 ` [RFC PATCH 10/11] rust: add null block driver Andreas Hindborg
2023-05-03  9:07 ` [RFC PATCH 11/11] rust: inline a number of short functions Andreas Hindborg
2023-05-03 11:32 ` [RFC PATCH 00/11] Rust null block driver Niklas Cassel
2023-05-03 12:29   ` Andreas Hindborg
2023-05-03 13:54     ` Niklas Cassel
2023-05-03 16:47 ` Bart Van Assche
2023-05-04 18:15   ` Andreas Hindborg
2023-05-04 18:36     ` Bart Van Assche
2023-05-04 18:46       ` Andreas Hindborg
2023-05-04 18:52       ` Keith Busch
2023-05-04 19:02         ` Jens Axboe
2023-05-04 19:59           ` Andreas Hindborg
2023-05-04 20:55             ` Jens Axboe
2023-05-05  5:06               ` Andreas Hindborg
2023-05-05 11:14               ` Miguel Ojeda
2023-05-04 20:11           ` Miguel Ojeda
2023-05-04 20:22             ` Jens Axboe
2023-05-05 10:53               ` Miguel Ojeda
2023-05-05 12:24                 ` Boqun Feng
2023-05-05 13:52                   ` Boqun Feng
2023-05-05 19:42                   ` Keith Busch
2023-05-05 21:46                     ` Boqun Feng
2023-05-05 19:38                 ` Bart Van Assche
2023-05-05  3:52           ` Christoph Hellwig
2023-06-06 13:33           ` Andreas Hindborg (Samsung)
2023-06-06 14:46             ` Miguel Ojeda
2023-05-05  5:28       ` Hannes Reinecke
2023-05-07 23:31 ` Luis Chamberlain
2023-05-07 23:37   ` Andreas Hindborg
     [not found] ` <2B3CA5F1CCCFEAB2+20230727034517.GB126117@1182282462>
2023-07-28  6:49   ` Andreas Hindborg (Samsung)
2023-07-31 14:14     ` Andreas Hindborg (Samsung)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230503090708.2524310-5-nmi@metaspace.dk \
    --to=nmi@metaspace.dk \
    --cc=Damien.LeMoal@wdc.com \
    --cc=a.hindborg@samsung.com \
    --cc=alex.gaynor@gmail.com \
    --cc=axboe@kernel.dk \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=gary@garyguo.net \
    --cc=gost.dev@samsung.com \
    --cc=hare@suse.de \
    --cc=hch@lst.de \
    --cc=kbusch@kernel.org \
    --cc=linux-block@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lsf-pc@lists.linux-foundation.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=wedsonaf@gmail.com \
    --cc=willy@infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®