All of lore.kernel.org
 help / color / mirror / Atom feed
From: Wedson Almeida Filho <wedsonaf@gmail.com>
To: Alexander Viro <viro@zeniv.linux.org.uk>,
	Christian Brauner <brauner@kernel.org>,
	Matthew Wilcox <willy@infradead.org>,
	Dave Chinner <david@fromorbit.com>
Cc: Kent Overstreet <kent.overstreet@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Wedson Almeida Filho <walmeida@microsoft.com>
Subject: [RFC PATCH v2 18/30] rust: fs: introduce `address_space::Operations::read_folio`
Date: Tue, 14 May 2024 10:16:59 -0300	[thread overview]
Message-ID: <20240514131711.379322-19-wedsonaf@gmail.com> (raw)
In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com>

From: Wedson Almeida Filho <walmeida@microsoft.com>

Allow Rust file systems to create regular file inodes backed by the page
cache. The contents of such files are read into folios via `read_folio`.

Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
---
 rust/helpers.c                  |  6 +++
 rust/kernel/folio.rs            |  1 -
 rust/kernel/fs/address_space.rs | 40 ++++++++++++++++++--
 rust/kernel/fs/file.rs          |  7 ++++
 rust/kernel/fs/inode.rs         | 20 +++++++++-
 samples/rust/rust_rofs.rs       | 67 ++++++++++++++++++++++++++-------
 6 files changed, 122 insertions(+), 19 deletions(-)

diff --git a/rust/helpers.c b/rust/helpers.c
index acff58e6caff..2db5df578df2 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -282,6 +282,12 @@ loff_t rust_helper_i_size_read(const struct inode *inode)
 }
 EXPORT_SYMBOL_GPL(rust_helper_i_size_read);
 
+void rust_helper_mapping_set_large_folios(struct address_space *mapping)
+{
+	mapping_set_large_folios(mapping);
+}
+EXPORT_SYMBOL_GPL(rust_helper_mapping_set_large_folios);
+
 unsigned long rust_helper_copy_to_user(void __user *to, const void *from,
 				       unsigned long n)
 {
diff --git a/rust/kernel/folio.rs b/rust/kernel/folio.rs
index 20f51db920e4..077328b733e4 100644
--- a/rust/kernel/folio.rs
+++ b/rust/kernel/folio.rs
@@ -49,7 +49,6 @@ impl<S> Folio<S> {
     /// Callers must ensure that:
     /// * `ptr` is valid and remains so for the lifetime of the returned reference.
     /// * The folio has the right state.
-    #[allow(dead_code)]
     pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::folio) -> &'a Self {
         // SAFETY: The safety requirements guarantee that the cast below is ok.
         unsafe { &*ptr.cast::<Self>() }
diff --git a/rust/kernel/fs/address_space.rs b/rust/kernel/fs/address_space.rs
index 5b4fcb568f46..e539d690235b 100644
--- a/rust/kernel/fs/address_space.rs
+++ b/rust/kernel/fs/address_space.rs
@@ -6,8 +6,9 @@
 //!
 //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h)
 
-use super::FileSystem;
-use crate::bindings;
+use super::{file::File, FileSystem};
+use crate::error::{from_result, Result};
+use crate::{bindings, folio::Folio, folio::PageCache, types::Locked};
 use core::marker::PhantomData;
 use macros::vtable;
 
@@ -16,10 +17,15 @@
 pub trait Operations {
     /// File system that these operations are compatible with.
     type FileSystem: FileSystem + ?Sized;
+
+    /// Reads the contents of the inode into the given folio.
+    fn read_folio(
+        file: Option<&File<Self::FileSystem>>,
+        folio: Locked<&Folio<PageCache<Self::FileSystem>>>,
+    ) -> Result;
 }
 
 /// Represents address space operations.
-#[allow(dead_code)]
 pub struct Ops<T: FileSystem + ?Sized>(
     pub(crate) *const bindings::address_space_operations,
     pub(crate) PhantomData<T>,
@@ -32,7 +38,11 @@ pub const fn new<U: Operations<FileSystem = T> + ?Sized>() -> Self {
         impl<T: Operations + ?Sized> Table<T> {
             const TABLE: bindings::address_space_operations = bindings::address_space_operations {
                 writepage: None,
-                read_folio: None,
+                read_folio: if T::HAS_READ_FOLIO {
+                    Some(Self::read_folio_callback)
+                } else {
+                    None
+                },
                 writepages: None,
                 dirty_folio: None,
                 readahead: None,
@@ -52,6 +62,28 @@ impl<T: Operations + ?Sized> Table<T> {
                 swap_deactivate: None,
                 swap_rw: None,
             };
+
+            extern "C" fn read_folio_callback(
+                file_ptr: *mut bindings::file,
+                folio_ptr: *mut bindings::folio,
+            ) -> i32 {
+                from_result(|| {
+                    let file = if file_ptr.is_null() {
+                        None
+                    } else {
+                        // SAFETY: The C API guarantees that `file_ptr` is a valid file if non-null.
+                        Some(unsafe { File::from_raw(file_ptr) })
+                    };
+
+                    // SAFETY: The C API guarantees that `folio_ptr` is a valid folio.
+                    let folio = unsafe { Folio::from_raw(folio_ptr) };
+
+                    // SAFETY: The C contract guarantees that the folio is valid and locked, with
+                    // ownership of the lock transferred to the callee (this function).
+                    T::read_folio(file, unsafe { Locked::new(folio) })?;
+                    Ok(0)
+                })
+            }
         }
         Self(&Table::<U>::TABLE, PhantomData)
     }
diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs
index 2ba456a1eee1..0828676eae1c 100644
--- a/rust/kernel/fs/file.rs
+++ b/rust/kernel/fs/file.rs
@@ -355,6 +355,12 @@ fn read_dir(
 pub struct Ops<T: FileSystem + ?Sized>(pub(crate) *const bindings::file_operations, PhantomData<T>);
 
 impl<T: FileSystem + ?Sized> Ops<T> {
+    /// Returns file operations for page-cache-based ro files.
+    pub fn generic_ro_file() -> Self {
+        // SAFETY: This is a constant in C, it never changes.
+        Self(unsafe { &bindings::generic_ro_fops }, PhantomData)
+    }
+
     /// Creates file operations from a type that implements the [`Operations`] trait.
     pub const fn new<U: Operations<FileSystem = T> + ?Sized>() -> Self {
         struct Table<T: Operations + ?Sized>(PhantomData<T>);
@@ -516,6 +522,7 @@ impl From<inode::Type> for DirEntryType {
     fn from(value: inode::Type) -> Self {
         match value {
             inode::Type::Dir => DirEntryType::Dir,
+            inode::Type::Reg => DirEntryType::Reg,
         }
     }
 }
diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs
index c314d036c87e..1a41c824d30d 100644
--- a/rust/kernel/fs/inode.rs
+++ b/rust/kernel/fs/inode.rs
@@ -6,7 +6,9 @@
 //!
 //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h)
 
-use super::{dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS};
+use super::{
+    address_space, dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS,
+};
 use crate::error::{code::*, Result};
 use crate::types::{ARef, AlwaysRefCounted, Lockable, Locked, Opaque};
 use crate::{bindings, block, time::Timespec};
@@ -127,6 +129,11 @@ pub fn init(mut self, params: Params) -> Result<ARef<INode<T>>> {
         let inode = unsafe { self.0.as_mut() };
         let mode = match params.typ {
             Type::Dir => bindings::S_IFDIR,
+            Type::Reg => {
+                // SAFETY: The `i_mapping` pointer doesn't change and is valid.
+                unsafe { bindings::mapping_set_large_folios(inode.i_mapping) };
+                bindings::S_IFREG
+            }
         };
 
         inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?;
@@ -166,6 +173,14 @@ pub fn set_fops(&mut self, fops: file::Ops<T>) -> &mut Self {
         inode.__bindgen_anon_3.i_fop = fops.0;
         self
     }
+
+    /// Sets the address space operations on this new inode.
+    pub fn set_aops(&mut self, aops: address_space::Ops<T>) -> &mut Self {
+        // SAFETY: By the type invariants, it's ok to modify the inode.
+        let inode = unsafe { self.0.as_mut() };
+        inode.i_data.a_ops = aops.0;
+        self
+    }
 }
 
 impl<T: FileSystem + ?Sized> Drop for New<T> {
@@ -181,6 +196,9 @@ fn drop(&mut self) {
 pub enum Type {
     /// Directory type.
     Dir,
+
+    /// Regular file type.
+    Reg,
 }
 
 /// Required inode parameters.
diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs
index 2a87e524e0e1..8005fd14b2e1 100644
--- a/samples/rust/rust_rofs.rs
+++ b/samples/rust/rust_rofs.rs
@@ -3,10 +3,11 @@
 //! Rust read-only file system sample.
 
 use kernel::fs::{
-    dentry, dentry::DEntry, file, file::File, inode, inode::INode, sb::SuperBlock, Offset,
+    address_space, dentry, dentry::DEntry, file, file::File, inode, inode::INode, sb, Offset,
 };
 use kernel::prelude::*;
-use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either, types::Locked, user};
+use kernel::types::{ARef, Either, Locked};
+use kernel::{c_str, folio::Folio, folio::PageCache, fs, time::UNIX_EPOCH, user};
 
 kernel::module_fs! {
     type: RoFs,
@@ -20,6 +21,7 @@ struct Entry {
     name: &'static [u8],
     ino: u64,
     etype: inode::Type,
+    contents: &'static [u8],
 }
 
 const ENTRIES: [Entry; 3] = [
@@ -27,41 +29,53 @@ struct Entry {
         name: b".",
         ino: 1,
         etype: inode::Type::Dir,
+        contents: b"",
     },
     Entry {
         name: b"..",
         ino: 1,
         etype: inode::Type::Dir,
+        contents: b"",
     },
     Entry {
-        name: b"subdir",
+        name: b"test.txt",
         ino: 2,
-        etype: inode::Type::Dir,
+        etype: inode::Type::Reg,
+        contents: b"hello world\n",
     },
 ];
 
 const DIR_FOPS: file::Ops<RoFs> = file::Ops::new::<RoFs>();
 const DIR_IOPS: inode::Ops<RoFs> = inode::Ops::new::<RoFs>();
+const FILE_AOPS: address_space::Ops<RoFs> = address_space::Ops::new::<RoFs>();
 
 struct RoFs;
 
 impl RoFs {
-    fn iget(sb: &SuperBlock<Self>, e: &'static Entry) -> Result<ARef<INode<Self>>> {
+    fn iget(sb: &sb::SuperBlock<Self>, e: &'static Entry) -> Result<ARef<INode<Self>>> {
         let mut new = match sb.get_or_create_inode(e.ino)? {
             Either::Left(existing) => return Ok(existing),
             Either::Right(new) => new,
         };
 
-        match e.etype {
-            inode::Type::Dir => new.set_iops(DIR_IOPS).set_fops(DIR_FOPS),
+        let (mode, nlink, size) = match e.etype {
+            inode::Type::Dir => {
+                new.set_iops(DIR_IOPS).set_fops(DIR_FOPS);
+                (0o555, 2, ENTRIES.len().try_into()?)
+            }
+            inode::Type::Reg => {
+                new.set_fops(file::Ops::generic_ro_file())
+                    .set_aops(FILE_AOPS);
+                (0o444, 1, e.contents.len().try_into()?)
+            }
         };
 
         new.init(inode::Params {
             typ: e.etype,
-            mode: 0o555,
-            size: ENTRIES.len().try_into()?,
-            blocks: 1,
-            nlink: 2,
+            mode,
+            size,
+            blocks: (u64::try_from(size)? + 511) / 512,
+            nlink,
             uid: 0,
             gid: 0,
             atime: UNIX_EPOCH,
@@ -74,12 +88,12 @@ fn iget(sb: &SuperBlock<Self>, e: &'static Entry) -> Result<ARef<INode<Self>>> {
 impl fs::FileSystem for RoFs {
     const NAME: &'static CStr = c_str!("rust_rofs");
 
-    fn fill_super(sb: &mut SuperBlock<Self>) -> Result {
+    fn fill_super(sb: &mut sb::SuperBlock<Self>) -> Result {
         sb.set_magic(0x52555354);
         Ok(())
     }
 
-    fn init_root(sb: &SuperBlock<Self>) -> Result<dentry::Root<Self>> {
+    fn init_root(sb: &sb::SuperBlock<Self>) -> Result<dentry::Root<Self>> {
         let inode = Self::iget(sb, &ENTRIES[0])?;
         dentry::Root::try_new(inode)
     }
@@ -109,6 +123,33 @@ fn lookup(
     }
 }
 
+#[vtable]
+impl address_space::Operations for RoFs {
+    type FileSystem = Self;
+
+    fn read_folio(_: Option<&File<Self>>, mut folio: Locked<&Folio<PageCache<Self>>>) -> Result {
+        let data = match folio.inode().ino() {
+            2 => ENTRIES[2].contents,
+            _ => return Err(EINVAL),
+        };
+
+        let pos = usize::try_from(folio.pos()).unwrap_or(usize::MAX);
+        let copied = if pos >= data.len() {
+            0
+        } else {
+            let to_copy = core::cmp::min(data.len() - pos, folio.size());
+            folio.write(0, &data[pos..][..to_copy])?;
+            to_copy
+        };
+
+        folio.zero_out(copied, folio.size() - copied)?;
+        folio.mark_uptodate();
+        folio.flush_dcache();
+
+        Ok(())
+    }
+}
+
 #[vtable]
 impl file::Operations for RoFs {
     type FileSystem = Self;
-- 
2.34.1


  parent reply	other threads:[~2024-05-14 13:17 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-14 13:16 [RFC PATCH v2 00/30] Rust abstractions for VFS Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 01/30] rust: fs: add registration/unregistration of file systems Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 02/30] rust: fs: introduce the `module_fs` macro Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 03/30] samples: rust: add initial ro file system sample Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 04/30] rust: fs: introduce `FileSystem::fill_super` Wedson Almeida Filho
2024-05-20 19:38   ` Darrick J. Wong
2024-05-14 13:16 ` [RFC PATCH v2 05/30] rust: fs: introduce `INode<T>` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 06/30] rust: fs: introduce `DEntry<T>` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 07/30] rust: fs: introduce `FileSystem::init_root` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 08/30] rust: file: move `kernel::file` to `kernel::fs::file` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 09/30] rust: fs: generalise `File` for different file systems Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 10/30] rust: fs: add empty file operations Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 11/30] rust: fs: introduce `file::Operations::read_dir` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 12/30] rust: fs: introduce `file::Operations::seek` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 13/30] rust: fs: introduce `file::Operations::read` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 14/30] rust: fs: add empty inode operations Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 15/30] rust: fs: introduce `inode::Operations::lookup` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 16/30] rust: folio: introduce basic support for folios Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 17/30] rust: fs: add empty address space operations Wedson Almeida Filho
2024-05-14 13:16 ` Wedson Almeida Filho [this message]
2024-05-14 13:17 ` [RFC PATCH v2 19/30] rust: fs: introduce `FileSystem::read_xattr` Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 20/30] rust: fs: introduce `FileSystem::statfs` Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 21/30] rust: fs: introduce more inode types Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 22/30] rust: fs: add per-superblock data Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 23/30] rust: fs: allow file systems backed by a block device Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 24/30] rust: fs: allow per-inode data Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 25/30] rust: fs: export file type from mode constants Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 26/30] rust: fs: allow populating i_lnk Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 27/30] rust: fs: add `iomap` module Wedson Almeida Filho
2024-05-20 19:32   ` Darrick J. Wong
2024-05-14 13:17 ` [RFC PATCH v2 28/30] rust: fs: add memalloc_nofs support Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 29/30] tarfs: introduce tar fs Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 30/30] WIP: fs: ext2: add rust ro ext2 implementation Wedson Almeida Filho
2024-05-20 20:01   ` Darrick J. Wong
2024-05-31 14:34 ` [RFC PATCH v2 00/30] Rust abstractions for VFS Danilo Krummrich

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=20240514131711.379322-19-wedsonaf@gmail.com \
    --to=wedsonaf@gmail.com \
    --cc=brauner@kernel.org \
    --cc=david@fromorbit.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=kent.overstreet@gmail.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=walmeida@microsoft.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.