[RFC PATCH 18/24] erofs: introduce iget alternative to C

Yiyang Wu posted 24 patches 2 months, 2 weeks ago
[RFC PATCH 18/24] erofs: introduce iget alternative to C
Posted by Yiyang Wu 2 months, 2 weeks ago
This patch introduces iget and fast symlink alternative written in Rust.
After this patch, erofs_iget can be replaced with erofs_iget_rust.

Iget related test and set are lifted after this patch as
rust_helpers.c will also use it to port the iget_locked to Rust.

Signed-off-by: Yiyang Wu <toolmanp@tlmp.cc>
---
 fs/erofs/Makefile        |  2 +-
 fs/erofs/inode.c         |  8 ++++--
 fs/erofs/inode_rs.rs     | 59 ++++++++++++++++++++++++++++++++++++++++
 fs/erofs/internal.h      | 33 ++++++++++++++++++++++
 fs/erofs/rust/kinode.rs  | 29 +++++++++++++++++---
 fs/erofs/rust_bindings.h | 12 ++++++++
 fs/erofs/rust_helpers.c  | 55 +++++++++++++++++++++++++++++++++++++
 fs/erofs/rust_helpers.h  |  4 ++-
 fs/erofs/super.c         | 34 ++++++++++++++++++-----
 9 files changed, 220 insertions(+), 16 deletions(-)
 create mode 100644 fs/erofs/inode_rs.rs

diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile
index dfa03edbe29a..46de6f490ca2 100644
--- a/fs/erofs/Makefile
+++ b/fs/erofs/Makefile
@@ -9,4 +9,4 @@ erofs-$(CONFIG_EROFS_FS_ZIP_DEFLATE) += decompressor_deflate.o
 erofs-$(CONFIG_EROFS_FS_ZIP_ZSTD) += decompressor_zstd.o
 erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o
 erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o
-erofs-$(CONFIG_EROFS_FS_RUST) += super_rs.o rust_helpers.o
+erofs-$(CONFIG_EROFS_FS_RUST) += super_rs.o inode_rs.o rust_helpers.o
diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c
index d2fd51fcebd2..b8467272a670 100644
--- a/fs/erofs/inode.c
+++ b/fs/erofs/inode.c
@@ -269,7 +269,7 @@ int erofs_fill_inode(struct inode *inode)
  * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down
  * so that it will fit.
  */
-static ino_t erofs_squash_ino(erofs_nid_t nid)
+ino_t erofs_squash_ino(erofs_nid_t nid)
 {
 	ino_t ino = (ino_t)nid;
 
@@ -278,12 +278,12 @@ static ino_t erofs_squash_ino(erofs_nid_t nid)
 	return ino;
 }
 
-static int erofs_iget5_eq(struct inode *inode, void *opaque)
+int erofs_iget5_eq(struct inode *inode, void *opaque)
 {
 	return EROFS_I(inode)->nid == *(erofs_nid_t *)opaque;
 }
 
-static int erofs_iget5_set(struct inode *inode, void *opaque)
+int erofs_iget5_set(struct inode *inode, void *opaque)
 {
 	const erofs_nid_t nid = *(erofs_nid_t *)opaque;
 
@@ -292,6 +292,7 @@ static int erofs_iget5_set(struct inode *inode, void *opaque)
 	return 0;
 }
 
+#ifndef CONFIG_EROFS_FS_RUST
 struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid)
 {
 	struct inode *inode;
@@ -312,6 +313,7 @@ struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid)
 	}
 	return inode;
 }
+#endif
 
 int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
 		  struct kstat *stat, u32 request_mask,
diff --git a/fs/erofs/inode_rs.rs b/fs/erofs/inode_rs.rs
new file mode 100644
index 000000000000..5cca2ae581ac
--- /dev/null
+++ b/fs/erofs/inode_rs.rs
@@ -0,0 +1,59 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-or-later
+
+//! EROFS Rust Kernel Module Helpers Implementation
+//! This is only for experimental purpose. Feedback is always welcome.
+
+#[allow(dead_code)]
+#[allow(missing_docs)]
+pub(crate) mod rust;
+
+use core::ffi::*;
+use core::mem::{offset_of, size_of};
+use core::ptr::NonNull;
+use kernel::bindings::{inode, super_block};
+use kernel::container_of;
+use rust::{
+    erofs_sys::{operations::*, *},
+    kinode::*,
+    ksuperblock::erofs_sbi,
+};
+
+/// Used as a size hint to be exported to kmem_caceh_create
+#[no_mangle]
+pub static EROFS_INODE_SIZE_RUST: c_uint = size_of::<KernelInode>() as c_uint;
+
+/// Used as a hint offset to be exported so EROFS_VFS_I to find the embedded the vfs inode.
+#[no_mangle]
+pub static EROFS_VFS_INODE_OFFSET_RUST: c_ulong = offset_of!(KernelInode, k_inode) as c_ulong;
+
+/// Used as a hint offset to be exported to EROFS_I to find the embedded c side erofs_inode.
+#[no_mangle]
+pub static EROFS_I_OFFSET_RUST: c_long =
+    offset_of!(KernelInode, k_opaque) as c_long - offset_of!(KernelInode, k_inode) as c_long;
+
+/// Exported as iget replacement
+#[no_mangle]
+pub unsafe extern "C" fn erofs_iget_rust(sb: NonNull<super_block>, nid: Nid) -> *mut c_void {
+    // SAFETY: The super_block is initialized when the erofs_alloc_sbi_rust is called.
+    let sbi = erofs_sbi(sb);
+    read_inode(sbi.filesystem.as_ref(), &mut sbi.inodes, nid)
+        .map_or_else(|e| e.into(), |inode| inode.k_inode.as_mut_ptr().cast())
+}
+
+fn try_fill_inode(k_inode: NonNull<inode>, nid: Nid) -> PosixResult<()> {
+    // SAFETY: The super_block is initialized when the erofs_fill_inode_rust is called.
+    let sbi = erofs_sbi(unsafe { NonNull::new(k_inode.as_ref().i_sb).unwrap() });
+    // SAFETY: k_inode is a part of KernelInode.
+    let erofs_inode: &mut KernelInode = unsafe {
+        &mut *(container_of!(k_inode.as_ptr(), KernelInode, k_inode) as *mut KernelInode)
+    };
+    erofs_inode.info.write(sbi.filesystem.read_inode_info(nid)?);
+    erofs_inode.nid.write(nid);
+    Ok(())
+}
+/// Exported as fill_inode additional fill inode
+#[no_mangle]
+pub unsafe extern "C" fn erofs_fill_inode_rust(k_inode: NonNull<inode>, nid: Nid) -> c_int {
+    try_fill_inode(k_inode, nid).map_or_else(|e| i32::from(e) as c_int, |_| 0)
+}
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 18e67219fbc8..42ce84783be7 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -306,10 +306,20 @@ struct erofs_inode {
 #endif	/* CONFIG_EROFS_FS_ZIP */
 	};
 	/* the corresponding vfs inode */
+#ifndef CONFIG_EROFS_FS_RUST
 	struct inode vfs_inode;
+#endif
 };
 
+#ifdef CONFIG_EROFS_FS_RUST
+#define EROFS_I(ptr)	(*(struct erofs_inode **)(((void *)(ptr)) + \
+			 EROFS_I_OFFSET_RUST))
+#define EROFS_I_VFS(ptr) ((struct inode *)(((void *)(ptr)) + EROFS_VFS_INODE_OFFSET_RUST))
+#define EROFS_I_RUST(ptr) ((void *)(ptr) - EROFS_VFS_INODE_OFFSET_RUST)
+#else
 #define EROFS_I(ptr)	container_of(ptr, struct erofs_inode, vfs_inode)
+#define EROFS_I_VFS(ptr) (&((struct erofs_inode *)(ptr))->vfs_inode)
+#endif
 
 static inline erofs_off_t erofs_iloc(struct inode *inode)
 {
@@ -427,10 +437,18 @@ void erofs_onlinefolio_init(struct folio *folio);
 void erofs_onlinefolio_split(struct folio *folio);
 void erofs_onlinefolio_end(struct folio *folio, int err);
 int erofs_fill_inode(struct inode *inode);
+ino_t erofs_squash_ino(erofs_nid_t nid);
+int erofs_iget5_eq(struct inode *inode, void *opaque);
+int erofs_iget5_set(struct inode *inode, void *opaque);
+#ifdef CONFIG_EROFS_FS_RUST
+#define erofs_iget erofs_iget_rust
+#else
 struct inode *erofs_iget(struct super_block *sb, erofs_nid_t nid);
+#endif
 int erofs_getattr(struct mnt_idmap *idmap, const struct path *path,
 		  struct kstat *stat, u32 request_mask,
 		  unsigned int query_flags);
+
 int erofs_namei(struct inode *dir, const struct qstr *name,
 		erofs_nid_t *nid, unsigned int *d_type);
 
@@ -538,6 +556,21 @@ static inline struct bio *erofs_fscache_bio_alloc(struct erofs_map_dev *mdev) {
 static inline void erofs_fscache_submit_bio(struct bio *bio) {}
 #endif
 
+#ifdef CONFIG_EROFS_FS_RUST
+extern int erofs_init_rust(void);
+extern void erofs_destroy_rust(void);
+extern void erofs_init_inode_rust(struct inode *inode);
+extern void erofs_free_inode_rust(struct inode *inode);
+#else
+static inline int erofs_init_rust(void)
+{
+	return 0;
+}
+static inline void erofs_destroy_rust(void) {}
+static inline void erofs_init_inode_rust(struct inode *inode) {}
+static inline void erofs_free_inode_rust(struct inode *inode) {}
+#endif
+
 #define EFSCORRUPTED    EUCLEAN         /* Filesystem is corrupted */
 
 #endif	/* __EROFS_INTERNAL_H */
diff --git a/fs/erofs/rust/kinode.rs b/fs/erofs/rust/kinode.rs
index df6de40d0594..fac72bd8b6b3 100644
--- a/fs/erofs/rust/kinode.rs
+++ b/fs/erofs/rust/kinode.rs
@@ -1,16 +1,23 @@
 // Copyright 2024 Yiyang Wu
 // SPDX-License-Identifier: MIT or GPL-2.0-or-later
 
-use core::ffi::c_void;
+use core::ffi::*;
 use core::mem::MaybeUninit;
 use core::ptr::NonNull;
 
 use kernel::bindings::{inode, super_block};
+use kernel::container_of;
 
+use super::erofs_sys::errnos::*;
 use super::erofs_sys::inode::*;
 use super::erofs_sys::superblock::*;
 use super::erofs_sys::*;
 
+extern "C" {
+    #[link_name = "erofs_iget_locked_rust_helper"]
+    fn iget_locked(sb: NonNull<c_void>, nid: Nid) -> *mut c_void;
+}
+
 #[repr(C)]
 pub(crate) struct KernelInode {
     pub(crate) info: MaybeUninit<InodeInfo>,
@@ -21,7 +28,12 @@ pub(crate) struct KernelInode {
 
 impl Inode for KernelInode {
     fn new(_sb: &SuperBlock, _info: InodeInfo, _nid: Nid) -> Self {
-        unimplemented!();
+        Self {
+            info: MaybeUninit::uninit(),
+            nid: MaybeUninit::uninit(),
+            k_inode: MaybeUninit::uninit(),
+            k_opaque: MaybeUninit::uninit(),
+        }
     }
     fn nid(&self) -> Nid {
         unsafe { self.nid.assume_init() }
@@ -37,8 +49,17 @@ pub(crate) struct KernelInodeCollection {
 
 impl InodeCollection for KernelInodeCollection {
     type I = KernelInode;
-    fn iget(&mut self, _nid: Nid, _f: &dyn FileSystem<Self::I>) -> PosixResult<&mut Self::I> {
-        unimplemented!();
+    fn iget(&mut self, nid: Nid, _f: &dyn FileSystem<Self::I>) -> PosixResult<&mut Self::I> {
+        // SAFETY: iget_locked is safe to call here.
+        let k_inode = unsafe { iget_locked(self.sb.cast(), nid) };
+        if is_value_err(k_inode.cast()) {
+            return Err(Errno::from(k_inode as i32));
+        } else {
+            let erofs_inode: &mut KernelInode =
+                // SAFETY: iget_locked returns a valid pointer to a vfs inode and it's embedded in a KernelInode.
+                unsafe { &mut *(container_of!(k_inode, KernelInode, k_inode) as *mut KernelInode) };
+            return Ok(erofs_inode);
+        }
     }
 }
 
diff --git a/fs/erofs/rust_bindings.h b/fs/erofs/rust_bindings.h
index 9695c5ed5a7c..657f109dd6e7 100644
--- a/fs/erofs/rust_bindings.h
+++ b/fs/erofs/rust_bindings.h
@@ -6,7 +6,19 @@
 
 #include <linux/fs.h>
 
+
+typedef u64 erofs_nid_t;
+typedef u64 erofs_off_t;
+/* data type for filesystem-wide blocks number */
+typedef u32 erofs_blk_t;
+
 extern const unsigned long EROFS_SB_INFO_OFFSET_RUST;
+extern const unsigned int EROFS_INODE_SIZE_RUST;
+extern const unsigned long EROFS_VFS_INODE_OFFSET_RUST;
+extern const long EROFS_I_OFFSET_RUST;
+
 extern void *erofs_alloc_sbi_rust(struct super_block *sb);
 extern void *erofs_free_sbi_rust(struct super_block *sb);
+extern int erofs_iget5_eq_rust(struct inode *inode, void *opaque);
+extern struct inode *erofs_iget_rust(struct super_block *sb, erofs_nid_t nid);
 #endif
diff --git a/fs/erofs/rust_helpers.c b/fs/erofs/rust_helpers.c
index 5fdc158ed9ef..94e9153fc3ff 100644
--- a/fs/erofs/rust_helpers.c
+++ b/fs/erofs/rust_helpers.c
@@ -1,5 +1,7 @@
 #include "rust_helpers.h"
 
+static struct kmem_cache *erofs_inode_cachep __read_mostly;
+
 static void erofs_init_metabuf_rust_helper(struct erofs_buf *buf,
 					   struct super_block *sb,
 					   struct erofs_sb_info *sbi)
@@ -29,3 +31,56 @@ void erofs_put_metabuf_rust_helper(void *addr)
 		.kmap_type = EROFS_KMAP,
 	});
 }
+
+int erofs_init_rust(void)
+{
+	erofs_inode_cachep = kmem_cache_create("erofs_inode",
+					       sizeof(struct erofs_inode), 0,
+					       SLAB_RECLAIM_ACCOUNT, NULL);
+	if (!erofs_inode_cachep)
+		return -ENOMEM;
+	return 0;
+}
+
+void erofs_destroy_rust(void)
+{
+	if (erofs_inode_cachep)
+		kmem_cache_destroy(erofs_inode_cachep);
+}
+
+void erofs_init_inode_rust(struct inode *inode)
+{
+	EROFS_I(inode) = kmem_cache_alloc(erofs_inode_cachep, GFP_KERNEL);
+}
+
+void erofs_free_inode_rust(struct inode *inode)
+{
+	struct erofs_inode *vi = EROFS_I(inode);
+	if (vi)
+		kmem_cache_free(erofs_inode_cachep, vi);
+}
+
+struct inode *erofs_iget_locked_rust_helper(struct super_block *sb, erofs_nid_t nid)
+{
+	struct inode *inode;
+	int err;
+
+	inode = iget5_locked(sb, erofs_squash_ino(nid), erofs_iget5_eq,
+			     erofs_iget5_set, &nid);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	err = erofs_fill_inode(inode);
+	if(err)
+		goto err_out;
+
+	err = erofs_fill_inode_rust(inode, nid);
+	if(err)
+		goto err_out;
+
+	return inode;
+err_out:
+	if (err)
+		iget_failed(inode);
+	return ERR_PTR(err);
+}
diff --git a/fs/erofs/rust_helpers.h b/fs/erofs/rust_helpers.h
index 158b21438314..5bcd452f6d82 100644
--- a/fs/erofs/rust_helpers.h
+++ b/fs/erofs/rust_helpers.h
@@ -17,5 +17,7 @@ void *erofs_read_metabuf_rust_helper(struct super_block *sb,
 				     struct erofs_sb_info *sbi,
 				     erofs_off_t offset);
 void erofs_put_metabuf_rust_helper(void *addr);
-
+extern int erofs_fill_inode_rust(struct inode *inode, erofs_nid_t nid);
+struct inode *erofs_iget_locked_rust_helper(struct super_block *sb,
+						   erofs_nid_t nid);
 #endif
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 61f138a7d8e2..659502bdf5fe 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -81,22 +81,23 @@ static int erofs_superblock_csum_verify(struct super_block *sb, void *sbdata)
 
 static void erofs_inode_init_once(void *ptr)
 {
-	struct erofs_inode *vi = ptr;
-
-	inode_init_once(&vi->vfs_inode);
+	inode_init_once(EROFS_I_VFS(ptr));
+	erofs_init_inode_rust(EROFS_I_VFS(ptr));
 }
 
 static struct inode *erofs_alloc_inode(struct super_block *sb)
 {
-	struct erofs_inode *vi =
+	void *ptr =
 		alloc_inode_sb(sb, erofs_inode_cachep, GFP_KERNEL);
 
-	if (!vi)
+	if (!ptr)
 		return NULL;
 
+#ifndef CONFIG_EROFS_FS_RUST
 	/* zero out everything except vfs_inode */
-	memset(vi, 0, offsetof(struct erofs_inode, vfs_inode));
-	return &vi->vfs_inode;
+	memset(ptr, 0, offsetof(struct erofs_inode, vfs_inode));
+#endif
+	return EROFS_I_VFS(ptr);
 }
 
 static void erofs_free_inode(struct inode *inode)
@@ -106,7 +107,12 @@ static void erofs_free_inode(struct inode *inode)
 	if (inode->i_op == &erofs_fast_symlink_iops)
 		kfree(inode->i_link);
 	kfree(vi->xattr_shared_xattrs);
+	erofs_free_inode_rust(inode);
+#ifdef CONFIG_EROFS_FS_RUST
+	kmem_cache_free(erofs_inode_cachep, EROFS_I_RUST(inode));
+#else
 	kmem_cache_free(erofs_inode_cachep, vi);
+#endif
 }
 
 /* read variable-sized metadata, offset will be aligned by 4-byte */
@@ -871,13 +877,25 @@ static int __init erofs_module_init(void)
 
 	erofs_check_ondisk_layout_definitions();
 
+#ifndef CONFIG_EROFS_FS_RUST
 	erofs_inode_cachep = kmem_cache_create("erofs_inode",
 			sizeof(struct erofs_inode), 0,
 			SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT,
 			erofs_inode_init_once);
+#else
+	erofs_inode_cachep = kmem_cache_create("erofs_inode_rust",
+			EROFS_INODE_SIZE_RUST, 0,
+			SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT,
+			erofs_inode_init_once);
+#endif
+
 	if (!erofs_inode_cachep)
 		return -ENOMEM;
 
+	err = erofs_init_rust();
+	if(err)
+		goto rust_err;
+
 	err = erofs_init_shrinker();
 	if (err)
 		goto shrinker_err;
@@ -904,6 +922,8 @@ static int __init erofs_module_init(void)
 	erofs_exit_shrinker();
 shrinker_err:
 	kmem_cache_destroy(erofs_inode_cachep);
+rust_err:
+	erofs_destroy_rust();
 	return err;
 }
 
-- 
2.46.0