summaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
authorBreno Leitao <leitao@debian.org>2026-10-16 09:17:46 -0700
committerBreno Leitao <leitao@debian.org>2026-10-16 09:17:46 -0700
commit6c1fa55e287a64ebbf6877f8dd288c6d2ecf4ede (patch)
treef6649a31675c9fb9e4cc4460008f971748ff3edf /rust/kernel
parent154f74a964af59d677195618834313fb9584cf89 (diff)
parente84c9b64df615e658e47561b835c1364dc48f0c0 (diff)
downloadlinux-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.rs110
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(),