Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045// SPDX-License-Identifier: GPL-2.0
//! configfs interface: Userspace-driven Kernel Object Configuration//!//! configfs is an in-memory pseudo file system for configuration of kernel//! modules. Please see the [C documentation] for details and intended use of//! configfs.//!//! This module does not support the following configfs features://!//! - Items. All group children are groups.//! - Symlink support.//! - `disconnect_notify` hook.//! - Default groups.//!//! See the [`rust_configfs.rs`] sample for a full example use of this module.//!//! C header: [`include/linux/configfs.h`](srctree/include/linux/configfs.h)//!//! # Examples//!//! ```ignore//! use kernel::alloc::flags;//! use kernel::configfs_attrs;//! use kernel::configfs;//! use kernel::new_mutex;//! use kernel::page::PAGE_SIZE;//! use kernel::sync::Mutex;//! use kernel::ThisModule;//!//! #[pin_data]//! struct RustConfigfs {//! #[pin]//! config: configfs::Subsystem<Configuration>,//! }//!//! impl kernel::InPlaceModule for RustConfigfs {//! fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {//! pr_info!("Rust configfs sample (init)\n");//!//! let item_type = configfs_attrs! {//! container: configfs::Subsystem<Configuration>,//! data: Configuration,//! attributes: [//! message: 0,//! bar: 1,//! ],//! };//!//! try_pin_init!(Self {//! config <- configfs::Subsystem::new(//! c"rust_configfs", item_type, Configuration::new()//! ),//! })//! }//! }//!//! #[pin_data]//! struct Configuration {//! message: &'static CStr,//! #[pin]//! bar: Mutex<(KBox<[u8; PAGE_SIZE]>, usize)>,//! }//!//! impl Configuration {//! fn new() -> impl PinInit<Self, Error> {//! try_pin_init!(Self {//! message: c"Hello World\n",//! bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KERNEL)?, 0)),//! })//! }//! }//!//! #[vtable]//! impl configfs::AttributeOperations<0> for Configuration {//! type Data = Configuration;//!//! fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {//! pr_info!("Show message\n");//! let data = container.message;//! page[0..data.len()].copy_from_slice(data);//! Ok(data.len())//! }//! }//!//! #[vtable]//! impl configfs::AttributeOperations<1> for Configuration {//! type Data = Configuration;//!//! fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {//! pr_info!("Show bar\n");//! let guard = container.bar.lock();//! let data = guard.0.as_slice();//! let len = guard.1;//! page[0..len].copy_from_slice(&data[0..len]);//! Ok(len)//! }//!//! fn store(container: &Configuration, page: &[u8]) -> Result {//! pr_info!("Store bar\n");//! let mut guard = container.bar.lock();//! guard.0[0..page.len()].copy_from_slice(page);//! guard.1 = page.len();//! Ok(())//! }//! }//! ```//!//! [C documentation]: srctree/Documentation/filesystems/configfs.rst//! [`rust_configfs.rs`]: srctree/samples/rust/rust_configfs.rs
use crate::alloc::flags;use crate::container_of;use crate::page::PAGE_SIZE;use crate::prelude::*;use crate::str::CString;use crate::sync::Arc;use crate::sync::ArcBorrow;use crate::types::Opaque;use core::cell::UnsafeCell;use core::marker::PhantomData;
/// A configfs subsystem.////// This is the top level entrypoint for a configfs hierarchy. To register/// with configfs, embed a field of this type into your kernel module struct.#[pin_data(PinnedDrop)]pub struct Subsystem<Data> { #[pin] subsystem: Opaque<bindings::configfs_subsystem>, #[pin] data: Data,}
// SAFETY: We do not provide any operations on `Subsystem`.unsafe impl<Data> Sync for Subsystem<Data> {}
// SAFETY: Ownership of `Subsystem` can safely be transferred to other threads.unsafe impl<Data> Send for Subsystem<Data> {}
impl<Data> Subsystem<Data> { /// Create an initializer for a [`Subsystem`]. /// /// The subsystem will appear in configfs as a directory name given by /// `name`. The attributes available in directory are specified by /// `item_type`. pub fn new( name: &'static CStr, item_type: &'static ItemType<Subsystem<Data>, Data>, data: impl PinInit<Data, Error>, ) -> impl PinInit<Self, Error> { try_pin_init!(Self { subsystem <- pin_init::init_zeroed().chain( |place: &mut Opaque<bindings::configfs_subsystem>| { // SAFETY: We initialized the required fields of `place.group` above. unsafe { bindings::config_group_init_type_name( &mut (*place.get()).su_group, name.as_char_ptr(), item_type.as_ptr(), ) };
// SAFETY: `place.su_mutex` is valid for use as a mutex. unsafe { bindings::__mutex_init( &mut (*place.get()).su_mutex, kernel::optional_name!().as_char_ptr(), kernel::static_lock_class!().as_ptr(), ) } Ok(()) } ), data <- data, }) .pin_chain(|this| { crate::error::to_result( // SAFETY: We initialized `this.subsystem` according to C API contract above. unsafe { bindings::configfs_register_subsystem(this.subsystem.get()) }, ) }) }}
#[pinned_drop]impl<Data> PinnedDrop for Subsystem<Data> { fn drop(self: Pin<&mut Self>) { // SAFETY: We registered `self.subsystem` in the initializer returned by `Self::new`. unsafe { bindings::configfs_unregister_subsystem(self.subsystem.get()) }; // SAFETY: We initialized the mutex in `Subsystem::new`. unsafe { bindings::mutex_destroy(&raw mut (*self.subsystem.get()).su_mutex) }; }}
/// Trait that allows offset calculations for structs that embed a/// `bindings::config_group`.////// Users of the configfs API should not need to implement this trait.////// # Safety////// - Implementers of this trait must embed a `bindings::config_group`./// - Methods must be implemented according to method documentation.pub unsafe trait HasGroup<Data> { /// Return the address of the `bindings::config_group` embedded in [`Self`]. /// /// # Safety /// /// - `this` must be a valid allocation of at least the size of [`Self`]. unsafe fn group(this: *const Self) -> *const bindings::config_group;
/// Return the address of the [`Self`] that `group` is embedded in. /// /// # Safety /// /// - `group` must point to the `bindings::config_group` that is embedded in /// [`Self`]. unsafe fn container_of(group: *const bindings::config_group) -> *const Self;}
// SAFETY: `Subsystem<Data>` embeds a field of type `bindings::config_group`// within the `subsystem` field.unsafe impl<Data> HasGroup<Data> for Subsystem<Data> { unsafe fn group(this: *const Self) -> *const bindings::config_group { // SAFETY: By impl and function safety requirement this projection is in bounds. unsafe { &raw const (*(*this).subsystem.get()).su_group } }
unsafe fn container_of(group: *const bindings::config_group) -> *const Self { // SAFETY: By impl and function safety requirement this projection is in bounds. let c_subsys_ptr = unsafe { container_of!(group, bindings::configfs_subsystem, su_group) }; let opaque_ptr = c_subsys_ptr.cast::<Opaque<bindings::configfs_subsystem>>(); // SAFETY: By impl and function safety requirement, `opaque_ptr` and the // pointer it returns, are within the same allocation. unsafe { container_of!(opaque_ptr, Subsystem<Data>, subsystem) } }}
/// A configfs group.////// To add a subgroup to configfs, pass this type as `ctype` to/// [`crate::configfs_attrs`] when creating a group in [`GroupOperations::make_group`].#[pin_data]pub struct Group<Data> { #[pin] group: Opaque<bindings::config_group>, #[pin] data: Data,}
impl<Data> Group<Data> { /// Create an initializer for a new group. /// /// When instantiated, the group will appear as a directory with the name /// given by `name` and it will contain attributes specified by `item_type`. pub fn new( name: CString, item_type: &'static ItemType<Group<Data>, Data>, data: impl PinInit<Data, Error>, ) -> impl PinInit<Self, Error> { try_pin_init!(Self { group <- pin_init::init_zeroed().chain(|v: &mut Opaque<bindings::config_group>| { let place = v.get(); let name = name.to_bytes_with_nul().as_ptr(); // SAFETY: It is safe to initialize a group once it has been zeroed. unsafe { bindings::config_group_init_type_name(place, name.cast(), item_type.as_ptr()) }; Ok(()) }), data <- data, }) }}
// SAFETY: `Group<Data>` embeds a field of type `bindings::config_group`// within the `group` field.unsafe impl<Data> HasGroup<Data> for Group<Data> { unsafe fn group(this: *const Self) -> *const bindings::config_group { Opaque::cast_into( // SAFETY: By impl and function safety requirements this field // projection is within bounds of the allocation. unsafe { &raw const (*this).group }, ) }
unsafe fn container_of(group: *const bindings::config_group) -> *const Self { let opaque_ptr = group.cast::<Opaque<bindings::config_group>>(); // SAFETY: By impl and function safety requirement, `opaque_ptr` and // pointer it returns will be in the same allocation. unsafe { container_of!(opaque_ptr, Self, group) } }}
/// # Safety////// `this` must be a valid pointer.////// If `this` does not represent the root group of a configfs subsystem,/// `this` must be a pointer to a `bindings::config_group` embedded in a/// `Group<Parent>`.////// Otherwise, `this` must be a pointer to a `bindings::config_group` that/// is embedded in a `bindings::configfs_subsystem` that is embedded in a/// `Subsystem<Parent>`.unsafe fn get_group_data<'a, Parent>(this: *mut bindings::config_group) -> &'a Parent { // SAFETY: `this` is a valid pointer. let is_root = unsafe { (*this).cg_subsys.is_null() };
if !is_root { // SAFETY: By C API contact,`this` was returned from a call to // `make_group`. The pointer is known to be embedded within a // `Group<Parent>`. unsafe { &(*Group::<Parent>::container_of(this)).data } } else { // SAFETY: By C API contract, `this` is a pointer to the // `bindings::config_group` field within a `Subsystem<Parent>`. unsafe { &(*Subsystem::container_of(this)).data } }}
struct GroupOperationsVTable<Parent, Child>(PhantomData<(Parent, Child)>);
impl<Parent, Child> GroupOperationsVTable<Parent, Child>where Parent: GroupOperations<Child = Child>, Child: 'static,{ /// # Safety /// /// `this` must be a valid pointer. /// /// If `this` does not represent the root group of a configfs subsystem, /// `this` must be a pointer to a `bindings::config_group` embedded in a /// `Group<Parent>`. /// /// Otherwise, `this` must be a pointer to a `bindings::config_group` that /// is embedded in a `bindings::configfs_subsystem` that is embedded in a /// `Subsystem<Parent>`. /// /// `name` must point to a null terminated string. unsafe extern "C" fn make_group( this: *mut bindings::config_group, name: *const kernel::ffi::c_char, ) -> *mut bindings::config_group { // SAFETY: By function safety requirements of this function, this call // is safe. let parent_data = unsafe { get_group_data(this) };
let group_init = match Parent::make_group( parent_data, // SAFETY: By function safety requirements, name points to a null // terminated string. unsafe { CStr::from_char_ptr(name) }, ) { Ok(init) => init, Err(e) => return e.to_ptr(), };
let child_group = <Arc<Group<Child>> as InPlaceInit<Group<Child>>>::try_pin_init( group_init, flags::GFP_KERNEL, );
match child_group { Ok(child_group) => { let child_group_ptr = child_group.into_raw(); // SAFETY: We allocated the pointee of `child_ptr` above as a // `Group<Child>`. unsafe { Group::<Child>::group(child_group_ptr) }.cast_mut() } Err(e) => e.to_ptr(), } }
/// # Safety /// /// If `this` does not represent the root group of a configfs subsystem, /// `this` must be a pointer to a `bindings::config_group` embedded in a /// `Group<Parent>`. /// /// Otherwise, `this` must be a pointer to a `bindings::config_group` that /// is embedded in a `bindings::configfs_subsystem` that is embedded in a /// `Subsystem<Parent>`. /// /// `item` must point to a `bindings::config_item` within a /// `bindings::config_group` within a `Group<Child>`. unsafe extern "C" fn drop_item( this: *mut bindings::config_group, item: *mut bindings::config_item, ) { // SAFETY: By function safety requirements of this function, this call // is safe. let parent_data = unsafe { get_group_data(this) };
// SAFETY: By function safety requirements, `item` is embedded in a // `config_group`. let c_child_group_ptr = unsafe { container_of!(item, bindings::config_group, cg_item) }; // SAFETY: By function safety requirements, `c_child_group_ptr` is // embedded within a `Group<Child>`. let r_child_group_ptr = unsafe { Group::<Child>::container_of(c_child_group_ptr) };
if Parent::HAS_DROP_ITEM { // SAFETY: We called `into_raw` to produce `r_child_group_ptr` in // `make_group`. let arc: Arc<Group<Child>> = unsafe { Arc::from_raw(r_child_group_ptr.cast_mut()) };
Parent::drop_item(parent_data, arc.as_arc_borrow()); arc.into_raw(); }
// SAFETY: By C API contract, we are required to drop a refcount on // `item`. unsafe { bindings::config_item_put(item) }; }
const VTABLE: bindings::configfs_group_operations = bindings::configfs_group_operations { make_item: None, make_group: Some(Self::make_group), disconnect_notify: None, drop_item: Some(Self::drop_item), is_visible: None, is_bin_visible: None, };
const fn vtable_ptr() -> *const bindings::configfs_group_operations { &Self::VTABLE }}
struct ItemOperationsVTable<Container, Data>(PhantomData<(Container, Data)>);
impl<Data> ItemOperationsVTable<Group<Data>, Data>where Data: 'static,{ /// # Safety /// /// `this` must be a pointer to a `bindings::config_group` embedded in a /// `Group<Parent>`. /// /// This function will destroy the pointee of `this`. The pointee of `this` /// must not be accessed after the function returns. unsafe extern "C" fn release(this: *mut bindings::config_item) { // SAFETY: By function safety requirements, `this` is embedded in a // `config_group`. let c_group_ptr = unsafe { kernel::container_of!(this, bindings::config_group, cg_item) }; // SAFETY: By function safety requirements, `c_group_ptr` is // embedded within a `Group<Data>`. let r_group_ptr = unsafe { Group::<Data>::container_of(c_group_ptr) };
// SAFETY: We called `into_raw` on `r_group_ptr` in // `make_group`. let pin_self: Arc<Group<Data>> = unsafe { Arc::from_raw(r_group_ptr.cast_mut()) }; drop(pin_self); }
const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations { release: Some(Self::release), allow_link: None, drop_link: None, };
const fn vtable_ptr() -> *const bindings::configfs_item_operations { &Self::VTABLE }}
impl<Data> ItemOperationsVTable<Subsystem<Data>, Data> { const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations { release: None, allow_link: None, drop_link: None, };
const fn vtable_ptr() -> *const bindings::configfs_item_operations { &Self::VTABLE }}
/// Operations implemented by configfs groups that can create subgroups.////// Implement this trait on structs that embed a [`Subsystem`] or a [`Group`].#[vtable]pub trait GroupOperations { /// The child data object type. /// /// This group will create subgroups (subdirectories) backed by this kind of /// object. type Child: 'static;
/// Creates a new subgroup. /// /// The kernel will call this method in response to `mkdir(2)` in the /// directory representing `this`. /// /// To accept the request to create a group, implementations should /// return an initializer of a `Group<Self::Child>`. To prevent creation, /// return a suitable error. fn make_group(&self, name: &CStr) -> Result<impl PinInit<Group<Self::Child>, Error>>;
/// Prepares the group for removal from configfs. /// /// The kernel will call this method before the directory representing `_child` is removed from /// configfs. /// /// Implementations can use this method to do house keeping before configfs drops its /// reference to `Child`. /// /// NOTE: "drop" in the name of this function is not related to the Rust drop term. Rather, the /// name is inherited from the callback name in the underlying C code. fn drop_item(&self, _child: ArcBorrow<'_, Group<Self::Child>>) { kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR) }}
/// A configfs attribute.////// An attribute appears as a file in configfs, inside a folder that represent/// the group that the attribute belongs to.#[repr(transparent)]pub struct Attribute<const ID: u64, O, Data> { attribute: Opaque<bindings::configfs_attribute>, _p: PhantomData<(O, Data)>,}
// SAFETY: We do not provide any operations on `Attribute`.unsafe impl<const ID: u64, O, Data> Sync for Attribute<ID, O, Data> {}
// SAFETY: Ownership of `Attribute` can safely be transferred to other threads.unsafe impl<const ID: u64, O, Data> Send for Attribute<ID, O, Data> {}
impl<const ID: u64, O, Data> Attribute<ID, O, Data>where O: AttributeOperations<ID, Data = Data>,{ /// # Safety /// /// `item` must be embedded in a `bindings::config_group`. /// /// If `item` does not represent the root group of a configfs subsystem, /// the group must be embedded in a `Group<Data>`. /// /// Otherwise, the group must be a embedded in a /// `bindings::configfs_subsystem` that is embedded in a `Subsystem<Data>`. /// /// `page` must point to a writable buffer of size at least [`PAGE_SIZE`]. unsafe extern "C" fn show( item: *mut bindings::config_item, page: *mut kernel::ffi::c_char, ) -> isize { let c_group: *mut bindings::config_group = // SAFETY: By function safety requirements, `item` is embedded in a // `config_group`. unsafe { container_of!(item, bindings::config_group, cg_item) };
// SAFETY: The function safety requirements for this function satisfy // the conditions for this call. let data: &Data = unsafe { get_group_data(c_group) };
// SAFETY: By function safety requirements, `page` is writable for `PAGE_SIZE`. let ret = O::show(data, unsafe { &mut *(page.cast::<[u8; PAGE_SIZE]>()) });
match ret { Ok(size) => size as isize, Err(err) => err.to_errno() as isize, } }
/// # Safety /// /// `item` must be embedded in a `bindings::config_group`. /// /// If `item` does not represent the root group of a configfs subsystem, /// the group must be embedded in a `Group<Data>`. /// /// Otherwise, the group must be a embedded in a /// `bindings::configfs_subsystem` that is embedded in a `Subsystem<Data>`. /// /// `page` must point to a readable buffer of size at least `size`. unsafe extern "C" fn store( item: *mut bindings::config_item, page: *const kernel::ffi::c_char, size: usize, ) -> isize { let c_group: *mut bindings::config_group = // SAFETY: By function safety requirements, `item` is embedded in a // `config_group`. unsafe { container_of!(item, bindings::config_group, cg_item) };
// SAFETY: The function safety requirements for this function satisfy // the conditions for this call. let data: &Data = unsafe { get_group_data(c_group) };
let ret = O::store( data, // SAFETY: By function safety requirements, `page` is readable // for at least `size`. unsafe { core::slice::from_raw_parts(page.cast(), size) }, );
match ret { Ok(()) => size as isize, Err(err) => err.to_errno() as isize, } }
/// Create a new attribute. /// /// The attribute will appear as a file with name given by `name`. pub const fn new(name: &'static CStr) -> Self { Self { attribute: Opaque::new(bindings::configfs_attribute { ca_name: crate::str::as_char_ptr_in_const_context(name), ca_owner: core::ptr::null_mut(), ca_mode: 0o660, show: Some(Self::show), store: if O::HAS_STORE { Some(Self::store) } else { None }, }), _p: PhantomData, } }}
/// Operations supported by an attribute.////// Implement this trait on type and pass that type as generic parameter when/// creating an [`Attribute`]. The type carrying the implementation serve no/// purpose other than specifying the attribute operations.////// This trait must be implemented on the `Data` type of for types that/// implement `HasGroup<Data>`. The trait must be implemented once for each/// attribute of the group. The constant type parameter `ID` maps the/// implementation to a specific `Attribute`. `ID` must be passed when declaring/// attributes via the [`kernel::configfs_attrs`] macro, to tie/// `AttributeOperations` implementations to concrete named attributes.#[vtable]pub trait AttributeOperations<const ID: u64 = 0> { /// The type of the object that contains the field that is backing the /// attribute for this operation. type Data;
/// Renders the value of an attribute. /// /// This function is called by the kernel to read the value of an attribute. /// /// Implementations should write the rendering of the attribute to `page` /// and return the number of bytes written. fn show(data: &Self::Data, page: &mut [u8; PAGE_SIZE]) -> Result<usize>;
/// Stores the value of an attribute. /// /// This function is called by the kernel to update the value of an attribute. /// /// Implementations should parse the value from `page` and update internal /// state to reflect the parsed value. fn store(_data: &Self::Data, _page: &[u8]) -> Result { kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR) }}
/// A list of attributes.////// This type is used to construct a new [`ItemType`]. It represents a list of/// [`Attribute`] that will appear in the directory representing a [`Group`]./// Users should not directly instantiate this type, rather they should use the/// [`kernel::configfs_attrs`] macro to declare a static set of attributes for a/// group.////// # Note////// Instances of this type are constructed statically at compile by the/// [`kernel::configfs_attrs`] macro.#[repr(transparent)]pub struct AttributeList<const N: usize, Data>( /// Null terminated Array of pointers to [`Attribute`]. The type is [`c_void`] /// to conform to the C API. UnsafeCell<[*mut kernel::ffi::c_void; N]>, PhantomData<Data>,);
// SAFETY: Ownership of `AttributeList` can safely be transferred to other threads.unsafe impl<const N: usize, Data> Send for AttributeList<N, Data> {}
// SAFETY: We do not provide any operations on `AttributeList` that need synchronization.unsafe impl<const N: usize, Data> Sync for AttributeList<N, Data> {}
impl<const N: usize, Data> AttributeList<N, Data> { /// # Safety /// /// This function must only be called by the [`kernel::configfs_attrs`] /// macro. #[doc(hidden)] pub const unsafe fn new() -> Self { Self(UnsafeCell::new([core::ptr::null_mut(); N]), PhantomData) }
/// # Safety /// /// The caller must ensure that there are no other concurrent accesses to /// `self`. That is, the caller has exclusive access to `self.` #[doc(hidden)] pub const unsafe fn add<const I: usize, const ID: u64, O>( &'static self, attribute: &'static Attribute<ID, O, Data>, ) where O: AttributeOperations<ID, Data = Data>, { // We need a space at the end of our list for a null terminator. const { assert!(I < N - 1, "Invalid attribute index") };
// SAFETY: By function safety requirements, we have exclusive access to // `self` and the reference created below will be exclusive. unsafe { (&mut *self.0.get())[I] = core::ptr::from_ref(attribute).cast_mut().cast() }; }}
/// A representation of the attributes that will appear in a [`Group`] or/// [`Subsystem`].////// Users should not directly instantiate objects of this type. Rather, they/// should use the [`kernel::configfs_attrs`] macro to statically declare the/// shape of a [`Group`] or [`Subsystem`].#[pin_data]pub struct ItemType<Container, Data> { #[pin] item_type: Opaque<bindings::config_item_type>, _p: PhantomData<(Container, Data)>,}
// SAFETY: We do not provide any operations on `ItemType` that need synchronization.unsafe impl<Container, Data> Sync for ItemType<Container, Data> {}
// SAFETY: Ownership of `ItemType` can safely be transferred to other threads.unsafe impl<Container, Data> Send for ItemType<Container, Data> {}
macro_rules! impl_item_type { ($tpe:ty) => { impl<Data> ItemType<$tpe, Data> { #[doc(hidden)] pub const fn new_with_child_ctor<const N: usize, Child>( owner: &'static ThisModule, attributes: &'static AttributeList<N, Data>, ) -> Self where Data: GroupOperations<Child = Child>, Child: 'static, { Self { item_type: Opaque::new(bindings::config_item_type { ct_owner: owner.as_ptr(), ct_group_ops: GroupOperationsVTable::<Data, Child>::vtable_ptr().cast_mut(), ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(), ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(), ct_bin_attrs: core::ptr::null_mut(), }), _p: PhantomData, } }
#[doc(hidden)] pub const fn new<const N: usize>( owner: &'static ThisModule, attributes: &'static AttributeList<N, Data>, ) -> Self { Self { item_type: Opaque::new(bindings::config_item_type { ct_owner: owner.as_ptr(), ct_group_ops: core::ptr::null_mut(), ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(), ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(), ct_bin_attrs: core::ptr::null_mut(), }), _p: PhantomData, } } } };}
impl_item_type!(Subsystem<Data>);impl_item_type!(Group<Data>);
impl<Container, Data> ItemType<Container, Data> { fn as_ptr(&self) -> *const bindings::config_item_type { self.item_type.get() }}
/// Define a list of configfs attributes statically.////// Invoking the macro in the following manner:////// ```ignore/// let item_type = configfs_attrs! {/// container: configfs::Subsystem<Configuration>,/// data: Configuration,/// child: Child,/// attributes: [/// message: 0,/// bar: 1,/// ],/// };/// ```////// Expands the following output:////// ```ignore/// let item_type = {/// static CONFIGURATION_MESSAGE_ATTR: kernel::configfs::Attribute</// 0,/// Configuration,/// Configuration,/// > = unsafe {/// kernel::configfs::Attribute::new({/// const S: &str = "message\u{0}";/// const C: &kernel::str::CStr = match kernel::str::CStr::from_bytes_with_nul(/// S.as_bytes()/// ) {/// Ok(v) => v,/// Err(_) => {/// core::panicking::panic_fmt(core::const_format_args!(/// "string contains interior NUL"/// ));/// }/// };/// C/// })/// };////// static CONFIGURATION_BAR_ATTR: kernel::configfs::Attribute</// 1,/// Configuration,/// Configuration/// > = unsafe {/// kernel::configfs::Attribute::new({/// const S: &str = "bar\u{0}";/// const C: &kernel::str::CStr = match kernel::str::CStr::from_bytes_with_nul(/// S.as_bytes()/// ) {/// Ok(v) => v,/// Err(_) => {/// core::panicking::panic_fmt(core::const_format_args!(/// "string contains interior NUL"/// ));/// }/// };/// C/// })/// };////// const N: usize = (1usize + (1usize + 0usize)) + 1usize;////// static CONFIGURATION_ATTRS: kernel::configfs::AttributeList<N, Configuration> =/// unsafe { kernel::configfs::AttributeList::new() };////// {/// const N: usize = 0usize;/// unsafe { CONFIGURATION_ATTRS.add::<N, 0, _>(&CONFIGURATION_MESSAGE_ATTR) };/// }////// {/// const N: usize = (1usize + 0usize);/// unsafe { CONFIGURATION_ATTRS.add::<N, 1, _>(&CONFIGURATION_BAR_ATTR) };/// }////// static CONFIGURATION_TPE:/// kernel::configfs::ItemType<configfs::Subsystem<Configuration> ,Configuration>/// = kernel::configfs::ItemType::</// configfs::Subsystem<Configuration>,/// Configuration/// >::new_with_child_ctor::<N,Child>(/// &THIS_MODULE,/// &CONFIGURATION_ATTRS/// );////// &CONFIGURATION_TPE/// }/// ```#[macro_export]macro_rules! configfs_attrs { ( container: $container:ty, data: $data:ty, attributes: [ $($name:ident: $attr:literal),* $(,)? ] $(,)? ) => { $crate::configfs_attrs!( count: @container($container), @data($data), @child(), @no_child(x), @attrs($($name $attr)*), @eat($($name $attr,)*), @assign(), @cnt(0usize), ) }; ( container: $container:ty, data: $data:ty, child: $child:ty, attributes: [ $($name:ident: $attr:literal),* $(,)? ] $(,)? ) => { $crate::configfs_attrs!( count: @container($container), @data($data), @child($child), @no_child(), @attrs($($name $attr)*), @eat($($name $attr,)*), @assign(), @cnt(0usize), ) }; (count: @container($container:ty), @data($data:ty), @child($($child:ty)?), @no_child($($no_child:ident)?), @attrs($($aname:ident $aattr:literal)*), @eat($name:ident $attr:literal, $($rname:ident $rattr:literal,)*), @assign($($assign:block)*), @cnt($cnt:expr), ) => { $crate::configfs_attrs!( count: @container($container), @data($data), @child($($child)?), @no_child($($no_child)?), @attrs($($aname $aattr)*), @eat($($rname $rattr,)*), @assign($($assign)* { const N: usize = $cnt; // The following macro text expands to a call to `Attribute::add`.
// SAFETY: By design of this macro, the name of the variable we // invoke the `add` method on below, is not visible outside of // the macro expansion. The macro does not operate concurrently // on this variable, and thus we have exclusive access to the // variable. unsafe { $crate::macros::paste!( [< $data:upper _ATTRS >] .add::<N, $attr, _>(&[< $data:upper _ $name:upper _ATTR >]) ) }; }), @cnt(1usize + $cnt), ) }; (count: @container($container:ty), @data($data:ty), @child($($child:ty)?), @no_child($($no_child:ident)?), @attrs($($aname:ident $aattr:literal)*), @eat(), @assign($($assign:block)*), @cnt($cnt:expr), ) => { $crate::configfs_attrs!( final: @container($container), @data($data), @child($($child)?), @no_child($($no_child)?), @attrs($($aname $aattr)*), @assign($($assign)*), @cnt($cnt), ) }; (final: @container($container:ty), @data($data:ty), @child($($child:ty)?), @no_child($($no_child:ident)?), @attrs($($name:ident $attr:literal)*), @assign($($assign:block)*), @cnt($cnt:expr), ) => { $crate::macros::paste!{ { $( // SAFETY: We are expanding `configfs_attrs`. static [< $data:upper _ $name:upper _ATTR >]: $crate::configfs::Attribute<$attr, $data, $data> = unsafe { $crate::configfs::Attribute::new( $crate::c_str!(::core::stringify!($name)), ) }; )*
// We need space for a null terminator. const N: usize = $cnt + 1usize;
// SAFETY: We are expanding `configfs_attrs`. static [< $data:upper _ATTRS >]: $crate::configfs::AttributeList<N, $data> = unsafe { $crate::configfs::AttributeList::new() };
$($assign)*
$( const [<$no_child:upper>]: bool = true;
static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data> = $crate::configfs::ItemType::<$container, $data>::new::<N>( &THIS_MODULE, &[<$ data:upper _ATTRS >] ); )?
$( static [< $data:upper _TPE >]: $crate::configfs::ItemType<$container, $data> = $crate::configfs::ItemType::<$container, $data>:: new_with_child_ctor::<N, $child>( &THIS_MODULE, &[<$ data:upper _ATTRS >] ); )?
& [< $data:upper _TPE >] } } };
}
pub use crate::configfs_attrs;