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