linux-fsdevel.vger.kernel.org archive mirror
 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 27/30] rust: fs: add `iomap` module
Date: Tue, 14 May 2024 10:17:08 -0300	[thread overview]
Message-ID: <20240514131711.379322-28-wedsonaf@gmail.com> (raw)
In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com>

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

Allow file systems to implement their address space operations via
iomap, which delegates a lot of the complexity to common code.

Signed-off-by: Wedson Almeida Filho <walmeida@microsoft.com>
---
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/fs.rs               |   1 +
 rust/kernel/fs/iomap.rs         | 281 ++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 rust/kernel/fs/iomap.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index f4c7c3951dbe..629fce394dbe 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/fs_context.h>
+#include <linux/iomap.h>
 #include <linux/jiffies.h>
 #include <linux/mdio.h>
 #include <linux/pagemap.h>
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 4d90b23735bc..7a1c4884c370 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -19,6 +19,7 @@
 pub mod dentry;
 pub mod file;
 pub mod inode;
+pub mod iomap;
 pub mod sb;
 
 /// The offset of a file in a file system.
diff --git a/rust/kernel/fs/iomap.rs b/rust/kernel/fs/iomap.rs
new file mode 100644
index 000000000000..e48e200e555e
--- /dev/null
+++ b/rust/kernel/fs/iomap.rs
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! File system io maps.
+//!
+//! This module allows Rust code to use iomaps to implement filesystems.
+//!
+//! C headers: [`include/linux/iomap.h`](srctree/include/linux/iomap.h)
+
+use super::{address_space, FileSystem, INode, Offset};
+use crate::error::{from_result, Result};
+use crate::{bindings, block};
+use core::marker::PhantomData;
+
+/// The type of mapping.
+///
+/// This is used in [`Map`].
+#[repr(u16)]
+pub enum Type {
+    /// No blocks allocated, need allocation.
+    Hole = bindings::IOMAP_HOLE as u16,
+
+    /// Delayed allocation blocks.
+    DelAlloc = bindings::IOMAP_DELALLOC as u16,
+
+    /// Blocks allocated at the given address.
+    Mapped = bindings::IOMAP_MAPPED as u16,
+
+    /// Blocks allocated at the given address in unwritten state.
+    Unwritten = bindings::IOMAP_UNWRITTEN as u16,
+
+    /// Data inline in the inode.
+    Inline = bindings::IOMAP_INLINE as u16,
+}
+
+/// Flags usable in [`Map`], in [`Map::set_flags`] in particular.
+pub mod map_flags {
+    /// Indicates that the blocks have been newly allocated and need zeroing for areas that no data
+    /// is copied to.
+    pub const NEW: u16 = bindings::IOMAP_F_NEW as u16;
+
+    /// Indicates that the inode has uncommitted metadata needed to access written data and
+    /// requires fdatasync to commit them to persistent storage. This needs to take into account
+    /// metadata changes that *may* be made at IO completion, such as file size updates from direct
+    /// IO.
+    pub const DIRTY: u16 = bindings::IOMAP_F_DIRTY as u16;
+
+    /// Indicates that the blocks are shared, and will need to be unshared as part a write.
+    pub const SHARED: u16 = bindings::IOMAP_F_SHARED as u16;
+
+    /// Indicates that the iomap contains the merge of multiple block mappings.
+    pub const MERGED: u16 = bindings::IOMAP_F_MERGED as u16;
+
+    /// Indicates that the file system requires the use of buffer heads for this mapping.
+    pub const BUFFER_HEAD: u16 = bindings::IOMAP_F_BUFFER_HEAD as u16;
+
+    /// Indicates that the iomap is for an extended attribute extent rather than a file data
+    /// extent.
+    pub const XATTR: u16 = bindings::IOMAP_F_XATTR as u16;
+
+    /// Indicates to the iomap_end method that the file size has changed as the result of this
+    /// write operation.
+    pub const SIZE_CHANGED: u16 = bindings::IOMAP_F_SIZE_CHANGED as u16;
+
+    /// Indicates that the iomap is not valid any longer and the file range it covers needs to be
+    /// remapped by the high level before the operation can proceed.
+    pub const STALE: u16 = bindings::IOMAP_F_STALE as u16;
+
+    /// Flags from 0x1000 up are for file system specific usage.
+    pub const PRIVATE: u16 = bindings::IOMAP_F_PRIVATE as u16;
+}
+
+/// A map from address space to block device.
+#[repr(transparent)]
+pub struct Map<'a>(pub bindings::iomap, PhantomData<&'a ()>);
+
+impl<'a> Map<'a> {
+    /// Sets the map type.
+    pub fn set_type(&mut self, t: Type) -> &mut Self {
+        self.0.type_ = t as u16;
+        self
+    }
+
+    /// Sets the file offset, in bytes.
+    pub fn set_offset(&mut self, v: Offset) -> &mut Self {
+        self.0.offset = v;
+        self
+    }
+
+    /// Sets the length of the mapping, in bytes.
+    pub fn set_length(&mut self, len: u64) -> &mut Self {
+        self.0.length = len;
+        self
+    }
+
+    /// Sets the mapping flags.
+    ///
+    /// Values come from the [`map_flags`] module.
+    pub fn set_flags(&mut self, flags: u16) -> &mut Self {
+        self.0.flags = flags;
+        self
+    }
+
+    /// Sets the disk offset of the mapping, in bytes.
+    pub fn set_addr(&mut self, addr: u64) -> &mut Self {
+        self.0.addr = addr;
+        self
+    }
+
+    /// Sets the block device of the mapping.
+    pub fn set_bdev(&mut self, bdev: Option<&'a block::Device>) -> &mut Self {
+        self.0.bdev = if let Some(b) = bdev {
+            b.0.get()
+        } else {
+            core::ptr::null_mut()
+        };
+        self
+    }
+}
+
+/// Flags passed to [`Operations::begin`] and [`Operations::end`].
+pub mod flags {
+    /// Writing, must allocate block.
+    pub const WRITE: u32 = bindings::IOMAP_WRITE;
+
+    /// Zeroing operation, may skip holes.
+    pub const ZERO: u32 = bindings::IOMAP_ZERO;
+
+    /// Report extent status, e.g. FIEMAP.
+    pub const REPORT: u32 = bindings::IOMAP_REPORT;
+
+    /// Mapping for page fault.
+    pub const FAULT: u32 = bindings::IOMAP_FAULT;
+
+    /// Direct I/O.
+    pub const DIRECT: u32 = bindings::IOMAP_DIRECT;
+
+    /// Do not block.
+    pub const NOWAIT: u32 = bindings::IOMAP_NOWAIT;
+
+    /// Only pure overwrites allowed.
+    pub const OVERWRITE_ONLY: u32 = bindings::IOMAP_OVERWRITE_ONLY;
+
+    /// `unshare_file_range`.
+    pub const UNSHARE: u32 = bindings::IOMAP_UNSHARE;
+
+    /// DAX mapping.
+    pub const DAX: u32 = bindings::IOMAP_DAX;
+}
+
+/// Operations implemented by iomap users.
+pub trait Operations {
+    /// File system that these operations are compatible with.
+    type FileSystem: FileSystem + ?Sized;
+
+    /// Returns the existing mapping at `pos`, or reserves space starting at `pos` for up to
+    /// `length`, as long as it can be done as a single mapping. The actual length is returned in
+    /// `iomap`.
+    ///
+    /// The values of `flags` come from the [`flags`] module.
+    fn begin<'a>(
+        inode: &'a INode<Self::FileSystem>,
+        pos: Offset,
+        length: Offset,
+        flags: u32,
+        map: &mut Map<'a>,
+        srcmap: &mut Map<'a>,
+    ) -> Result;
+
+    /// Commits and/or unreserves space previously allocated using [`Operations::begin`]. `writte`n
+    /// indicates the length of the successful write operation which needs to be commited, while
+    /// the rest needs to be unreserved. `written` might be zero if no data was written.
+    ///
+    /// The values of `flags` come from the [`flags`] module.
+    fn end<'a>(
+        _inode: &'a INode<Self::FileSystem>,
+        _pos: Offset,
+        _length: Offset,
+        _written: isize,
+        _flags: u32,
+        _map: &Map<'a>,
+    ) -> Result {
+        Ok(())
+    }
+}
+
+/// Returns address space oprerations backed by iomaps.
+pub const fn ro_aops<T: Operations + ?Sized>() -> address_space::Ops<T::FileSystem> {
+    struct Table<T: Operations + ?Sized>(PhantomData<T>);
+    impl<T: Operations + ?Sized> Table<T> {
+        const MAP_TABLE: bindings::iomap_ops = bindings::iomap_ops {
+            iomap_begin: Some(Self::iomap_begin_callback),
+            iomap_end: Some(Self::iomap_end_callback),
+        };
+
+        extern "C" fn iomap_begin_callback(
+            inode_ptr: *mut bindings::inode,
+            pos: Offset,
+            length: Offset,
+            flags: u32,
+            map: *mut bindings::iomap,
+            srcmap: *mut bindings::iomap,
+        ) -> i32 {
+            from_result(|| {
+                // SAFETY: The C API guarantees that `inode_ptr` is a valid inode.
+                let inode = unsafe { INode::from_raw(inode_ptr) };
+                T::begin(
+                    inode,
+                    pos,
+                    length,
+                    flags,
+                    // SAFETY: The C API guarantees that `map` is valid for write.
+                    unsafe { &mut *map.cast::<Map<'_>>() },
+                    // SAFETY: The C API guarantees that `srcmap` is valid for write.
+                    unsafe { &mut *srcmap.cast::<Map<'_>>() },
+                )?;
+                Ok(0)
+            })
+        }
+
+        extern "C" fn iomap_end_callback(
+            inode_ptr: *mut bindings::inode,
+            pos: Offset,
+            length: Offset,
+            written: isize,
+            flags: u32,
+            map: *mut bindings::iomap,
+        ) -> i32 {
+            from_result(|| {
+                // SAFETY: The C API guarantees that `inode_ptr` is a valid inode.
+                let inode = unsafe { INode::from_raw(inode_ptr) };
+                // SAFETY: The C API guarantees that `map` is valid for read.
+                T::end(inode, pos, length, written, flags, unsafe {
+                    &*map.cast::<Map<'_>>()
+                })?;
+                Ok(0)
+            })
+        }
+
+        const TABLE: bindings::address_space_operations = bindings::address_space_operations {
+            writepage: None,
+            read_folio: Some(Self::read_folio_callback),
+            writepages: None,
+            dirty_folio: None,
+            readahead: Some(Self::readahead_callback),
+            write_begin: None,
+            write_end: None,
+            bmap: Some(Self::bmap_callback),
+            invalidate_folio: Some(bindings::iomap_invalidate_folio),
+            release_folio: Some(bindings::iomap_release_folio),
+            free_folio: None,
+            direct_IO: Some(bindings::noop_direct_IO),
+            migrate_folio: None,
+            launder_folio: None,
+            is_partially_uptodate: None,
+            is_dirty_writeback: None,
+            error_remove_folio: None,
+            swap_activate: None,
+            swap_deactivate: None,
+            swap_rw: None,
+        };
+
+        extern "C" fn read_folio_callback(
+            _file: *mut bindings::file,
+            folio: *mut bindings::folio,
+        ) -> i32 {
+            // SAFETY: `folio` is just forwarded from C and `Self::MAP_TABLE` is always valid.
+            unsafe { bindings::iomap_read_folio(folio, &Self::MAP_TABLE) }
+        }
+
+        extern "C" fn readahead_callback(rac: *mut bindings::readahead_control) {
+            // SAFETY: `rac` is just forwarded from C and `Self::MAP_TABLE` is always valid.
+            unsafe { bindings::iomap_readahead(rac, &Self::MAP_TABLE) }
+        }
+
+        extern "C" fn bmap_callback(mapping: *mut bindings::address_space, block: u64) -> u64 {
+            // SAFETY: `mapping` is just forwarded from C and `Self::MAP_TABLE` is always valid.
+            unsafe { bindings::iomap_bmap(mapping, block, &Self::MAP_TABLE) }
+        }
+    }
+    address_space::Ops(&Table::<T>::TABLE, PhantomData)
+}
-- 
2.34.1


  parent reply	other threads:[~2024-05-14 13:18 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 ` [RFC PATCH v2 18/30] rust: fs: introduce `address_space::Operations::read_folio` Wedson Almeida Filho
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 ` Wedson Almeida Filho [this message]
2024-05-20 19:32   ` [RFC PATCH v2 27/30] rust: fs: add `iomap` module 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-28-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).