|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Kernel memory caches (kmem_cache). |
| 4 | +//! |
| 5 | +//! C headers: [`include/linux/slab.h`](../../include/linux/slab.h) |
| 6 | +
|
| 7 | +use crate::error::{code::*, Result}; |
| 8 | +use crate::{bindings, str::CStr}; |
| 9 | +use core::ptr; |
| 10 | + |
| 11 | +/// A kernel memory cache. |
| 12 | +/// |
| 13 | +/// This isn't ready to be made public yet because it only provides functionality useful for the |
| 14 | +/// allocation of inodes in file systems. |
| 15 | +pub(crate) struct MemCache { |
| 16 | + ptr: ptr::NonNull<bindings::kmem_cache>, |
| 17 | +} |
| 18 | + |
| 19 | +impl MemCache { |
| 20 | + /// Allocates a new `kmem_cache` for type `T`. |
| 21 | + /// |
| 22 | + /// `init` is called by the C code when entries are allocated. |
| 23 | + #[allow(dead_code)] |
| 24 | + pub(crate) fn try_new<T>( |
| 25 | + name: &'static CStr, |
| 26 | + init: Option<unsafe extern "C" fn(*mut core::ffi::c_void)>, |
| 27 | + ) -> Result<Self> { |
| 28 | + // SAFETY: `name` is static, so always valid. |
| 29 | + let ptr = ptr::NonNull::new(unsafe { |
| 30 | + bindings::kmem_cache_create( |
| 31 | + name.as_char_ptr(), |
| 32 | + core::mem::size_of::<T>().try_into()?, |
| 33 | + core::mem::align_of::<T>().try_into()?, |
| 34 | + bindings::SLAB_RECLAIM_ACCOUNT | bindings::SLAB_MEM_SPREAD | bindings::SLAB_ACCOUNT, |
| 35 | + init, |
| 36 | + ) |
| 37 | + }) |
| 38 | + .ok_or(ENOMEM)?; |
| 39 | + |
| 40 | + Ok(Self { ptr }) |
| 41 | + } |
| 42 | + |
| 43 | + /// Returns the pointer to the `kmem_cache` instance, or null if it's `None`. |
| 44 | + /// |
| 45 | + /// This is a helper for functions like `alloc_inode_sb` where the cache is optional. |
| 46 | + #[allow(dead_code)] |
| 47 | + pub(crate) fn ptr(c: &Option<Self>) -> *mut bindings::kmem_cache { |
| 48 | + match c { |
| 49 | + Some(m) => m.ptr.as_ptr(), |
| 50 | + None => ptr::null_mut(), |
| 51 | + } |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +impl Drop for MemCache { |
| 56 | + fn drop(&mut self) { |
| 57 | + // SAFETY: Just an FFI call with no additional safety requirements. |
| 58 | + unsafe { bindings::rcu_barrier() }; |
| 59 | + |
| 60 | + // SAFETY: `ptr` was previously returned by successful call to `kmem_cache_create`, so it's |
| 61 | + // ok to destroy it here. |
| 62 | + unsafe { bindings::kmem_cache_destroy(self.ptr.as_ptr()) }; |
| 63 | + } |
| 64 | +} |
0 commit comments