diff mbox series

[RFC,15/19] rust: fs: add basic support for fs buffer heads

Message ID 20231018122518.128049-16-wedsonaf@gmail.com (mailing list archive)
State New, archived
Headers show
Series Rust abstractions for VFS | expand

Commit Message

Wedson Almeida Filho Oct. 18, 2023, 12:25 p.m. UTC
From: Wedson Almeida Filho <walmeida@microsoft.com>

Introduce the abstractions that will be used by modules to handle buffer
heads, which will be used to access cached blocks from block devices.

All dead-code annotations are removed in the next commit in the series.

Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
---
 rust/bindings/bindings_helper.h |  1 +
 rust/helpers.c                  | 15 ++++++++
 rust/kernel/fs.rs               |  3 ++
 rust/kernel/fs/buffer.rs        | 61 +++++++++++++++++++++++++++++++++
 4 files changed, 80 insertions(+)
 create mode 100644 rust/kernel/fs/buffer.rs

Comments

Andreas Hindborg Jan. 3, 2024, 2:17 p.m. UTC | #1
Wedson Almeida Filho <wedsonaf@gmail.com> writes:

[...]

> +// SAFETY: The type invariants guarantee that `INode` is always ref-counted.
> +unsafe impl AlwaysRefCounted for Head {
> +    fn inc_ref(&self) {
> +        // SAFETY: The existence of a shared reference means that the refcount is nonzero.
> +        unsafe { bindings::get_bh(self.0.get()) };
> +    }
> +
> +    unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
> +        // SAFETY: The safety requirements guarantee that the refcount is nonzero.
> +        unsafe { bindings::put_bh(obj.cast().as_ptr()) }

I would prefer the target type of the cast to be explicit.

> +    }
> +}
> +
> +impl Head {
> +    /// Returns the block data associated with the given buffer head.
> +    pub fn data(&self) -> &[u8] {
> +        let h = self.0.get();
> +        // SAFETY: The existence of a shared reference guarantees that the buffer head is
> +        // available and so we can access its contents.
> +        unsafe { core::slice::from_raw_parts((*h).b_data.cast(), (*h).b_size) }

Same

BR Andreas
diff mbox series

Patch

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index e2b2ccc835e3..d328375f7cb7 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -7,6 +7,7 @@ 
  */
 
 #include <kunit/test.h>
+#include <linux/buffer_head.h>
 #include <linux/errname.h>
 #include <linux/fs.h>
 #include <linux/fs_context.h>
diff --git a/rust/helpers.c b/rust/helpers.c
index af335d1912e7..a5393c6b93f2 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -21,6 +21,7 @@ 
  */
 
 #include <kunit/test-bug.h>
+#include <linux/buffer_head.h>
 #include <linux/bug.h>
 #include <linux/build_bug.h>
 #include <linux/cacheflush.h>
@@ -250,6 +251,20 @@  unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor)
 }
 EXPORT_SYMBOL_GPL(rust_helper_MKDEV);
 
+#ifdef CONFIG_BUFFER_HEAD
+void rust_helper_get_bh(struct buffer_head *bh)
+{
+	get_bh(bh);
+}
+EXPORT_SYMBOL_GPL(rust_helper_get_bh);
+
+void rust_helper_put_bh(struct buffer_head *bh)
+{
+	put_bh(bh);
+}
+EXPORT_SYMBOL_GPL(rust_helper_put_bh);
+#endif
+
 /*
  * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
  * use it in contexts where Rust expects a `usize` like slice (array) indices.
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index e9a9362d2897..4f04cb1d3c6f 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -15,6 +15,9 @@ 
 use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
 use macros::{pin_data, pinned_drop};
 
+#[cfg(CONFIG_BUFFER_HEAD)]
+pub mod buffer;
+
 /// Maximum size of an inode.
 pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
 
diff --git a/rust/kernel/fs/buffer.rs b/rust/kernel/fs/buffer.rs
new file mode 100644
index 000000000000..6052af8822b3
--- /dev/null
+++ b/rust/kernel/fs/buffer.rs
@@ -0,0 +1,61 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+//! File system buffers.
+//!
+//! C headers: [`include/linux/buffer_head.h`](../../../include/linux/buffer_head.h)
+
+use crate::types::{ARef, AlwaysRefCounted, Opaque};
+use core::ptr;
+
+/// Wraps the kernel's `struct buffer_head`.
+///
+/// # Invariants
+///
+/// Instances of this type are always ref-counted, that is, a call to `get_bh` ensures that the
+/// allocation remains valid at least until the matching call to `put_bh`.
+#[repr(transparent)]
+pub struct Head(Opaque<bindings::buffer_head>);
+
+// SAFETY: The type invariants guarantee that `INode` is always ref-counted.
+unsafe impl AlwaysRefCounted for Head {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference means that the refcount is nonzero.
+        unsafe { bindings::get_bh(self.0.get()) };
+    }
+
+    unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is nonzero.
+        unsafe { bindings::put_bh(obj.cast().as_ptr()) }
+    }
+}
+
+impl Head {
+    /// Returns the block data associated with the given buffer head.
+    pub fn data(&self) -> &[u8] {
+        let h = self.0.get();
+        // SAFETY: The existence of a shared reference guarantees that the buffer head is
+        // available and so we can access its contents.
+        unsafe { core::slice::from_raw_parts((*h).b_data.cast(), (*h).b_size) }
+    }
+}
+
+/// A view of a buffer.
+///
+/// It may contain just a contiguous subset of the buffer.
+pub struct View {
+    head: ARef<Head>,
+    offset: usize,
+    size: usize,
+}
+
+impl View {
+    #[allow(dead_code)]
+    pub(crate) fn new(head: ARef<Head>, offset: usize, size: usize) -> Self {
+        Self { head, size, offset }
+    }
+
+    /// Returns the view of the buffer head.
+    pub fn data(&self) -> &[u8] {
+        &self.head.data()[self.offset..][..self.size]
+    }
+}