From: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx> Allow Rust file systems to associate [typed] data to super blocks when they're created. Since we only have a pointer-sized field in which to store the state, it must implement the `ForeignOwnable` trait. Signed-off-by: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx> --- rust/kernel/fs.rs | 42 +++++++++++++++++++++++++++++++++------ samples/rust/rust_rofs.rs | 4 +++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 5b7eaa16d254..e9a9362d2897 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -7,7 +7,7 @@ //! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) use crate::error::{code::*, from_result, to_result, Error, Result}; -use crate::types::{ARef, AlwaysRefCounted, Either, Opaque}; +use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque}; use crate::{ bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule, @@ -20,11 +20,14 @@ /// A file system type. pub trait FileSystem { + /// Data associated with each file system instance (super-block). + type Data: ForeignOwnable + Send + Sync; + /// The name of the file system type. const NAME: &'static CStr; /// Returns the parameters to initialise a super block. - fn super_params(sb: &NewSuperBlock<Self>) -> Result<SuperParams>; + fn super_params(sb: &NewSuperBlock<Self>) -> Result<SuperParams<Self::Data>>; /// Initialises and returns the root inode of the given superblock. /// @@ -174,7 +177,7 @@ pub fn new<T: FileSystem + ?Sized>(module: &'static ThisModule) -> impl PinInit< fs.owner = module.0; fs.name = T::NAME.as_char_ptr(); fs.init_fs_context = Some(Self::init_fs_context_callback::<T>); - fs.kill_sb = Some(Self::kill_sb_callback); + fs.kill_sb = Some(Self::kill_sb_callback::<T>); fs.fs_flags = 0; // SAFETY: Pointers stored in `fs` are static so will live for as long as the @@ -195,10 +198,22 @@ pub fn new<T: FileSystem + ?Sized>(module: &'static ThisModule) -> impl PinInit< }) } - unsafe extern "C" fn kill_sb_callback(sb_ptr: *mut bindings::super_block) { + unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>( + sb_ptr: *mut bindings::super_block, + ) { // SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is // the appropriate function to call for cleanup. unsafe { bindings::kill_anon_super(sb_ptr) }; + + // SAFETY: The C API contract guarantees that `sb_ptr` is valid for read. + let ptr = unsafe { (*sb_ptr).s_fs_info }; + if !ptr.is_null() { + // SAFETY: The only place where `s_fs_info` is assigned is `NewSuperBlock::init`, where + // it's initialised with the result of an `into_foreign` call. We checked above that + // `ptr` is non-null because it would be null if we never reached the point where we + // init the field. + unsafe { T::Data::from_foreign(ptr) }; + } } } @@ -429,6 +444,14 @@ pub struct INodeParams { pub struct SuperBlock<T: FileSystem + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>); impl<T: FileSystem + ?Sized> SuperBlock<T> { + /// Returns the data associated with the superblock. + pub fn data(&self) -> <T::Data as ForeignOwnable>::Borrowed<'_> { + // SAFETY: This method is only available after the `NeedsData` typestate, so `s_fs_info` + // has been initialised initialised with the result of a call to `T::into_foreign`. + let ptr = unsafe { (*self.0.get()).s_fs_info }; + unsafe { T::Data::borrow(ptr) } + } + /// Tries to get an existing inode or create a new one if it doesn't exist yet. pub fn get_or_create_inode(&self, ino: Ino) -> Result<Either<ARef<INode<T>>, NewINode<T>>> { // SAFETY: The only initialisation missing from the superblock is the root, and this @@ -458,7 +481,7 @@ pub fn get_or_create_inode(&self, ino: Ino) -> Result<Either<ARef<INode<T>>, New /// Required superblock parameters. /// /// This is returned by implementations of [`FileSystem::super_params`]. -pub struct SuperParams { +pub struct SuperParams<T: ForeignOwnable + Send + Sync> { /// The magic number of the superblock. pub magic: u32, @@ -472,6 +495,9 @@ pub struct SuperParams { /// Granularity of c/m/atime in ns (cannot be worse than a second). pub time_gran: u32, + + /// Data to be associated with the superblock. + pub data: T, } /// A superblock that is still being initialised. @@ -522,6 +548,9 @@ impl<T: FileSystem + ?Sized> Tables<T> { sb.0.s_blocksize = 1 << sb.0.s_blocksize_bits; sb.0.s_flags |= bindings::SB_RDONLY; + // N.B.: Even on failure, `kill_sb` is called and frees the data. + sb.0.s_fs_info = params.data.into_foreign().cast_mut(); + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a // newly-created (and initialised above) superblock. let sb = unsafe { &mut *sb_ptr.cast() }; @@ -934,8 +963,9 @@ fn init(module: &'static ThisModule) -> impl PinInit<Self, Error> { /// /// struct MyFs; /// impl fs::FileSystem for MyFs { +/// type Data = (); /// const NAME: &'static CStr = c_str!("myfs"); -/// fn super_params(_: &NewSuperBlock<Self>) -> Result<SuperParams> { +/// fn super_params(_: &NewSuperBlock<Self>) -> Result<SuperParams<Self::Data>> { /// todo!() /// } /// fn init_root(_sb: &SuperBlock<Self>) -> Result<ARef<INode<Self>>> { diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 95ce28efa1c3..093425650f26 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -52,14 +52,16 @@ struct Entry { struct RoFs; impl fs::FileSystem for RoFs { + type Data = (); const NAME: &'static CStr = c_str!("rust-fs"); - fn super_params(_sb: &NewSuperBlock<Self>) -> Result<SuperParams> { + fn super_params(_sb: &NewSuperBlock<Self>) -> Result<SuperParams<Self::Data>> { Ok(SuperParams { magic: 0x52555354, blocksize_bits: 12, maxbytes: fs::MAX_LFS_FILESIZE, time_gran: 1, + data: (), }) } -- 2.34.1