99use crate :: error:: { code:: * , from_result, to_result, Error , Result } ;
1010use crate :: types:: { ARef , AlwaysRefCounted , Either , ForeignOwnable , Opaque } ;
1111use crate :: {
12- bindings, folio:: LockedFolio , init:: PinInit , str :: CStr , time :: Timespec , try_pin_init ,
13- ThisModule ,
12+ bindings, container_of , folio:: LockedFolio , init:: PinInit , mem_cache :: MemCache , str :: CStr ,
13+ time :: Timespec , try_pin_init , ThisModule ,
1414} ;
15- use core:: { marker:: PhantomData , marker:: PhantomPinned , mem:: ManuallyDrop , pin:: Pin , ptr} ;
15+ use core:: mem:: { size_of, ManuallyDrop , MaybeUninit } ;
16+ use core:: { marker:: PhantomData , marker:: PhantomPinned , pin:: Pin , ptr} ;
1617use macros:: { pin_data, pinned_drop} ;
1718
1819#[ cfg( CONFIG_BUFFER_HEAD ) ]
@@ -37,6 +38,9 @@ pub trait FileSystem {
3738 /// Data associated with each file system instance (super-block).
3839 type Data : ForeignOwnable + Send + Sync ;
3940
41+ /// Type of data associated with each inode.
42+ type INodeData : Send + Sync ;
43+
4044 /// The name of the file system type.
4145 const NAME : & ' static CStr ;
4246
@@ -161,6 +165,7 @@ impl core::convert::TryFrom<u32> for DirEntryType {
161165pub struct Registration {
162166 #[ pin]
163167 fs : Opaque < bindings:: file_system_type > ,
168+ inode_cache : Option < MemCache > ,
164169 #[ pin]
165170 _pin : PhantomPinned ,
166171}
@@ -178,6 +183,14 @@ impl Registration {
178183 pub fn new < T : FileSystem + ?Sized > ( module : & ' static ThisModule ) -> impl PinInit < Self , Error > {
179184 try_pin_init ! ( Self {
180185 _pin: PhantomPinned ,
186+ inode_cache: if size_of:: <T :: INodeData >( ) == 0 {
187+ None
188+ } else {
189+ Some ( MemCache :: try_new:: <INodeWithData <T :: INodeData >>(
190+ T :: NAME ,
191+ Some ( Self :: inode_init_once_callback:: <T >) ,
192+ ) ?)
193+ } ,
181194 fs <- Opaque :: try_ffi_init( |fs_ptr: * mut bindings:: file_system_type| {
182195 // SAFETY: `try_ffi_init` guarantees that `fs_ptr` is valid for write.
183196 unsafe { fs_ptr. write( bindings:: file_system_type:: default ( ) ) } ;
@@ -235,6 +248,16 @@ impl Registration {
235248 unsafe { T :: Data :: from_foreign ( ptr) } ;
236249 }
237250 }
251+
252+ unsafe extern "C" fn inode_init_once_callback < T : FileSystem + ?Sized > (
253+ outer_inode : * mut core:: ffi:: c_void ,
254+ ) {
255+ let ptr = outer_inode. cast :: < INodeWithData < T :: INodeData > > ( ) ;
256+
257+ // SAFETY: This is only used in `new`, so we know that we have a valid `INodeWithData`
258+ // instance whose inode part can be initialised.
259+ unsafe { bindings:: inode_init_once ( ptr:: addr_of_mut!( ( * ptr) . inode) ) } ;
260+ }
238261}
239262
240263#[ pinned_drop]
@@ -276,6 +299,15 @@ impl<T: FileSystem + ?Sized> INode<T> {
276299 unsafe { & * ( * self . 0 . get ( ) ) . i_sb . cast ( ) }
277300 }
278301
302+ /// Returns the data associated with the inode.
303+ pub fn data ( & self ) -> & T :: INodeData {
304+ let outerp = container_of ! ( self . 0 . get( ) , INodeWithData <T :: INodeData >, inode) ;
305+ // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference
306+ // (`&self`) to it. Additionally, we know `T::INodeData` is always initialised in an
307+ // `INode`.
308+ unsafe { & * ( * outerp) . data . as_ptr ( ) }
309+ }
310+
279311 /// Returns the size of the inode contents.
280312 pub fn size ( & self ) -> i64 {
281313 // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference.
@@ -296,15 +328,29 @@ unsafe impl<T: FileSystem + ?Sized> AlwaysRefCounted for INode<T> {
296328 }
297329}
298330
331+ struct INodeWithData < T > {
332+ data : MaybeUninit < T > ,
333+ inode : bindings:: inode ,
334+ }
335+
299336/// An inode that is locked and hasn't been initialised yet.
300337#[ repr( transparent) ]
301338pub struct NewINode < T : FileSystem + ?Sized > ( ARef < INode < T > > ) ;
302339
303340impl < T : FileSystem + ?Sized > NewINode < T > {
304341 /// Initialises the new inode with the given parameters.
305- pub fn init ( self , params : INodeParams ) -> Result < ARef < INode < T > > > {
306- // SAFETY: This is a new inode, so it's safe to manipulate it mutably.
307- let inode = unsafe { & mut * self . 0 . 0 . get ( ) } ;
342+ pub fn init ( self , params : INodeParams < T :: INodeData > ) -> Result < ARef < INode < T > > > {
343+ let outerp = container_of ! ( self . 0 . 0 . get( ) , INodeWithData <T :: INodeData >, inode) ;
344+
345+ // SAFETY: This is a newly-created inode. No other references to it exist, so it is
346+ // safe to mutably dereference it.
347+ let outer = unsafe { & mut * outerp. cast_mut ( ) } ;
348+
349+ // N.B. We must always write this to a newly allocated inode because the free callback
350+ // expects the data to be initialised and drops it.
351+ outer. data . write ( params. value ) ;
352+
353+ let inode = & mut outer. inode ;
308354
309355 let mode = match params. typ {
310356 INodeType :: Dir => {
@@ -420,7 +466,7 @@ pub enum INodeType {
420466/// Required inode parameters.
421467///
422468/// This is used when creating new inodes.
423- pub struct INodeParams {
469+ pub struct INodeParams < T > {
424470 /// The access mode. It's a mask that grants execute (1), write (2) and read (4) access to
425471 /// everyone, the owner group, and the owner.
426472 pub mode : u16 ,
@@ -455,6 +501,9 @@ pub struct INodeParams {
455501
456502 /// Last access time.
457503 pub atime : Timespec ,
504+
505+ /// Value to attach to this node.
506+ pub value : T ,
458507}
459508
460509/// A file system super block.
@@ -751,8 +800,12 @@ impl<T: FileSystem + ?Sized> Tables<T> {
751800 }
752801
753802 const SUPER_BLOCK : bindings:: super_operations = bindings:: super_operations {
754- alloc_inode : None ,
755- destroy_inode : None ,
803+ alloc_inode : if size_of :: < T :: INodeData > ( ) != 0 {
804+ Some ( Self :: alloc_inode_callback)
805+ } else {
806+ None
807+ } ,
808+ destroy_inode : Some ( Self :: destroy_inode_callback) ,
756809 free_inode : None ,
757810 dirty_inode : None ,
758811 write_inode : None ,
@@ -782,6 +835,61 @@ impl<T: FileSystem + ?Sized> Tables<T> {
782835 shutdown : None ,
783836 } ;
784837
838+ unsafe extern "C" fn alloc_inode_callback (
839+ sb : * mut bindings:: super_block ,
840+ ) -> * mut bindings:: inode {
841+ // SAFETY: The callback contract guarantees that `sb` is valid for read.
842+ let super_type = unsafe { ( * sb) . s_type } ;
843+
844+ // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily
845+ // embedded in a `Registration`, which is guaranteed to be valid because it has a
846+ // superblock associated to it.
847+ let reg = unsafe { & * container_of ! ( super_type, Registration , fs) } ;
848+
849+ // SAFETY: `sb` and `cache` are guaranteed to be valid by the callback contract and by
850+ // the existence of a superblock respectively.
851+ let ptr = unsafe {
852+ bindings:: alloc_inode_sb ( sb, MemCache :: ptr ( & reg. inode_cache ) , bindings:: GFP_KERNEL )
853+ }
854+ . cast :: < INodeWithData < T :: INodeData > > ( ) ;
855+ if ptr. is_null ( ) {
856+ return ptr:: null_mut ( ) ;
857+ }
858+ ptr:: addr_of_mut!( ( * ptr) . inode)
859+ }
860+
861+ unsafe extern "C" fn destroy_inode_callback ( inode : * mut bindings:: inode ) {
862+ // SAFETY: By the C contract, `inode` is a valid pointer.
863+ let is_bad = unsafe { bindings:: is_bad_inode ( inode) } ;
864+
865+ // SAFETY: The inode is guaranteed to be valid by the callback contract. Additionally, the
866+ // superblock is also guaranteed to still be valid by the inode existence.
867+ let super_type = unsafe { ( * ( * inode) . i_sb ) . s_type } ;
868+
869+ // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily
870+ // embedded in a `Registration`, which is guaranteed to be valid because it has a
871+ // superblock associated to it.
872+ let reg = unsafe { & * container_of ! ( super_type, Registration , fs) } ;
873+ let ptr = container_of ! ( inode, INodeWithData <T :: INodeData >, inode) . cast_mut ( ) ;
874+
875+ if !is_bad {
876+ // SAFETY: The code either initialises the data or marks the inode as bad. Since the
877+ // inode is not bad, the data is initialised, and thus safe to drop.
878+ unsafe { ptr:: drop_in_place ( ( * ptr) . data . as_mut_ptr ( ) ) } ;
879+ }
880+
881+ if size_of :: < T :: INodeData > ( ) == 0 {
882+ // SAFETY: When the size of `INodeData` is zero, we don't use a separate mem_cache, so
883+ // it is allocated from the regular mem_cache, which is what `free_inode_nonrcu` uses
884+ // to free the inode.
885+ unsafe { bindings:: free_inode_nonrcu ( inode) } ;
886+ } else {
887+ // The callback contract guarantees that the inode was previously allocated via the
888+ // `alloc_inode_callback` callback, so it is safe to free it back to the cache.
889+ unsafe { bindings:: kmem_cache_free ( MemCache :: ptr ( & reg. inode_cache ) , ptr. cast ( ) ) } ;
890+ }
891+ }
892+
785893 unsafe extern "C" fn statfs_callback (
786894 dentry : * mut bindings:: dentry ,
787895 buf : * mut bindings:: kstatfs ,
@@ -1136,6 +1244,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
11361244/// struct MyFs;
11371245/// impl fs::FileSystem for MyFs {
11381246/// type Data = ();
1247+ /// type INodeData =();
11391248/// const NAME: &'static CStr = c_str!("myfs");
11401249/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
11411250/// todo!()
0 commit comments