Re: [RFC PATCH 17/19] rust: fs: allow per-inode data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Wedson Almeida Filho <wedsonaf@xxxxxxxxx> writes:

[...]

> @@ -239,6 +255,16 @@ pub fn new<T: FileSystem + ?Sized>(module: &'static ThisModule) -> impl PinInit<
>              unsafe { T::Data::from_foreign(ptr) };
>          }
>      }
> +
> +    unsafe extern "C" fn inode_init_once_callback<T: FileSystem + ?Sized>(
> +        outer_inode: *mut core::ffi::c_void,
> +    ) {

A docstring with intended use for this function would be nice.

> +        let ptr = outer_inode.cast::<INodeWithData<T::INodeData>>();
> +
> +        // SAFETY: This is only used in `new`, so we know that we have a valid `INodeWithData`
> +        // instance whose inode part can be initialised.

What does "This" refer to here?

> +        unsafe { bindings::inode_init_once(ptr::addr_of_mut!((*ptr).inode)) };
> +    }
>  }
>  
>  #[pinned_drop]
> @@ -280,6 +306,15 @@ pub fn super_block(&self) -> &SuperBlock<T> {
>          unsafe { &*(*self.0.get()).i_sb.cast() }

I would prefer the target type of the cast to be explicit.

[...]

>  
>  impl<T: FileSystem + ?Sized> NewINode<T> {
>      /// Initialises the new inode with the given parameters.
> -    pub fn init(self, params: INodeParams) -> Result<ARef<INode<T>>> {
> -        // SAFETY: This is a new inode, so it's safe to manipulate it mutably.
> -        let inode = unsafe { &mut *self.0 .0.get() };
> +    pub fn init(self, params: INodeParams<T::INodeData>) -> Result<ARef<INode<T>>> {
> +        let outerp = container_of!(self.0 .0.get(), INodeWithData<T::INodeData>, inode);
> +
> +        // SAFETY: This is a newly-created inode. No other references to it exist, so it is
> +        // safe to mutably dereference it.
> +        let outer = unsafe { &mut *outerp.cast_mut() };

Same

[...]

> @@ -766,6 +822,61 @@ impl<T: FileSystem + ?Sized> Tables<T> {
>          shutdown: None,
>      };
>  
> +    unsafe extern "C" fn alloc_inode_callback(
> +        sb: *mut bindings::super_block,
> +    ) -> *mut bindings::inode {
> +        // SAFETY: The callback contract guarantees that `sb` is valid for read.
> +        let super_type = unsafe { (*sb).s_type };
> +
> +        // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily
> +        // embedded in a `Registration`, which is guaranteed to be valid because it has a
> +        // superblock associated to it.
> +        let reg = unsafe { &*container_of!(super_type, Registration, fs) };
> +
> +        // SAFETY: `sb` and `cache` are guaranteed to be valid by the callback contract and by
> +        // the existence of a superblock respectively.
> +        let ptr = unsafe {
> +            bindings::alloc_inode_sb(sb, MemCache::ptr(&reg.inode_cache), bindings::GFP_KERNEL)
> +        }
> +        .cast::<INodeWithData<T::INodeData>>();
> +        if ptr.is_null() {
> +            return ptr::null_mut();
> +        }
> +        ptr::addr_of_mut!((*ptr).inode)
> +    }
> +
> +    unsafe extern "C" fn destroy_inode_callback(inode: *mut bindings::inode) {
> +        // SAFETY: By the C contract, `inode` is a valid pointer.
> +        let is_bad = unsafe { bindings::is_bad_inode(inode) };
> +
> +        // SAFETY: The inode is guaranteed to be valid by the callback contract. Additionally, the
> +        // superblock is also guaranteed to still be valid by the inode existence.
> +        let super_type = unsafe { (*(*inode).i_sb).s_type };
> +
> +        // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily
> +        // embedded in a `Registration`, which is guaranteed to be valid because it has a
> +        // superblock associated to it.
> +        let reg = unsafe { &*container_of!(super_type, Registration, fs) };
> +        let ptr = container_of!(inode, INodeWithData<T::INodeData>, inode).cast_mut();

Same

> +
> +        if !is_bad {
> +            // SAFETY: The code either initialises the data or marks the inode as bad. Since the
> +            // inode is not bad, the data is initialised, and thus safe to drop.
> +            unsafe { ptr::drop_in_place((*ptr).data.as_mut_ptr()) };
> +        }
> +
> +        if size_of::<T::INodeData>() == 0 {
> +            // SAFETY: When the size of `INodeData` is zero, we don't use a separate mem_cache, so
> +            // it is allocated from the regular mem_cache, which is what `free_inode_nonrcu` uses
> +            // to free the inode.
> +            unsafe { bindings::free_inode_nonrcu(inode) };
> +        } else {
> +            // The callback contract guarantees that the inode was previously allocated via the
> +            // `alloc_inode_callback` callback, so it is safe to free it back to the cache.
> +            unsafe { bindings::kmem_cache_free(MemCache::ptr(&reg.inode_cache), ptr.cast()) };

Same

BR Andreas





[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux