diff options
| author | Breno Leitao <leitao@debian.org> | 2026-10-16 09:17:46 -0700 |
|---|---|---|
| committer | Breno Leitao <leitao@debian.org> | 2026-10-16 09:17:46 -0700 |
| commit | 6c1fa55e287a64ebbf6877f8dd288c6d2ecf4ede (patch) | |
| tree | f6649a31675c9fb9e4cc4460008f971748ff3edf /rust/kernel | |
| parent | 154f74a964af59d677195618834313fb9584cf89 (diff) | |
| parent | e84c9b64df615e658e47561b835c1364dc48f0c0 (diff) | |
| download | linux-6c1fa55e287a64ebbf6877f8dd288c6d2ecf4ede.tar.gz linux-6c1fa55e287a64ebbf6877f8dd288c6d2ecf4ede.zip | |
Merge tag 'rust-configfs-for-v7.4-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/a.hindborg/linux into updates-v7.4
Rust configfs changes for v7.4-rc1
Three changes for the Rust configfs bindings. One typo fix for a safety comment and two bug fixes.
* tag 'rust-configfs-for-v7.4-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/a.hindborg/linux:
rust: configfs: fix data offset calculation for subsystem callbacks
rust: configfs: require thread-safe callback data
rust: configfs: fix release safety documentation
Diffstat (limited to 'rust/kernel')
| -rw-r--r-- | rust/kernel/configfs.rs | 110 |
1 files changed, 74 insertions, 36 deletions
diff --git a/rust/kernel/configfs.rs b/rust/kernel/configfs.rs index 0be10cf61927..dbed72844b31 100644 --- a/rust/kernel/configfs.rs +++ b/rust/kernel/configfs.rs @@ -135,8 +135,9 @@ pub struct Subsystem<Data> { // SAFETY: We do not provide any operations on `Subsystem`. unsafe impl<Data> Sync for Subsystem<Data> {} -// SAFETY: Ownership of `Subsystem` can safely be transferred to other threads. -unsafe impl<Data> Send for Subsystem<Data> {} +// SAFETY: Ownership of `Subsystem` can safely be transferred to other threads +// if its data can be transferred as well. +unsafe impl<Data: Send> Send for Subsystem<Data> {} impl<Data> Subsystem<Data> { /// Create an initializer for a [`Subsystem`]. @@ -249,6 +250,13 @@ pub struct Group<Data> { data: Data, } +// SAFETY: We do not provide any operations on `Group`. +unsafe impl<Data> Sync for Group<Data> {} + +// SAFETY: Ownership of `Group` can safely be transferred to other threads if +// its data can be transferred as well. +unsafe impl<Data: Send> Send for Group<Data> {} + impl<Data> Group<Data> { /// Create an initializer for a new group. /// @@ -297,26 +305,39 @@ unsafe impl<Data> HasGroup<Data> for Group<Data> { /// /// `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>`. +/// If `this` is the `su_group` field of a `bindings::configfs_subsystem`, that +/// `configfs_subsystem` must be embedded in a `Subsystem<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>`. +/// Otherwise, `this` must be a pointer to a `bindings::config_group` embedded +/// in a `Group<Parent>`. unsafe fn get_group_data<'a, Parent>(this: *mut bindings::config_group) -> &'a Parent { // SAFETY: `this` is a valid pointer. - let is_root = unsafe { (*this).cg_subsys.is_null() }; + let subsys = unsafe { (*this).cg_subsys }; + + // `link_group()` in `fs/configfs/dir.c` assigns `cg_subsys` for every + // `config_group` attached anywhere in a registered subsystem, including + // the subsystem's own `su_group` (which gets a pointer to itself). The + // only `config_group` with a NULL `cg_subsys` is the configfs root, and + // userspace cannot trigger callbacks on it. The group is therefore the + // subsystem's `su_group` iff it equals `&cg_subsys->su_group`. + let is_root = !subsys.is_null() + && core::ptr::eq( + this.cast_const(), + // SAFETY: For every `config_group` the configfs core dispatches a + // callback on, `cg_subsys` was set by `link_group()` at registration time + // and points to a valid `configfs_subsystem` that outlives the callback. + unsafe { &raw const (*subsys).su_group }, + ); - 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>`. + if is_root { + // SAFETY: By the above, `this` is the `su_group` field of a + // `configfs_subsystem` that, by function safety requirements, is + // embedded in a `Subsystem<Parent>`. unsafe { &(*Subsystem::container_of(this)).data } + } else { + // SAFETY: By function safety requirements, `this` is a + // `config_group` embedded in a `Group<Parent>`. + unsafe { &(*Group::<Parent>::container_of(this)).data } } } @@ -325,7 +346,9 @@ struct GroupOperationsVTable<Parent, Child>(PhantomData<(Parent, Child)>); impl<Parent, Child> GroupOperationsVTable<Parent, Child> where Parent: GroupOperations<Child = Child>, - Child: 'static, + // We transfer `Arc<Group<Data>>` across a thread boundary in `make_group` + // and `drop_item`. + Arc<Group<Child>>: Send, { /// # Safety /// @@ -344,8 +367,9 @@ where 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. + // SAFETY: By function safety requirements, `this` points to a configfs + // group containing `Parent`. The `GroupOperations` bound guarantees + // that `Parent: Sync`, so it is safe to share it with this thread. let parent_data = unsafe { get_group_data(this) }; let group_init = match Parent::make_group( @@ -390,8 +414,9 @@ where this: *mut bindings::config_group, item: *mut bindings::config_item, ) { - // SAFETY: By function safety requirements of this function, this call - // is safe. + // SAFETY: By function safety requirements, `this` points to a configfs + // group containing `Parent`. The `GroupOperations` bound guarantees + // that `Parent: Sync`, so it is safe to share it with this thread. let parent_data = unsafe { get_group_data(this) }; // SAFETY: By function safety requirements, `item` is embedded in a @@ -403,7 +428,9 @@ where if Parent::HAS_DROP_ITEM { // SAFETY: We called `into_raw` to produce `r_child_group_ptr` in - // `make_group`. + // `make_group`. This function may be executing on a different + // thread than `into_raw`. As `Arc<Group<Child>>: Send` this + // ownership transfer is safe. let arc: Arc<Group<Child>> = unsafe { Arc::from_raw(r_child_group_ptr.cast_mut()) }; Parent::drop_item(parent_data, arc.as_arc_borrow()); @@ -434,11 +461,13 @@ struct ItemOperationsVTable<Container, Data>(PhantomData<(Container, Data)>); impl<Data> ItemOperationsVTable<Group<Data>, Data> where Data: 'static, + // We transfer `Arc<Group<Data>>` across a thread boundary in `release`. + Arc<Group<Data>>: Send, { /// # Safety /// - /// `this` must be a pointer to a `bindings::config_group` embedded in a - /// `Group<Parent>`. + /// `this` must be a pointer to the `cg_item` field of a `bindings::config_group` embedded in a + /// valid `Group<Data>`. /// /// This function will destroy the pointee of `this`. The pointee of `this` /// must not be accessed after the function returns. @@ -450,8 +479,9 @@ where // 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`. + // SAFETY: We called `into_raw` on `r_group_ptr` in `make_group`. This + // function may be running on a different thread than the thread that + // called `into_raw`. As `Arc<Group<Data>>: Send`, this is safe. let pin_self: Arc<Group<Data>> = unsafe { Arc::from_raw(r_group_ptr.cast_mut()) }; drop(pin_self); } @@ -483,12 +513,12 @@ impl<Data> ItemOperationsVTable<Subsystem<Data>, Data> { /// /// Implement this trait on structs that embed a [`Subsystem`] or a [`Group`]. #[vtable] -pub trait GroupOperations { +pub trait GroupOperations: Sync { /// The child data object type. /// /// This group will create subgroups (subdirectories) backed by this kind of /// object. - type Child: 'static; + type Child: 'static + Send; /// Creates a new subgroup. /// @@ -555,8 +585,10 @@ where // `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. + // SAFETY: By function safety requirements, `c_group` points to a + // configfs group containing `Data`. The `AttributeOperations` bound + // guarantees that `Data: Sync`, so it is safe to share it with this + // thread. let data: &Data = unsafe { get_group_data(c_group) }; // SAFETY: By function safety requirements, `page` is writable for `PAGE_SIZE`. @@ -589,8 +621,10 @@ where // `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. + // SAFETY: By function safety requirements, `c_group` points to a + // configfs group containing `Data`. The `AttributeOperations` bound + // guarantees that `Data: Sync`, so it is safe to share it with this + // thread. let data: &Data = unsafe { get_group_data(c_group) }; let ret = O::store( @@ -643,7 +677,7 @@ where pub trait AttributeOperations<const ID: u64 = 0> { /// The type of the object that contains the field that is backing the /// attribute for this operation. - type Data; + type Data: Sync; /// Renders the value of an attribute. /// @@ -749,7 +783,8 @@ macro_rules! impl_item_type { ) -> Self where Data: GroupOperations<Child = Child>, - Child: 'static, + Arc<Group<Child>>: Send, + Arc<Group<Data>>: Send, { Self { item_type: Opaque::new(bindings::config_item_type { @@ -769,7 +804,10 @@ macro_rules! impl_item_type { pub const fn new<const N: usize>( owner: &'static ThisModule, attributes: &'static AttributeList<N, Data>, - ) -> Self { + ) -> Self + where + Arc<Group<Data>>: Send, + { Self { item_type: Opaque::new(bindings::config_item_type { ct_owner: owner.as_ptr(), |
