//! 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::c_str; //! 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_str!("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_str!("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
usecrate::alloc::flags; usecrate::container_of; usecrate::page::PAGE_SIZE; usecrate::prelude::*; usecrate::str::CString; usecrate::sync::Arc; usecrate::sync::ArcBorrow; usecrate::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)] pubstruct Subsystem<Data> { #[pin]
subsystem: Opaque<bindings::configfs_subsystem>, #[pin]
data: Data,
}
// SAFETY: We do not provide any operations on `Subsystem`. unsafeimpl<Data> Sync for Subsystem<Data> {}
// SAFETY: Ownership of `Subsystem` can safely be transferred to other threads. unsafeimpl<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`. pubfn 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_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<&mutSelf>) { // 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. pubunsafetrait 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`]. unsafefn group(this: *constSelf) -> *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`]. unsafefn container_of(group: *const bindings::config_group) -> *constSelf;
}
// SAFETY: `Subsystem<Data>` embeds a field of type `bindings::config_group` // within the `subsystem` field. unsafeimpl<Data> HasGroup<Data> for Subsystem<Data> { unsafefn group(this: *constSelf) -> *const bindings::config_group { // SAFETY: By impl and function safety requirement this projection is in bounds. unsafe { &raw const (*(*this).subsystem.get()).su_group }
}
unsafefn container_of(group: *const bindings::config_group) -> *constSelf { // 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] pubstruct 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`. pubfn 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.as_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. unsafeimpl<Data> HasGroup<Data> for Group<Data> { unsafefn group(this: *constSelf) -> *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 },
)
}
unsafefn container_of(group: *const bindings::config_group) -> *constSelf { 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>`. unsafefn 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 }
}
}
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. unsafeextern"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>`. unsafeextern"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()) };
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. unsafeextern"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);
}
/// Operations implemented by configfs groups that can create subgroups. /// /// Implement this trait on structs that embed a [`Subsystem`] or a [`Group`]. #[vtable] pubtrait 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)] pubstruct Attribute<const ID: u64, O, Data> {
attribute: Opaque<bindings::configfs_attribute>,
_p: PhantomData<(O, Data)>,
}
// SAFETY: We do not provide any operations on `Attribute`. unsafeimpl<const ID: u64, O, Data> Sync for Attribute<ID, O, Data> {}
// SAFETY: Ownership of `Attribute` can safely be transferred to other threads. unsafeimpl<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`]. unsafeextern"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`. unsafeextern"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`. pubconstfn new(name: &'static CStr) -> Self { Self {
attribute: Opaque::new(bindings::configfs_attribute {
ca_name: name.as_char_ptr(),
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] pubtrait 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)] pubstruct 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. unsafeimpl<const N: usize, Data> Send for AttributeList<N, Data> {}
// SAFETY: We do not provide any operations on `AttributeList` that need synchronization. unsafeimpl<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)] pubconstunsafefn 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)] pubconstunsafefn 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] pubstruct 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. unsafeimpl<Container, Data> Sync for ItemType<Container, Data> {}
// SAFETY: Ownership of `ItemType` can safely be transferred to other threads. unsafeimpl<Container, Data> Send for ItemType<Container, Data> {}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.