Skip to main content

kernel/
configfs.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! configfs interface: Userspace-driven Kernel Object Configuration
4//!
5//! configfs is an in-memory pseudo file system for configuration of kernel
6//! modules. Please see the [C documentation] for details and intended use of
7//! configfs.
8//!
9//! This module does not support the following configfs features:
10//!
11//! - Items. All group children are groups.
12//! - Symlink support.
13//! - `disconnect_notify` hook.
14//! - Default groups.
15//!
16//! See the [`rust_configfs.rs`] sample for a full example use of this module.
17//!
18//! C header: [`include/linux/configfs.h`](srctree/include/linux/configfs.h)
19//!
20//! # Examples
21//!
22//! ```ignore
23//! use kernel::alloc::flags;
24//! use kernel::configfs_attrs;
25//! use kernel::configfs;
26//! use kernel::new_mutex;
27//! use kernel::page::PAGE_SIZE;
28//! use kernel::sync::Mutex;
29//! use kernel::ThisModule;
30//!
31//! #[pin_data]
32//! struct RustConfigfs {
33//!     #[pin]
34//!     config: configfs::Subsystem<Configuration>,
35//! }
36//!
37//! impl kernel::InPlaceModule for RustConfigfs {
38//!     fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
39//!         pr_info!("Rust configfs sample (init)\n");
40//!
41//!         let item_type = configfs_attrs! {
42//!             container: configfs::Subsystem<Configuration>,
43//!             data: Configuration,
44//!             attributes: [
45//!                 message: 0,
46//!                 bar: 1,
47//!             ],
48//!         };
49//!
50//!         try_pin_init!(Self {
51//!             config <- configfs::Subsystem::new(
52//!                 c"rust_configfs", item_type, Configuration::new()
53//!             ),
54//!         })
55//!     }
56//! }
57//!
58//! #[pin_data]
59//! struct Configuration {
60//!     message: &'static CStr,
61//!     #[pin]
62//!     bar: Mutex<(KBox<[u8; PAGE_SIZE]>, usize)>,
63//! }
64//!
65//! impl Configuration {
66//!     fn new() -> impl PinInit<Self, Error> {
67//!         try_pin_init!(Self {
68//!             message: c"Hello World\n",
69//!             bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KERNEL)?, 0)),
70//!         })
71//!     }
72//! }
73//!
74//! #[vtable]
75//! impl configfs::AttributeOperations<0> for Configuration {
76//!     type Data = Configuration;
77//!
78//!     fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
79//!         pr_info!("Show message\n");
80//!         let data = container.message;
81//!         page[0..data.len()].copy_from_slice(data);
82//!         Ok(data.len())
83//!     }
84//! }
85//!
86//! #[vtable]
87//! impl configfs::AttributeOperations<1> for Configuration {
88//!     type Data = Configuration;
89//!
90//!     fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
91//!         pr_info!("Show bar\n");
92//!         let guard = container.bar.lock();
93//!         let data = guard.0.as_slice();
94//!         let len = guard.1;
95//!         page[0..len].copy_from_slice(&data[0..len]);
96//!         Ok(len)
97//!     }
98//!
99//!     fn store(container: &Configuration, page: &[u8]) -> Result {
100//!         pr_info!("Store bar\n");
101//!         let mut guard = container.bar.lock();
102//!         guard.0[0..page.len()].copy_from_slice(page);
103//!         guard.1 = page.len();
104//!         Ok(())
105//!     }
106//! }
107//! ```
108//!
109//! [C documentation]: srctree/Documentation/filesystems/configfs.rst
110//! [`rust_configfs.rs`]: srctree/samples/rust/rust_configfs.rs
111
112use crate::alloc::flags;
113use crate::container_of;
114use crate::page::PAGE_SIZE;
115use crate::prelude::*;
116use crate::str::CString;
117use crate::sync::Arc;
118use crate::sync::ArcBorrow;
119use crate::types::Opaque;
120use core::cell::UnsafeCell;
121use core::marker::PhantomData;
122
123/// A configfs subsystem.
124///
125/// This is the top level entrypoint for a configfs hierarchy. To register
126/// with configfs, embed a field of this type into your kernel module struct.
127#[pin_data(PinnedDrop)]
128pub struct Subsystem<Data> {
129    #[pin]
130    subsystem: Opaque<bindings::configfs_subsystem>,
131    #[pin]
132    data: Data,
133}
134
135// SAFETY: We do not provide any operations on `Subsystem`.
136unsafe impl<Data> Sync for Subsystem<Data> {}
137
138// SAFETY: Ownership of `Subsystem` can safely be transferred to other threads.
139unsafe impl<Data> Send for Subsystem<Data> {}
140
141impl<Data> Subsystem<Data> {
142    /// Create an initializer for a [`Subsystem`].
143    ///
144    /// The subsystem will appear in configfs as a directory name given by
145    /// `name`. The attributes available in directory are specified by
146    /// `item_type`.
147    pub fn new(
148        name: &'static CStr,
149        item_type: &'static ItemType<Subsystem<Data>, Data>,
150        data: impl PinInit<Data, Error>,
151    ) -> impl PinInit<Self, Error> {
152        try_pin_init!(Self {
153            subsystem <- pin_init::init_zeroed().chain(
154                |place: &mut Opaque<bindings::configfs_subsystem>| {
155                    // SAFETY: We initialized the required fields of `place.group` above.
156                    unsafe {
157                        bindings::config_group_init_type_name(
158                            &mut (*place.get()).su_group,
159                            name.as_char_ptr(),
160                            item_type.as_ptr(),
161                        )
162                    };
163
164                    // SAFETY: `place.su_mutex` is valid for use as a mutex.
165                    unsafe {
166                        bindings::__mutex_init(
167                            &mut (*place.get()).su_mutex,
168                            kernel::optional_name!().as_char_ptr(),
169                            kernel::static_lock_class!().as_ptr(),
170                        )
171                    }
172                    Ok(())
173                }
174            ),
175            data <- data,
176        })
177        .pin_chain(|this| {
178            crate::error::to_result(
179                // SAFETY: We initialized `this.subsystem` according to C API contract above.
180                unsafe { bindings::configfs_register_subsystem(this.subsystem.get()) },
181            )
182        })
183    }
184}
185
186#[pinned_drop]
187impl<Data> PinnedDrop for Subsystem<Data> {
188    fn drop(self: Pin<&mut Self>) {
189        // SAFETY: We registered `self.subsystem` in the initializer returned by `Self::new`.
190        unsafe { bindings::configfs_unregister_subsystem(self.subsystem.get()) };
191        // SAFETY: We initialized the mutex in `Subsystem::new`.
192        unsafe { bindings::mutex_destroy(&raw mut (*self.subsystem.get()).su_mutex) };
193    }
194}
195
196/// Trait that allows offset calculations for structs that embed a
197/// `bindings::config_group`.
198///
199/// Users of the configfs API should not need to implement this trait.
200///
201/// # Safety
202///
203/// - Implementers of this trait must embed a `bindings::config_group`.
204/// - Methods must be implemented according to method documentation.
205pub unsafe trait HasGroup<Data> {
206    /// Return the address of the `bindings::config_group` embedded in [`Self`].
207    ///
208    /// # Safety
209    ///
210    /// - `this` must be a valid allocation of at least the size of [`Self`].
211    unsafe fn group(this: *const Self) -> *const bindings::config_group;
212
213    /// Return the address of the [`Self`] that `group` is embedded in.
214    ///
215    /// # Safety
216    ///
217    /// - `group` must point to the `bindings::config_group` that is embedded in
218    ///   [`Self`].
219    unsafe fn container_of(group: *const bindings::config_group) -> *const Self;
220}
221
222// SAFETY: `Subsystem<Data>` embeds a field of type `bindings::config_group`
223// within the `subsystem` field.
224unsafe impl<Data> HasGroup<Data> for Subsystem<Data> {
225    unsafe fn group(this: *const Self) -> *const bindings::config_group {
226        // SAFETY: By impl and function safety requirement this projection is in bounds.
227        unsafe { &raw const (*(*this).subsystem.get()).su_group }
228    }
229
230    unsafe fn container_of(group: *const bindings::config_group) -> *const Self {
231        // SAFETY: By impl and function safety requirement this projection is in bounds.
232        let c_subsys_ptr = unsafe { container_of!(group, bindings::configfs_subsystem, su_group) };
233        let opaque_ptr = c_subsys_ptr.cast::<Opaque<bindings::configfs_subsystem>>();
234        // SAFETY: By impl and function safety requirement, `opaque_ptr` and the
235        // pointer it returns, are within the same allocation.
236        unsafe { container_of!(opaque_ptr, Subsystem<Data>, subsystem) }
237    }
238}
239
240/// A configfs group.
241///
242/// To add a subgroup to configfs, pass this type as `ctype` to
243/// [`crate::configfs_attrs`] when creating a group in [`GroupOperations::make_group`].
244#[pin_data]
245pub struct Group<Data> {
246    #[pin]
247    group: Opaque<bindings::config_group>,
248    #[pin]
249    data: Data,
250}
251
252impl<Data> Group<Data> {
253    /// Create an initializer for a new group.
254    ///
255    /// When instantiated, the group will appear as a directory with the name
256    /// given by `name` and it will contain attributes specified by `item_type`.
257    pub fn new(
258        name: CString,
259        item_type: &'static ItemType<Group<Data>, Data>,
260        data: impl PinInit<Data, Error>,
261    ) -> impl PinInit<Self, Error> {
262        try_pin_init!(Self {
263            group <- pin_init::init_zeroed().chain(|v: &mut Opaque<bindings::config_group>| {
264                let place = v.get();
265                let name = name.to_bytes_with_nul().as_ptr();
266                // SAFETY: It is safe to initialize a group once it has been zeroed.
267                unsafe {
268                    bindings::config_group_init_type_name(place, name.cast(), item_type.as_ptr())
269                };
270                Ok(())
271            }),
272            data <- data,
273        })
274    }
275}
276
277// SAFETY: `Group<Data>` embeds a field of type `bindings::config_group`
278// within the `group` field.
279unsafe impl<Data> HasGroup<Data> for Group<Data> {
280    unsafe fn group(this: *const Self) -> *const bindings::config_group {
281        Opaque::cast_into(
282            // SAFETY: By impl and function safety requirements this field
283            // projection is within bounds of the allocation.
284            unsafe { &raw const (*this).group },
285        )
286    }
287
288    unsafe fn container_of(group: *const bindings::config_group) -> *const Self {
289        let opaque_ptr = group.cast::<Opaque<bindings::config_group>>();
290        // SAFETY: By impl and function safety requirement, `opaque_ptr` and
291        // pointer it returns will be in the same allocation.
292        unsafe { container_of!(opaque_ptr, Self, group) }
293    }
294}
295
296/// # Safety
297///
298/// `this` must be a valid pointer.
299///
300/// If `this` is the `su_group` field of a `bindings::configfs_subsystem`, that
301/// `configfs_subsystem` must be embedded in a `Subsystem<Parent>`.
302///
303/// Otherwise, `this` must be a pointer to a `bindings::config_group` embedded
304/// in a `Group<Parent>`.
305unsafe fn get_group_data<'a, Parent>(this: *mut bindings::config_group) -> &'a Parent {
306    // SAFETY: `this` is a valid pointer.
307    let subsys = unsafe { (*this).cg_subsys };
308
309    // `link_group()` in `fs/configfs/dir.c` assigns `cg_subsys` for every
310    // `config_group` attached anywhere in a registered subsystem, including
311    // the subsystem's own `su_group` (which gets a pointer to itself). The
312    // only `config_group` with a NULL `cg_subsys` is the configfs root, and
313    // userspace cannot trigger callbacks on it. The group is therefore the
314    // subsystem's `su_group` iff it equals `&cg_subsys->su_group`.
315    let is_root = !subsys.is_null()
316        && core::ptr::eq(
317            this.cast_const(),
318            // SAFETY: For every `config_group` the configfs core dispatches a
319            // callback on, `cg_subsys` was set by `link_group()` at registration time
320            // and points to a valid `configfs_subsystem` that outlives the callback.
321            unsafe { &raw const (*subsys).su_group },
322        );
323
324    if is_root {
325        // SAFETY: By the above, `this` is the `su_group` field of a
326        // `configfs_subsystem` that, by function safety requirements, is
327        // embedded in a `Subsystem<Parent>`.
328        unsafe { &(*Subsystem::container_of(this)).data }
329    } else {
330        // SAFETY: By function safety requirements, `this` is a
331        // `config_group` embedded in a `Group<Parent>`.
332        unsafe { &(*Group::<Parent>::container_of(this)).data }
333    }
334}
335
336struct GroupOperationsVTable<Parent, Child>(PhantomData<(Parent, Child)>);
337
338impl<Parent, Child> GroupOperationsVTable<Parent, Child>
339where
340    Parent: GroupOperations<Child = Child>,
341    Child: 'static,
342{
343    /// # Safety
344    ///
345    /// `this` must be a valid pointer.
346    ///
347    /// If `this` does not represent the root group of a configfs subsystem,
348    /// `this` must be a pointer to a `bindings::config_group` embedded in a
349    /// `Group<Parent>`.
350    ///
351    /// Otherwise, `this` must be a pointer to a `bindings::config_group` that
352    /// is embedded in a `bindings::configfs_subsystem` that is embedded in a
353    /// `Subsystem<Parent>`.
354    ///
355    /// `name` must point to a null terminated string.
356    unsafe extern "C" fn make_group(
357        this: *mut bindings::config_group,
358        name: *const kernel::ffi::c_char,
359    ) -> *mut bindings::config_group {
360        // SAFETY: By function safety requirements of this function, this call
361        // is safe.
362        let parent_data = unsafe { get_group_data(this) };
363
364        let group_init = match Parent::make_group(
365            parent_data,
366            // SAFETY: By function safety requirements, name points to a null
367            // terminated string.
368            unsafe { CStr::from_char_ptr(name) },
369        ) {
370            Ok(init) => init,
371            Err(e) => return e.to_ptr(),
372        };
373
374        let child_group = <Arc<Group<Child>> as InPlaceInit<Group<Child>>>::try_pin_init(
375            group_init,
376            flags::GFP_KERNEL,
377        );
378
379        match child_group {
380            Ok(child_group) => {
381                let child_group_ptr = child_group.into_raw();
382                // SAFETY: We allocated the pointee of `child_ptr` above as a
383                // `Group<Child>`.
384                unsafe { Group::<Child>::group(child_group_ptr) }.cast_mut()
385            }
386            Err(e) => e.to_ptr(),
387        }
388    }
389
390    /// # Safety
391    ///
392    /// If `this` does not represent the root group of a configfs subsystem,
393    /// `this` must be a pointer to a `bindings::config_group` embedded in a
394    /// `Group<Parent>`.
395    ///
396    /// Otherwise, `this` must be a pointer to a `bindings::config_group` that
397    /// is embedded in a `bindings::configfs_subsystem` that is embedded in a
398    /// `Subsystem<Parent>`.
399    ///
400    /// `item` must point to a `bindings::config_item` within a
401    /// `bindings::config_group` within a `Group<Child>`.
402    unsafe extern "C" fn drop_item(
403        this: *mut bindings::config_group,
404        item: *mut bindings::config_item,
405    ) {
406        // SAFETY: By function safety requirements of this function, this call
407        // is safe.
408        let parent_data = unsafe { get_group_data(this) };
409
410        // SAFETY: By function safety requirements, `item` is embedded in a
411        // `config_group`.
412        let c_child_group_ptr = unsafe { container_of!(item, bindings::config_group, cg_item) };
413        // SAFETY: By function safety requirements, `c_child_group_ptr` is
414        // embedded within a `Group<Child>`.
415        let r_child_group_ptr = unsafe { Group::<Child>::container_of(c_child_group_ptr) };
416
417        if Parent::HAS_DROP_ITEM {
418            // SAFETY: We called `into_raw` to produce `r_child_group_ptr` in
419            // `make_group`.
420            let arc: Arc<Group<Child>> = unsafe { Arc::from_raw(r_child_group_ptr.cast_mut()) };
421
422            Parent::drop_item(parent_data, arc.as_arc_borrow());
423            arc.into_raw();
424        }
425
426        // SAFETY: By C API contract, we are required to drop a refcount on
427        // `item`.
428        unsafe { bindings::config_item_put(item) };
429    }
430
431    const VTABLE: bindings::configfs_group_operations = bindings::configfs_group_operations {
432        make_item: None,
433        make_group: Some(Self::make_group),
434        disconnect_notify: None,
435        drop_item: Some(Self::drop_item),
436        is_visible: None,
437        is_bin_visible: None,
438    };
439
440    const fn vtable_ptr() -> *const bindings::configfs_group_operations {
441        &Self::VTABLE
442    }
443}
444
445struct ItemOperationsVTable<Container, Data>(PhantomData<(Container, Data)>);
446
447impl<Data> ItemOperationsVTable<Group<Data>, Data>
448where
449    Data: 'static,
450{
451    /// # Safety
452    ///
453    /// `this` must be a pointer to a `bindings::config_group` embedded in a
454    /// `Group<Parent>`.
455    ///
456    /// This function will destroy the pointee of `this`. The pointee of `this`
457    /// must not be accessed after the function returns.
458    unsafe extern "C" fn release(this: *mut bindings::config_item) {
459        // SAFETY: By function safety requirements, `this` is embedded in a
460        // `config_group`.
461        let c_group_ptr = unsafe { kernel::container_of!(this, bindings::config_group, cg_item) };
462        // SAFETY: By function safety requirements, `c_group_ptr` is
463        // embedded within a `Group<Data>`.
464        let r_group_ptr = unsafe { Group::<Data>::container_of(c_group_ptr) };
465
466        // SAFETY: We called `into_raw` on `r_group_ptr` in
467        // `make_group`.
468        let pin_self: Arc<Group<Data>> = unsafe { Arc::from_raw(r_group_ptr.cast_mut()) };
469        drop(pin_self);
470    }
471
472    const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations {
473        release: Some(Self::release),
474        allow_link: None,
475        drop_link: None,
476    };
477
478    const fn vtable_ptr() -> *const bindings::configfs_item_operations {
479        &Self::VTABLE
480    }
481}
482
483impl<Data> ItemOperationsVTable<Subsystem<Data>, Data> {
484    const VTABLE: bindings::configfs_item_operations = bindings::configfs_item_operations {
485        release: None,
486        allow_link: None,
487        drop_link: None,
488    };
489
490    const fn vtable_ptr() -> *const bindings::configfs_item_operations {
491        &Self::VTABLE
492    }
493}
494
495/// Operations implemented by configfs groups that can create subgroups.
496///
497/// Implement this trait on structs that embed a [`Subsystem`] or a [`Group`].
498#[vtable]
499pub trait GroupOperations {
500    /// The child data object type.
501    ///
502    /// This group will create subgroups (subdirectories) backed by this kind of
503    /// object.
504    type Child: 'static;
505
506    /// Creates a new subgroup.
507    ///
508    /// The kernel will call this method in response to `mkdir(2)` in the
509    /// directory representing `this`.
510    ///
511    /// To accept the request to create a group, implementations should
512    /// return an initializer of a `Group<Self::Child>`. To prevent creation,
513    /// return a suitable error.
514    fn make_group(&self, name: &CStr) -> Result<impl PinInit<Group<Self::Child>, Error>>;
515
516    /// Prepares the group for removal from configfs.
517    ///
518    /// The kernel will call this method before the directory representing `_child` is removed from
519    /// configfs.
520    ///
521    /// Implementations can use this method to do house keeping before configfs drops its
522    /// reference to `Child`.
523    ///
524    /// NOTE: "drop" in the name of this function is not related to the Rust drop term. Rather, the
525    /// name is inherited from the callback name in the underlying C code.
526    fn drop_item(&self, _child: ArcBorrow<'_, Group<Self::Child>>) {
527        kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR)
528    }
529}
530
531/// A configfs attribute.
532///
533/// An attribute appears as a file in configfs, inside a folder that represent
534/// the group that the attribute belongs to.
535#[repr(transparent)]
536pub struct Attribute<const ID: u64, O, Data> {
537    attribute: Opaque<bindings::configfs_attribute>,
538    _p: PhantomData<(O, Data)>,
539}
540
541// SAFETY: We do not provide any operations on `Attribute`.
542unsafe impl<const ID: u64, O, Data> Sync for Attribute<ID, O, Data> {}
543
544// SAFETY: Ownership of `Attribute` can safely be transferred to other threads.
545unsafe impl<const ID: u64, O, Data> Send for Attribute<ID, O, Data> {}
546
547impl<const ID: u64, O, Data> Attribute<ID, O, Data>
548where
549    O: AttributeOperations<ID, Data = Data>,
550{
551    /// # Safety
552    ///
553    /// `item` must be embedded in a `bindings::config_group`.
554    ///
555    /// If `item` does not represent the root group of a configfs subsystem,
556    /// the group must be embedded in a `Group<Data>`.
557    ///
558    /// Otherwise, the group must be a embedded in a
559    /// `bindings::configfs_subsystem` that is embedded in a `Subsystem<Data>`.
560    ///
561    /// `page` must point to a writable buffer of size at least [`PAGE_SIZE`].
562    unsafe extern "C" fn show(
563        item: *mut bindings::config_item,
564        page: *mut kernel::ffi::c_char,
565    ) -> isize {
566        let c_group: *mut bindings::config_group =
567            // SAFETY: By function safety requirements, `item` is embedded in a
568            // `config_group`.
569            unsafe { container_of!(item, bindings::config_group, cg_item) };
570
571        // SAFETY: The function safety requirements for this function satisfy
572        // the conditions for this call.
573        let data: &Data = unsafe { get_group_data(c_group) };
574
575        // SAFETY: By function safety requirements, `page` is writable for `PAGE_SIZE`.
576        let ret = O::show(data, unsafe { &mut *(page.cast::<[u8; PAGE_SIZE]>()) });
577
578        match ret {
579            Ok(size) => size as isize,
580            Err(err) => err.to_errno() as isize,
581        }
582    }
583
584    /// # Safety
585    ///
586    /// `item` must be embedded in a `bindings::config_group`.
587    ///
588    /// If `item` does not represent the root group of a configfs subsystem,
589    /// the group must be embedded in a `Group<Data>`.
590    ///
591    /// Otherwise, the group must be a embedded in a
592    /// `bindings::configfs_subsystem` that is embedded in a `Subsystem<Data>`.
593    ///
594    /// `page` must point to a readable buffer of size at least `size`.
595    unsafe extern "C" fn store(
596        item: *mut bindings::config_item,
597        page: *const kernel::ffi::c_char,
598        size: usize,
599    ) -> isize {
600        let c_group: *mut bindings::config_group =
601        // SAFETY: By function safety requirements, `item` is embedded in a
602        // `config_group`.
603            unsafe { container_of!(item, bindings::config_group, cg_item) };
604
605        // SAFETY: The function safety requirements for this function satisfy
606        // the conditions for this call.
607        let data: &Data = unsafe { get_group_data(c_group) };
608
609        let ret = O::store(
610            data,
611            // SAFETY: By function safety requirements, `page` is readable
612            // for at least `size`.
613            unsafe { core::slice::from_raw_parts(page.cast(), size) },
614        );
615
616        match ret {
617            Ok(()) => size as isize,
618            Err(err) => err.to_errno() as isize,
619        }
620    }
621
622    /// Create a new attribute.
623    ///
624    /// The attribute will appear as a file with name given by `name`.
625    pub const fn new(name: &'static CStr) -> Self {
626        Self {
627            attribute: Opaque::new(bindings::configfs_attribute {
628                ca_name: crate::str::as_char_ptr_in_const_context(name),
629                ca_owner: core::ptr::null_mut(),
630                ca_mode: 0o660,
631                show: Some(Self::show),
632                store: if O::HAS_STORE {
633                    Some(Self::store)
634                } else {
635                    None
636                },
637            }),
638            _p: PhantomData,
639        }
640    }
641}
642
643/// Operations supported by an attribute.
644///
645/// Implement this trait on type and pass that type as generic parameter when
646/// creating an [`Attribute`]. The type carrying the implementation serve no
647/// purpose other than specifying the attribute operations.
648///
649/// This trait must be implemented on the `Data` type of for types that
650/// implement `HasGroup<Data>`. The trait must be implemented once for each
651/// attribute of the group. The constant type parameter `ID` maps the
652/// implementation to a specific `Attribute`. `ID` must be passed when declaring
653/// attributes via the [`kernel::configfs_attrs`] macro, to tie
654/// `AttributeOperations` implementations to concrete named attributes.
655#[vtable]
656pub trait AttributeOperations<const ID: u64 = 0> {
657    /// The type of the object that contains the field that is backing the
658    /// attribute for this operation.
659    type Data;
660
661    /// Renders the value of an attribute.
662    ///
663    /// This function is called by the kernel to read the value of an attribute.
664    ///
665    /// Implementations should write the rendering of the attribute to `page`
666    /// and return the number of bytes written.
667    fn show(data: &Self::Data, page: &mut [u8; PAGE_SIZE]) -> Result<usize>;
668
669    /// Stores the value of an attribute.
670    ///
671    /// This function is called by the kernel to update the value of an attribute.
672    ///
673    /// Implementations should parse the value from `page` and update internal
674    /// state to reflect the parsed value.
675    fn store(_data: &Self::Data, _page: &[u8]) -> Result {
676        kernel::build_error!(kernel::error::VTABLE_DEFAULT_ERROR)
677    }
678}
679
680/// A list of attributes.
681///
682/// This type is used to construct a new [`ItemType`]. It represents a list of
683/// [`Attribute`] that will appear in the directory representing a [`Group`].
684/// Users should not directly instantiate this type, rather they should use the
685/// [`kernel::configfs_attrs`] macro to declare a static set of attributes for a
686/// group.
687///
688/// # Note
689///
690/// Instances of this type are constructed statically at compile by the
691/// [`kernel::configfs_attrs`] macro.
692#[repr(transparent)]
693pub struct AttributeList<const N: usize, Data>(
694    /// Null terminated Array of pointers to [`Attribute`]. The type is [`c_void`]
695    /// to conform to the C API.
696    UnsafeCell<[*mut kernel::ffi::c_void; N]>,
697    PhantomData<Data>,
698);
699
700// SAFETY: Ownership of `AttributeList` can safely be transferred to other threads.
701unsafe impl<const N: usize, Data> Send for AttributeList<N, Data> {}
702
703// SAFETY: We do not provide any operations on `AttributeList` that need synchronization.
704unsafe impl<const N: usize, Data> Sync for AttributeList<N, Data> {}
705
706impl<const N: usize, Data> AttributeList<N, Data> {
707    /// # Safety
708    ///
709    /// This function must only be called by the [`kernel::configfs_attrs`]
710    /// macro.
711    #[doc(hidden)]
712    pub const unsafe fn new() -> Self {
713        Self(UnsafeCell::new([core::ptr::null_mut(); N]), PhantomData)
714    }
715
716    /// # Safety
717    ///
718    /// The caller must ensure that there are no other concurrent accesses to
719    /// `self`. That is, the caller has exclusive access to `self.`
720    #[doc(hidden)]
721    pub const unsafe fn add<const I: usize, const ID: u64, O>(
722        &'static self,
723        attribute: &'static Attribute<ID, O, Data>,
724    ) where
725        O: AttributeOperations<ID, Data = Data>,
726    {
727        // We need a space at the end of our list for a null terminator.
728        const { assert!(I < N - 1, "Invalid attribute index") };
729
730        // SAFETY: By function safety requirements, we have exclusive access to
731        // `self` and the reference created below will be exclusive.
732        unsafe { (&mut *self.0.get())[I] = core::ptr::from_ref(attribute).cast_mut().cast() };
733    }
734}
735
736/// A representation of the attributes that will appear in a [`Group`] or
737/// [`Subsystem`].
738///
739/// Users should not directly instantiate objects of this type. Rather, they
740/// should use the [`kernel::configfs_attrs`] macro to statically declare the
741/// shape of a [`Group`] or [`Subsystem`].
742#[pin_data]
743pub struct ItemType<Container, Data> {
744    #[pin]
745    item_type: Opaque<bindings::config_item_type>,
746    _p: PhantomData<(Container, Data)>,
747}
748
749// SAFETY: We do not provide any operations on `ItemType` that need synchronization.
750unsafe impl<Container, Data> Sync for ItemType<Container, Data> {}
751
752// SAFETY: Ownership of `ItemType` can safely be transferred to other threads.
753unsafe impl<Container, Data> Send for ItemType<Container, Data> {}
754
755macro_rules! impl_item_type {
756    ($tpe:ty) => {
757        impl<Data> ItemType<$tpe, Data> {
758            #[doc(hidden)]
759            pub const fn new_with_child_ctor<const N: usize, Child>(
760                owner: &'static ThisModule,
761                attributes: &'static AttributeList<N, Data>,
762            ) -> Self
763            where
764                Data: GroupOperations<Child = Child>,
765                Child: 'static,
766            {
767                Self {
768                    item_type: Opaque::new(bindings::config_item_type {
769                        ct_owner: owner.as_ptr(),
770                        ct_group_ops: GroupOperationsVTable::<Data, Child>::vtable_ptr().cast_mut(),
771                        ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
772                        ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(),
773                        ct_bin_attrs: core::ptr::null_mut(),
774                    }),
775                    _p: PhantomData,
776                }
777            }
778
779            #[doc(hidden)]
780            pub const fn new<const N: usize>(
781                owner: &'static ThisModule,
782                attributes: &'static AttributeList<N, Data>,
783            ) -> Self {
784                Self {
785                    item_type: Opaque::new(bindings::config_item_type {
786                        ct_owner: owner.as_ptr(),
787                        ct_group_ops: core::ptr::null_mut(),
788                        ct_item_ops: ItemOperationsVTable::<$tpe, Data>::vtable_ptr().cast_mut(),
789                        ct_attrs: core::ptr::from_ref(attributes).cast_mut().cast(),
790                        ct_bin_attrs: core::ptr::null_mut(),
791                    }),
792                    _p: PhantomData,
793                }
794            }
795        }
796    };
797}
798
799impl_item_type!(Subsystem<Data>);
800impl_item_type!(Group<Data>);
801
802impl<Container, Data> ItemType<Container, Data> {
803    fn as_ptr(&self) -> *const bindings::config_item_type {
804        self.item_type.get()
805    }
806}
807
808/// Define a list of configfs attributes statically.
809///
810/// Invoking the macro in the following manner:
811///
812/// ```ignore
813/// let item_type = configfs_attrs! {
814///     container: configfs::Subsystem<Configuration>,
815///     data: Configuration,
816///     child: Child,
817///     attributes: [
818///         message: 0,
819///         bar: 1,
820///     ],
821/// };
822/// ```
823///
824/// Expands the following output:
825///
826/// ```ignore
827/// let item_type = {
828///     static CONFIGURATION_MESSAGE_ATTR: kernel::configfs::Attribute<
829///         0,
830///         Configuration,
831///         Configuration,
832///     > = unsafe {
833///         kernel::configfs::Attribute::new({
834///             const S: &str = "message\u{0}";
835///             const C: &kernel::str::CStr = match kernel::str::CStr::from_bytes_with_nul(
836///                 S.as_bytes()
837///             ) {
838///                 Ok(v) => v,
839///                 Err(_) => {
840///                     core::panicking::panic_fmt(core::const_format_args!(
841///                         "string contains interior NUL"
842///                     ));
843///                 }
844///             };
845///             C
846///         })
847///     };
848///
849///     static CONFIGURATION_BAR_ATTR: kernel::configfs::Attribute<
850///             1,
851///             Configuration,
852///             Configuration
853///     > = unsafe {
854///         kernel::configfs::Attribute::new({
855///             const S: &str = "bar\u{0}";
856///             const C: &kernel::str::CStr = match kernel::str::CStr::from_bytes_with_nul(
857///                 S.as_bytes()
858///             ) {
859///                 Ok(v) => v,
860///                 Err(_) => {
861///                     core::panicking::panic_fmt(core::const_format_args!(
862///                         "string contains interior NUL"
863///                     ));
864///                 }
865///             };
866///             C
867///         })
868///     };
869///
870///     const N: usize = (1usize + (1usize + 0usize)) + 1usize;
871///
872///     static CONFIGURATION_ATTRS: kernel::configfs::AttributeList<N, Configuration> =
873///         unsafe { kernel::configfs::AttributeList::new() };
874///
875///     {
876///         const N: usize = 0usize;
877///         unsafe { CONFIGURATION_ATTRS.add::<N, 0, _>(&CONFIGURATION_MESSAGE_ATTR) };
878///     }
879///
880///     {
881///         const N: usize = (1usize + 0usize);
882///         unsafe { CONFIGURATION_ATTRS.add::<N, 1, _>(&CONFIGURATION_BAR_ATTR) };
883///     }
884///
885///     static CONFIGURATION_TPE:
886///       kernel::configfs::ItemType<configfs::Subsystem<Configuration> ,Configuration>
887///         = kernel::configfs::ItemType::<
888///                 configfs::Subsystem<Configuration>,
889///                 Configuration
890///                 >::new_with_child_ctor::<N,Child>(
891///             ::kernel::module::this_module::<crate::LocalModule>(),
892///             &CONFIGURATION_ATTRS
893///         );
894///
895///     &CONFIGURATION_TPE
896/// }
897/// ```
898#[allow(clippy::crate_in_macro_def)]
899#[macro_export]
900macro_rules! configfs_attrs {
901    (
902        container: $container:ty,
903        data: $data:ty,
904        attributes: [
905            $($name:ident: $attr:literal),* $(,)?
906        ] $(,)?
907    ) => {
908        $crate::configfs_attrs!(
909            count:
910            @container($container),
911            @data($data),
912            @child(),
913            @no_child(x),
914            @attrs($($name $attr)*),
915            @eat($($name $attr,)*),
916            @assign(),
917            @cnt(0usize),
918        )
919    };
920    (
921        container: $container:ty,
922        data: $data:ty,
923        child: $child:ty,
924        attributes: [
925            $($name:ident: $attr:literal),* $(,)?
926        ] $(,)?
927    ) => {
928        $crate::configfs_attrs!(
929            count:
930            @container($container),
931            @data($data),
932            @child($child),
933            @no_child(),
934            @attrs($($name $attr)*),
935            @eat($($name $attr,)*),
936            @assign(),
937            @cnt(0usize),
938        )
939    };
940    (count:
941     @container($container:ty),
942     @data($data:ty),
943     @child($($child:ty)?),
944     @no_child($($no_child:ident)?),
945     @attrs($($aname:ident $aattr:literal)*),
946     @eat($name:ident $attr:literal, $($rname:ident $rattr:literal,)*),
947     @assign($($assign:block)*),
948     @cnt($cnt:expr),
949    ) => {
950        $crate::configfs_attrs!(
951            count:
952            @container($container),
953            @data($data),
954            @child($($child)?),
955            @no_child($($no_child)?),
956            @attrs($($aname $aattr)*),
957            @eat($($rname $rattr,)*),
958            @assign($($assign)* {
959                const N: usize = $cnt;
960                // The following macro text expands to a call to `Attribute::add`.
961
962                // SAFETY: By design of this macro, the name of the variable we
963                // invoke the `add` method on below, is not visible outside of
964                // the macro expansion. The macro does not operate concurrently
965                // on this variable, and thus we have exclusive access to the
966                // variable.
967                unsafe {
968                    $crate::macros::paste!(
969                        [< $data:upper _ATTRS >]
970                            .add::<N, $attr, _>(&[< $data:upper _ $name:upper _ATTR >])
971                    )
972                };
973            }),
974            @cnt(1usize + $cnt),
975        )
976    };
977    (count:
978     @container($container:ty),
979     @data($data:ty),
980     @child($($child:ty)?),
981     @no_child($($no_child:ident)?),
982     @attrs($($aname:ident $aattr:literal)*),
983     @eat(),
984     @assign($($assign:block)*),
985     @cnt($cnt:expr),
986    ) =>
987    {
988        $crate::configfs_attrs!(
989            final:
990            @container($container),
991            @data($data),
992            @child($($child)?),
993            @no_child($($no_child)?),
994            @attrs($($aname $aattr)*),
995            @assign($($assign)*),
996            @cnt($cnt),
997        )
998    };
999    (final:
1000     @container($container:ty),
1001     @data($data:ty),
1002     @child($($child:ty)?),
1003     @no_child($($no_child:ident)?),
1004     @attrs($($name:ident $attr:literal)*),
1005     @assign($($assign:block)*),
1006     @cnt($cnt:expr),
1007    ) =>
1008    {
1009        $crate::macros::paste!{
1010            {
1011                $(
1012                    // SAFETY: We are expanding `configfs_attrs`.
1013                    static [< $data:upper _ $name:upper _ATTR >]:
1014                        $crate::configfs::Attribute<$attr, $data, $data> =
1015                            unsafe {
1016                                $crate::configfs::Attribute::new(
1017                                    $crate::c_str!(::core::stringify!($name)),
1018                                )
1019                            };
1020                )*
1021
1022
1023                // We need space for a null terminator.
1024                const N: usize = $cnt + 1usize;
1025
1026                // SAFETY: We are expanding `configfs_attrs`.
1027                static [< $data:upper _ATTRS >]:
1028                $crate::configfs::AttributeList<N, $data> =
1029                    unsafe { $crate::configfs::AttributeList::new() };
1030
1031                $($assign)*
1032
1033                $(
1034                    const [<$no_child:upper>]: bool = true;
1035
1036                    static [< $data:upper _TPE >] : $crate::configfs::ItemType<$container, $data>  =
1037                        $crate::configfs::ItemType::<$container, $data>::new::<N>(
1038                            $crate::module::this_module::<crate::LocalModule>(),
1039                            &[<$ data:upper _ATTRS >]
1040                        );
1041                )?
1042
1043                $(
1044                    static [< $data:upper _TPE >]:
1045                        $crate::configfs::ItemType<$container, $data>  =
1046                            $crate::configfs::ItemType::<$container, $data>::
1047                            new_with_child_ctor::<N, $child>(
1048                                $crate::module::this_module::<crate::LocalModule>(),
1049                                &[<$ data:upper _ATTRS >]
1050                            );
1051                )?
1052
1053                & [< $data:upper _TPE >]
1054            }
1055        }
1056    };
1057
1058}
1059
1060pub use crate::configfs_attrs;